unobtrusive validation not working with dynamic content - asp.net-mvc-4

I'm having problems trying to get the unobtrusive jquery validation to work with a partial view that is loaded dynamically through an AJAX call.
I've been spending days trying to get this code to work with no luck.
Here's the View:
#model MvcApplication2.Models.test
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<div id="res"></div>
<input id="submit" type="submit" value="submit" />
}
The Partial View:
#model MvcApplication2.Models.test
#Html.TextAreaFor(m => m.MyProperty);
#Html.ValidationMessageFor(m => m.MyProperty);
<script type="text/javascript" >
$.validator.unobtrusive.parse(document);
</script>
The Model:
public class test
{
[Required(ErrorMessage= "required field")]
public int MyProperty { get; set; }
}
The Controller:
public ActionResult GetView()
{
return PartialView("Test");
}
and finally, the javascript:
$(doument).ready(function () {
$.ajax({
url: '/test/getview',
success: function (res) {
$("#res").html(res);
$.validator.unobtrusive.parse($("#res"));
}
});
$("#submit").click(function () {
if ($("form").valid()) {
alert('valid');
return true;
} else {
alert('not valid');
return false;
}
});
The validation does not work. Even if I don't fill any information in the texbox, the submit event shows the alert ('valid').
However, if instead of loading dynamically the view, I use #Html.Partial("test", Model) to render the partial View in the main View (and I don't do the AJAX call), then the validation works just fine.
This is probably because if I load the content dynamically, the controls don't exist in the DOM yet. But I do a call to $.validator.unobtrusive.parse($("#res")); which should be enough to let the validator about the newly loaded controls...
Can anyone help ?

If you try to parse a form that is already parsed it won't update
What you could do when you add dynamic element to the form is either
You could remove the form's validation and re validate it like this:
var form = $(formSelector)
.removeData("validator") /* added by the raw jquery.validate plugin */
.removeData("unobtrusiveValidation"); /* added by the jquery unobtrusive plugin*/
$.validator.unobtrusive.parse(form);
Access the form's unobtrusiveValidation data using the jquery data method:
$(form).data('unobtrusiveValidation')
then access the rules collection and add the new elements attributes (which is somewhat complicated).
You can also check out this article on Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC for a plugin used for adding dynamic elements to a form. This plugin uses the 2nd solution.

As an addition to Nadeem Khedr's answer....
If you've loaded a form in to your DOM dynamically and then call
jQuery.validator.unobtrusive.parse(form);
(with the extra bits mentioned) and are then going to submit that form using ajax remember to call
$(form).valid()
which returns true or false (and runs the actual validation) before you submit your form.

Surprisingly, when I viewed this question, the official ASP.NET docs still did not have any info about the unobtrusive parse() method or how to use it with dynamic content. I took the liberty of creating an issue at the docs repo (referencing #Nadeem's original answer) and submitting a pull request to fix it. This information is now visible in the client side validation section of the model validation topic.

add this to your _Layout.cshtml
$(function () {
//parsing the unobtrusive attributes when we get content via ajax
$(document).ajaxComplete(function () {
$.validator.unobtrusive.parse(document);
});
});

test this:
if ($.validator.unobtrusive != undefined) {
$.validator.unobtrusive.parse("form");
}

I got struck in the same problem and nothing worked except this:
$(document).ready(function () {
rebindvalidators();
});
function rebindvalidators() {
var $form = $("#id-of-form");
$form.unbind();
$form.data("validator", null);
$.validator.unobtrusive.parse($form);
$form.validate($form.data("unobtrusiveValidation").options);
}
and add
// Check if the form is valid
var $form = $(this.form);
if (!$form.valid())
return;
where you are trying to save the form.
I was saving the form through Ajax call.
Hope this will help someone.

just copy this code again in end of modal code
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
;)

Related

In Razor Pages, how can I preserve the state of a checkbox which is not part of a form?

I have page that has checkbox that is used to expand/collapse some part of the page. This is client-side logic done in JavaScript.
I want to preserve the state of this checkbox for this particular page. Can Razor Pages do this automatically?
I tried by adding bool property with [BindProperty(SupportsGet = true)] in PageModel but it doesn't work - when I check the checkbox and reload (HTTP GET) the checkbox is always false.
Guessing that this toggle feature is user-specific, and that you want to persist their choice over a number of HTTP requests, I recommend setting a cookie using client-side code, which is user- or more accurately device-specific and can persist for as long as you need, and can be read on the server too.
https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
https://www.learnrazorpages.com/razor-pages/cookies
I want to preserve the state of this checkbox for this particular page. Can Razor Pages do this automatically?
No, since you don't send it to the backend it will not show it.
As Mike said, it better we could store it inside the client cookie or storage.
More details, you could refer to below codes:
<p>
<input type="checkbox" id="cbox1" checked="checked">
<label >This is the first checkbox</label>
</p>
#section scripts{
<script>
$(function(){
var status = getValue();
if(status === "true"){
$("#cbox1").attr("checked","checked");
}else{
$("#cbox1").removeAttr("checked");
}
})
$("#cbox1").click(function(){
var re = $("#cbox1").is(":checked")
alert(re);
createItem(re);
});
function createItem(value) {
localStorage.setItem('status', value);
}
function getValue() {
return localStorage.getItem('status');
} // Gets the value of 'nameOfItem' and returns it
console.log(getValue()); //'value';
</script>
}

MVC PartialView not rendered when passing strongly typed model

I'm calling a controller method using AJAX request.
This function used to return a partial view so I will load it in an HTML element.
the function:
public PartialViewResult LoadLockTimerEnd()
{
Session["Info"] = new Request();
RequestReply reqRep = new RequestReply("/Home/Index", "ID missing. Reseting");
return PartialView("FailurePartialView", reqRep);
}
When passing a simple string as model to this PartialView it works fine, but when passing a RequestReply object as model it is not working and the partialView is not loaded at all.
The PatialView:
#model EPS_WEB_SITE.Models.RequestReply;
#{
Layout = "~/Views/Shared/_FailureLayout.cshtml";
}
<strong>#Html.Raw(#Model.Message.ToString())</strong>
<div class="buttons-container button-container-small">
<div data-request-url="#Model.RedirectURL.ToString()">
<button type="button" id="dismiss-failure-btn" class="btn btn-danger dismiss">Dismiss</button>
</div>
</div>
The AJAX call:
$.get('/Home/LoadLockTimerEnd', function (data) {
$("#resultDiv").html(data);
});
Why does the PartialView works with string as model and not class as model?
$.ajax({
dataType: "HTML",
url: '/Home/LoadLockTimerEnd',
success: function (data) {
$("#resultDiv").html(data);
}
});
Try to call your Action using this way
OK so I found the problem:
It was a compilation error.
I needed to remove ; in the model binding in the view
#model EPS_WEB_SITE.Models.RequestReply;
I was able to find that in the network tab on Chrome browser.
Double click on the problematic request and it shown the server error.
Hope it will help someone

Change WebGrid row color conditionally without Jquery/javascript ASP.NET MVC 4

I know this has been asked before but I wanted to ask it in my own way with more clarification. I am trying to conditionally set the background of a td that is created using a webGrid in ASP.NET MVC. I don't see a good way to do this.
So far what I have come up with is this:
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<div style=\"background-color: red\">Item Backordered</div>"
:
""
)),
This is an okay solution but I would like a more clean look because the webgrid default has a small padding in the table cell so the div won't expand completely to the size of the cell either.
Is there a way to edit the td in any way? I know I can change the background and other style attributes using jquery or javascript but I don't like the idea of having doing duplicate work to first build the table on the server, then on the client side iterate over it again conditionally changing the colors when this should have been completed with the first iteration.
Hope the following answer will help you
grid.GetHtml(columns: grid.Columns(grid.Column(columnName: "DATT", header: "Date",format: #<text> #{
if (#item.isCurrentlyBackordered)
{
<span>Item Backordered</span>
<script>
$("tr:contains('Item Backordered')").css("background-color", "yellow");
</script>
}
}</text>)))
Also you can write this in a common JQuery too
grid.Column("DATT", header: "Date", format: (item) => new MvcHtmlString
(
(item.isCurrentlyBackordered)
?
"<span>Item Backordered</span>"
:
""
)),
JQuery
<script type="text/javascript">
$(function () {
$("tr:contains('Item Backordered')").css("background-color", "yellow");
})
</script>
With the help of Golda's response and here
I was able to create an elegant solution. This solution uses JavaScript/JQuery, as it doesn't seem possible to do it without it but using (to me) a cleaner solution than what I had came across. What I did in the model class (type for List<>()) was add a property that refers to itself and returns an instance cast to its interface like so:
public iTrans This
{
get
{
return this;
}
}
I did this because the webGrid seems to only allow access to the properties and not methods; regardless of access level.
Then in that same model I have a method which will conditionally attach markup for a hidden input field to the data string and return it as an MvcHtmlString object:
public MvcHtmlString htmlColorWrapper(string cellStr, string hexColor = "#ccc")
{
if (isOnBackorder)
{
cellStr = cellStr + "<input type='hidden' class='color' value='" + hexColor + "'/>";
}
return new MvcHtmlString(cellStr);
}
And in the markup (partial view) I make my grid.Column call:
grid.Column("Date", header: "Date", format: (item) => item.This.htmlColorWrapper(item.Date.ToString("MM/dd/yyy"))),
Then I create the JavaScript function(s):
window.onload = function () {
SetFeaturedRow();
};
function SetFeaturedRow() {
$('.color').each(function (index, element) {
$(element).parent().parent().css('background-color', $(element).val());
});
}
The window.onload is needed to point to the SetFeaturedRow() function to set the row colors at page load, the function name, "SetFeaturedRow" is stored in the ajaxUpdateCallback property through the webgrid constructor arguments: new WebGrid(Model ..... ajaxUpdateCallback: "SetFeaturedRow"); Or it can be set through the WebGrid reference, ref.ajaxUpdateCallback = "SetFeatureRow"
This will be used during any ajax call the WebGrid class will make. So for example if there are multiple pages to the webgrid each selection is an ajax call and the row colors will need to be re-updated.
Hopefully this helps someone.

Knockout bindings getting lost in Durandal app

I have a SPA in which I am using Durandal / KnockoutJS with knockout.validation.js. Inside the {view}.js I have this set
ko.validation.configure ({
decorateElement: true,
registerExtenders: true,
messagesOnModified: true,
insertMessages: false,
messageTemplate: null,
parseInputAttributes: true,
});
and inside one of the views I have
<input class="input-member"
type="text"
data-bind="value: memberno, validationOptions: { errorElementClass: 'input-validation-error' }"/>
When the view is first activated the element correctly has the style input-validation-error applied.
On subsequent loading of the view the css is not applied as I require. I can see in firebug that the input field now has this class applied input-member validationElement
I don't know where validationElement came from - but something got messed up with the knockout bindings. I have tried moving the validation config into the shell.js but the result is the same (and not a good idea anyway).
Edit:
So far looks like errorElementClass: 'input-validation-error' is not being reapplied to the element after navigation. If the field is modified-focused-cleared , the validation fires normally. validationElement is the placeholder for the errorElementClass
Update
Found this bit of info at the github site and seems to be what im after
in {view}.js
function activate() {
vm.memberno.isModified(false);
return true;
}
The above code seems to work for input fields but not for select fields. Another idea I'm looking at is adding a binding handler like so
ko.bindingHandlers.validationElement = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var valueIsValid = valueAccessor().isValid();
if (!valueIsValid) {
$(element).addClass("input-validation-error");
} else {
$(element).removeClass("input-validation-error");
}
}
}
which works for all selects and inputs but is always on. If there's a way to deactivate this binding until the first form submit fires I think that will do it.
There needs to be a way to re-apply the binding or cause the binding to update. Try putting some code in the view model's activate function that forces validation.

Yii: How to force CGridview to load data via AJAX only?

Is there a way to make a CGridView Not load data on the initial rendering of the view it lives on, and instead make it load the first page with a subsequent AJAX request after the initial page load?
This is mostly for performance optimization. There is a data model that is rather slow behind that CGridView, and I would like to be able to have the page load in a snappy way, then have the data load up a few seconds later with an AJAX request.
You could modify the action as follows:
public function actionIndex() {
$dataProvider = new CActiveDataProvider('User'); // The dataprovider your grid uses
if (!$this->isAjaxRequest()) {
$dataProvider->criteria->addCondition('1 = 0'); // You could also use 0, but I think this is more clear
}
[...]
}
And then in your view in the javascript section:
$(function() { // If you are using jQuery this is executed when the page is loaded
$.fn.yiiGridView.update("{id-of-your-grid-view}");
});
Brewer Gorge was very close, and thanks to his suggested answer, it put me on the right track. This works:
// Controller, after creating $dataProvider, before calling $this->render...
if (!Yii::app()->request->isAjaxRequest) {
$dataProvider->criteria->addCondition('1 = 0');
}
// View
<script type="text/javascript">
$(window).load(function() {
$('#id-of-grid').yiiGridView('update');
});
</script>
Just write your code in controller like this:
$model= new Data('search');
$model->unsetAttributes();
if(isset($_GET['Data']))
$model->attributes = $_GET['Data'];
if (!Yii::app()->request->isAjaxRequest)
$data->id=0; //or something that sure model return empty