MVC Unobtrusive validation for a dropdown using knockout.js? - asp.net-mvc

I am using knockout.js to populate a dropdown:
<select data-bind="options: AvailableUsers, optionsText: 'DisplayName', value: SelectedUser, optionsCaption: '-- Select a User --'" data-val="true" data-val-required="You must select a user." id="SelectedUser" name="SelectedUser"></select>
<span class="field-validation-valid" data-valmsg-for="SelectedUser" data-valmsg-replace="true"></span>
and I am registering the validator to the form and having it called on the submithandler (I dont think this is related to the problem since the validation is executing):
$.validator.unobtrusive.parse("#UserProfileCreation");
$("#UserProfileCreation").data("validator").settings.submitHandler = mappedModel.save;
However when trying to submit the page, it always acts like the dropdown has no selected value. Even when I confirm via console that SelectedUser has a value. I have successfully done the same thing in other pages for textareas like so:
<textarea style="width: 100%; height: 50px; min-height: 30px;" name="GroupReply" id="GroupReply" data-bind="value: GroupReply" data-val="true" data-val-required="You must enter a reply."></textarea><br/><span class="field-validation-valid" data-valmsg-for="GroupReply" data-valmsg-replace="true"></span>
And that works fine. So I am not sure what I am missing for the dropdown, but whether I select an option or not, it keeps acting like it's blank and bringing up the validation error message. What am I missing?

I figured it out and it was quite simple due to my lack of understanding of how knockout handles dropdown's selected values.
My AvailableUsers in the KO View Model consisted of a list of KeyValueModels which were based off a C# MVC class (converted using the KO mapping plugin):
public class KeyValueModel{
public Guid Id {get; set;}
public string DisplayName {get; set;}
}
I simply needed to add optionsValue: 'Id' to the data-bind attribute of the dropdown. However it should be noted that this only works because I am passing a Guid as the SelectedUser property of the MVC View Model in the action parameter.
There have been times where I wanted to pass an entire javascript object that the dropdown selection represents to the MVC view model, in those cases this solution wouldn't work.
Note in console, if you do NOT have the optionsValue: 'Id' you select an option from the dropdown and type mappedModel.SelectedUser() you get:
Object {Id: "adb9ae2d-01c8-468d-96e6-06ec39039e29", DisplayName: "Johnson, John"}
Because knockout can store the whole selected object. HOWEVER, knockout does not set ANY value to the options in the dropdown in the actual HTML markup, they are all null (which is why the validation was failing).
If you do add optionsValue: 'Id' and type mappedModel.SelectedUser() into console, then you would simply get:
"adb9ae2d-01c8-468d-96e6-06ec39039e29"
Which for my purposes on this page is fine. As mentioned if you wanted to pass an entire object to the MVC action based on that selection, this setup would not work. You would probably have to do something like setup a hidden field and set it's value to the SelectedUser().Id and put the validation on that hidden field.

Related

Razor/MVC Empty List Validation

I have a SelectList of IDs that the user can choose zero-many items into a List. By default the validation logic appears to require at least one element to be selected. I have tried using the annotation [MinLength(0)] as various documentation implies that it applies to not only strings but collections/lists.
The obvious answer to my question is to just disable validation of the property.
The slightly less obvious answer is to write my own custom ValidationAttribute which seems to be more effort than warranted.
I am looking if there is another simple way.
My ViewModel property:
public List<int> DiverIDList { get; } = new List<int>();
My CSHTML:
#{
var DiverSL = (SelectList)ViewData["DiverSL"];
}
<div class="form-group">
<label asp-for="DiverIDList" class="control-label"></label>
<select asp-for="DiverIDList" class="form-control"
asp-items="#DiverSL" size="8">
<option value="">-- Select Divers --</option>
</select>
<span asp-validation-for="DiverIDList" class="text-danger" />
</div>
In order to better understand the problem, I decided to create a custom ValidationAttribute derived from RequiredAttribute. Although I was able to verify that my new validation attribute's IsValid was getting called, there were still cases when the ICollection.Count == 0 was causing the custom validation to not be called and the server ModelState contained an error. Something in the framework is bypassing attribute based validation in this case.
Because I have Nullable enabled, I tried the different combinations of making the List nullable or not. No change in behavior.
The only viable solution appears to be disabling validation for that property. This requires two steps:
In the OnPost method before calling ModelState.IsValid, insert a call to ModelState.Remove("ModelDiver.DiverIDList");
In the CSHTML, add data-val="false" to the <select> tag.

Binding checkboxes to array with Knockout.js (MVC Razor)

I have a Knockout.js view model that looks like this:
"UserName":null,
"FirstName":null,
"LastName":null,
"Countries":{
"arrCountries":[]
}
And a set of country checkboxes that all must be rendered with the same data-bind value (I'm using this custom CheckBoxListFor helper, which can apply custom HTML to each checkbox, but it's the same custom HTML for each checkbox. It takes a property that lists every available country from my razor view model to create the checkboxes, and then renders some as marked based on another razor view model property).
The checkbox values are integers (1, 2, 3, etc) and it's those integers that I'd like to throw into the arrCountries property of the view model.
Here's how a checkbox is rendered as it stands at the moment:
<input checked="checked" data_bind="checked: ???" id="Countries.arrCountries107" name="Countries.arrCountries" type="checkbox" value="1"></input>
I've proven the concept of using viewModel.Countries.arrCountries.push(1); to update the view model (followed by an alert that gives me a count of the elements in arrCountries to prove that the push worked), but I can't seem to get the data-bind HTML property on the checkboxes to wire up so that they check and uncheck as I send push and remove commands.
Any help much appreciated!
Make the checked data attribute point to the selected countries. If the checkbox's value is in the array of checkboxes, then it will be checked. It won't otherwise.
<input checked="checked" data-bind="checked: Countries" type="checkbox" value="3"></input>
Take a look at this fiddle I put up.
http://jsfiddle.net/u8xP9/3/

Clientside validation attributes on hidden,radio, dropdown elements by default in asp.net mvc 3

I'm using ASP.NET MVC 3 + FLUENT VALIDATION + NINJECT
I have model X and rules for that model are something like this:
RuleFor(c => c.FirstName).NotEmpty();
RuleFor(c => c.LastName).NotEmpty();
I spotted a little strange thing, the engine puts validation attributes on all hidden elements and on dropdowns, radio buttons, etc.., and I didn't specified this in the validation configuration for that model, so I guess it is by default...
<input type="hidden" value="1" name="Id" id="Id" data-val-required="&#39;Id&#39; must not be empty." data-val-number="The field Id must be a number." data-val="true">
Validation works because hidden element always have a value, but I have a problem with radio buttons. For example, if I don't want one radio button always to be selected by default but empty and if I want to put validation rules on that item, the rendering puts default validation attributes and on top of my rules, so it's getting messed up and validation doesn't work properly...
Anyone had similar issues or knows about this, or do I have to pull the ASP.NET MVC source and look it up by myself? :)
Semi-Lazy and little-pushed-down-by-deadlines coder
Edit:
I tried proposed solution from this link:
Fluent Validations. Error: Validation type names in unobtrusive client validation rules must be unique
but asp.net mvc emits required attributes on each field regardless of AddImplicitRequiredAttribute settings...
Make the Id property on your view model a nullable integer.
So instead of:
public int Id { get; set; }
you should use:
public int? Id { get; set; }
Same stands true for any other value types that you don't want to be required. You should use nullable types.
Html helpers automatically emit data-val attributes for all non-nullable types which is by design and if you do not want this behavior you will have to write your own custom HTML helpers to generate your input fields and dropdowns. You can no longer rely on TextBoxFor and DropDownListFor helpers as that's how they are designed.

How to create a single select list box that shows the selection using Razor

How do I create a simple, single select list box using the Razor view engine? I'm currently running into two problems. The first is that the list box "Select" code generated has 'multiple' automatically added. Question One is how to turn that off. No, I don't want to have to use a drop down list box.
Question Two is trickier. The generated "Select" in the output html does not show any items as being selected, despite the item in question have selected values. Here's my code:
Object model:
public class Description
{
public String code { get; set; }
public SelectList codelist;
}
Controller:
code = "drain";
codelist = new SelectList(sourcelist, "Key", "Value", "drain");
View:
#Html.ListBoxFor(model => model.code, Model.codelist)
Output HTML:
<select data-val="true" data-val-required="The Select the permit type to apply for field is required." id="code" multiple="multiple" name="code"><option value="drain">Interior Drain Repair</option>
... yadda yadda yadda
</select>
You can see my two problems here. First, "multiple" has been added to the select list, and the selected option "drain" is not selected.
Any suggestions? I'm at the point of just tossing Razor and hand coding this stuff.
To create a single select list box you can use DropDownListFor but set a size attribute... so do this:
#Html.DropDownListFor(model => model.code,
Model.codelist,
new Dictionary< string, object >() { { "size", "3"} })
User Html.DropDownList instead of Html.ListBox to create a single select box.
Well, I've sort of got an answer to question one - turns out it's the browser that changes the rendering from simple list to dropdown list if "multiple" is removed, so I'm going to have to be creative to solve that one.
Question Two remains - why doesn't Razor keep my selected value during rendering?

jQuery Mobile and Unobtrusive Validation

I'm creating a jQuery Mobile (Alpha 3) based ASP.NET MVC 3 application utilizing the unobtrusive validation that comes with MVC3. When a page is accessed directly (no hash in the Url), validation works perfectly. However, when you navigate to the page, jQuery Mobile uses Ajax Navigation to dynamically load it (displaying the hash in the Url) and validation stops working.
Here is a sample of the code in use:
Model:
[Required(ErrorMessage = "Missing value")]
[DisplayName("Property Display Name")]
public int? PropertyName { get; set; }
View (Razor):
#Html.LabelFor(model => model.PropertyName)
#Html.TextBoxFor(model => model.PropertyName)
#Html.ValidationMessageFor(model => model.PropertyName)
Generated HTML:
<label for="PropertyName">Property Display Name</label>
<input data-val="true" data-val-number="The field Property Display Name must be a number." data-val-required="Missing value" id="PropertyName" name="PropertyName" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="PropertyName" data-valmsg-replace="true"></span>
It is possible that other pages have been loaded previously and the HTML elements no longer have unique Ids. Other than rolling my own Html Helper class to generate the HTML for the Label, TextBox, and ValidationMessage, is there any way to handle this scenario?
I've been struggling a bit with this same issue, but #Zote pointed me in the right direction.
parse() is the way to go, but make sure to pass in a selector ie:
jQuery.validator.unobtrusive.parse("form")
or
jQuery.validator.unobtrusive.parse(document)
The best way of hooking this up is probably through JQMspageshow event. This would then be triggered after each new page transition, like so, You may also prefer to do this before jqm has done it's magic on the page as well by using the pagebeforeshow event
$('div').live('pageshow',function(event){
jQuery.validator.unobtrusive.parse(".ui-page-active form");
});
By using the .ui-page-active, you narrow your search down to the currently active page.
Did you call jQuery.validator.unobtrusive.parse() after loaded new content?
Read this post at Brad Wilson's blog.
I solved the same problem I encountered, my answer is posted here -
Hash navigation problem while using jquery mobile with asp.net mvc2

Resources