jQuery Mobile and Unobtrusive Validation - asp.net-mvc

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

Related

MVC Unobtrusive validation for a dropdown using knockout.js?

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.

How to specify that a Search Input field is required

I am working with an asp.net mvc web application and i have a text input field to enter a search criteria, but my question is how i can define that this field is required incase the user click on the search button. i was thinking of manually adding the following data dash attributes, to simulate how the [Required] model data annotation works .
<input name="selectedUserNames" type="text" data-val="true" data-val-required= "Please enter a value." data-autocomplete-source= "#Url.Action("AutoComplete", "SecurityGroup")" />
<span class="field-validation-valid" data-valmsg-for="selectedUserNames" data-valmsg-replace="true"></span>
<input type="submit" value="Seach" />
What you are doing is not a clean solution, and it only works on the client side. You should create a SearchViewModel like below and pass it to your View:
public class SearchViewModel
{
[Required]
public string Search { get; set; }
}
And, in your View:
#Html.TextBoxFor(model => model.Search)
#Html.ValidationMessageFor(model => model.Search)
as noted in previous answer, enabling only client side validation is not a complete solution. what if the client has disable javascript on their browser? then your validation want fire.
best option you have is to use DataAnnotation [Required] attribute. In your view enable client side validation.
If the client has disable javascript on browser server side validation will fire.

Exclude <input type='file'> from ViewModel

I have the following file input tag in the "Create" View:
<input type="file" id="RequestFile" name="RequestFile"/>
#Html.ValidationMessage("RequestFile")
The ViewModel contains this corresponding property:
[Required(ErrorMessage="Please select a file")]
public HttpPostedFileBase RequestFile { get; set; }
This works fine in the "Create" View, but in the "Edit" View, I get ModelState.Isvalid as false. Using the same ViewModel I would like to exclude this field from validations because I would not want to upload the file again.
I tried simply disabling the input tag like this:
<input type="file" id="RequestFile" name="RequestFile" disabled/>
This has a disabled input control but the Validation still fired.
Also applying the BindAttribute in the Controller did not work (see this Question)
Ideally (I know it sounds unlikely), if there is a server-side solution to this, please post your thoughts. If there is a small client-side trick, please let me know!
The best ways are remove the property altogether, and always access it directly from the form collection (and manually validate it) or manually remove the model state error using the property name (as #cheesemacfly has in his comment, ModelState.Remove("RequestFile")). The latter makes it very easy to fix then.
You could use form.onsubmit to check to see it document.getElementById("RequestFile").value is not null/empty and cancel the submit if it is.
something like
<form onsubmit="if(!document.getElementById('RequestFile').value){alert('Please select a file.');return false;}" >
<input type="file" id="RequestFile" name="RequestFile" />
<input type="submit"/>
</form>
return false cancels the submission.
http://jsfiddle.net/Cg7HY/1/
or put it in the click event of the submit button itself
http://jsfiddle.net/Cg7HY/3/

performing searches with ASP.NET MVC

Ok, I've just started learning ASP.NET MVC after being an ASP.NET developer for awhile. I think my main problem I'm having is trying to "unlearn" ASP.NET when developing my MVC projects.
Here's my question: I have a page which has some input fields. These fields are parameters to a search I'm trying to run against a database. The user checks the boxes next to the types of items they want to see and then clicks "Search". Very simple stuff.
I'm having trouble wrapping my mind around how exactly to "postback" to the page to display the results. Is it better to use jQuery and serialize the form? Do I use my Entity Framework models I've created? What's the best way to go about
I'm really excited about MVC and the control it gives me, but I need to get over these initial obstacles if I ever want to "sell" it to my boss as the way to develop all of our web apps. Thanks for reading!
If you haven't already, consider taking a look at the NerdDinner Tutorial featured in Professional ASP.NET MVC 1.0 by Rob Conery, Scott Hanselman, Phil Haack, and Scott Guthrie. It contains a great demonstration of many of the features of ASP.NET MVC including performing a search and returning the data both through a full page post and also asynchronously using JSON.
If your inputs are inside html form element (different story if javascript is involved) - you can use default model binding (it binds route values and querystring parameters too).
<form ...>
<input type="text" name="query" />
<input type="submit" .../>
</form>
on submit it will automagically bind form values (by name) to action parameters:
public ActionResult PerformSearch(string query)
{
//whatever
}
In your case - i suspect you got inputs as checkboxes. Something like this should work:
<form...>
<input type="checkbox" name="p" value="value1" />
<input type="checkbox" name="p" value="value2" />
<input type="checkbox" name="p" value="value3" />
<input type="checkbox" name="p" value="value4" />
<input type="checkbox" name="p" value="value5" />
</form>
public ActionResult PerformSearch(string[] p)
{
//whatever
}
Only - if (form method == "GET"), URL won't look nicely. :)
To show results, make a model for your view in action and just show it through view:
public ActionResult PerformSearch(string[] p)
{
var model = _searchService(p);
return View("Results", model);
}
Views/Results.aspx
<% foreach(var bar in Model){ %>
<%= bar.Name %>
<%}%>
P.s. When considering AJAX calls, always remember that you are loosing ability to show URL + search engines don't understand JS.

View Control name concatenated with ClientID and masterpage control name in ASP.NET MVC

I am working with an ASP.NET MVC application.
I have one master page having one contentplaceholder.
I have one view placed in the contentplaceholder of master page.
I have a few textBoxes that say "name", "age" , "email" in them.
I also have a submit button in my master page.
when I click the submit button, postback event will be called in the controller.
//POST
public ActionResult Result(FormCollection Form)
{
}
If I try to access the value of the text box name using
Form["name"]
it will give me null value.
Instead
Form["$ct100$contentplaceholder1$name"]
will give me the correct value.
How can I get the value using only name?
Don't mix Web Forms with MVC
You shouldn't be using <asp:TextBox id="name" /> but rather
<%= Html.TextBox("name") %>
The input name was autogenerated for you, which you don't want to happen. Try to generate those inputs in MVC-style, like this:
<%=Html.TextBox("name")%>
or like this:
<input type="text" id="name" name="name" value="" />

Resources