Been struggling hunting around the web looking for an answer to this one but to no avail. What I want to do is show an icon beside a textbox if the field is valid.
I have a form that posts back data to the controller to be checked against some internal logic, essentially what I want is to post the form run the method to check the data and if it is valid it should show a green tick beside the field. I have validation on the form and it is working as expected if the validation fails, just not on success.
Any help would be much appreciated.
Thanks
As for code not sure it is really relevant but it follows the structure below.
// Controller
private ActionResult DoSomeThing(myModelType model)
{
//... Do checking in here
}
// Model
[Required(ErrorMessage = "Please enter a value.")]
[StringLength(45, ErrorMessage = "Value can only have a maximum of 45 characters.")]
public string SomeValue { get; set; }
// View
#Html.LabelFor(x => x.SomeValue , new { #class="control-label" })
#Html.TextBoxFor(x => x.SomeValue , new { #class="form-control" })
#Html.ValidationMessageFor(x => x.SomeValue )
The way you could achieving "Client-side custom data annotation validation" is using jQuery. Use $('#myelement').change(function () ) syntax for tracking changes on you element on view. Use function () for validation logic and then based on your resault use a green check mark visible beside your element you want to validate.
Also you could some third party one like here.
Related
EDIT: I got it working now for text fields (see the comments below the question).
However, for some reason, the validation doesn't kick in for my dropdown-list.
In my ViewModel I have this:
[Display(Name = "Country")]
[Required(ErrorMessage = "{0} is definitely required!")]
public int CountryId { get; set; }
In my View I have this:
#Html.LabelFor(m => m.CountryId, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.CountryId, Model.CountryList, "—Select a country --",
new { #class = "form-control" })
<div class="container_voor_error">
#Html.ValidationMessageFor(m => m.CountryId, "", new { #class = "text-danger" })
</div>
</div>
The code in the controller need not be given here I think, since it works fine: it fills the CountryList with the countries, and then later after submit it does the server-side validation and processing, no problem.
So the scenario is this:
I select a country in the dropdown list (say, Belgium);
Then I de-select that choice again, so that it shows “-- Select a country -- “ again;
Now the (lazy) client-side validation should kick in and result in an error message: “Country is definitely required!”;
But that doesn’t happen, no error message is shown.
So... Am I forgetting something, or doing something wrong?
END-OF-EDIT
An easy question for the MVC/ASP.NET experts, I suppose. But as I'm still finding my way in this framework (school courses), this is something I've been wondering about, and that I haven't found an explicit answer to.
It's about client side validation of form input, using annotations in my model (or viewmodel). I get that, as long as one or more of the annotated validation requirements are NOT satisfied, the values of the form fields are NOT sent to the server upon submit. Instead, the same form view is displayed again, this time with the applicable error message shown. That is, of course, assuming that an HTML helper for those error messages (like Html.ValidationMessageFor) is included with the form fields. So far, so good.
However, there's such a thing as "validation-as-you-type". Meaning that the error messages appear (and change) while the user is still typing, as opposed to appearing only on the re-loaded form after the user has hit submit. And this is my question: Is "validation-as-you-type" supposed to be operational automatically? Because that's what seems to be generally implied (though hardly explicitly stated) by internet sources/tutorials.
But my experience has been different: In order to have "validation-as-you-type", I always need to write JavaScript/jQuery event handlers (such as keyUp() and onChange()), in which I explicitly call
$('some-selector').validate().element('some-element');
So my question is: Is it normal that I have to make these explicit calls to method validate() for each to-be-validated form element? Or is there, in ASP.NET/MVC, a way to enable automatic "validation-as-you-type" for all elements (or perhaps a subset of elements) on a view form?
Thanks.
No, ASP.NET MVC does not offer Automatic "validation as you type". To have a "validation as you type" it must be javascript code. ASP.NET Mvc uses jQuery Validation plugin by default, and this plugin does validation on blur event, which is not the same as on keyup events.
All that ASP.NET MVC does, is render some html attributes to the elements, also known as unobtrusive validation. You can check this if you see the page source with validation enabled and then with disabled. Then there is a javscript library that uses the values in those attributes to hook up to proper events.
Lastly, one is also able to use different plugin/library for this, and also one is also able to change how ASP.NET emits validation to the client side.
This is how you validate immediately. This works in ASP.NET Core. This validates immediately, not only after the first blur.
In document.ready (jquery), do this:
function CustomValidationMarco() {
var validator = $("form").data("validator");
if (validator) {
//validator.settings.onkeyup = true; // disable validation on keyup
validator.settings.onkeyup = function (element) {
$(element).valid();
};
//validator.settings.onfocusout = function (element) {
// $(element).valid();
//};
}
}
In an MVC view with a form on it and given the following code using HTML helpers:
#Html.TextBoxFor(m => m.FirstName, new { id = "firstName", maxlength = "50", #class = "form-input" })
Is there a way to stop this form automatically populating the fields with data? I know it's by design and in most cases is helpful but in this case I want to be able to turn that functionality off.
To clarify - If I have a ViewModel with this property in it:
[Display(Name = "First Name")]
public string FirstName { get; set; }
I understand that the HTMLHelper TextBoxFor will allow me to use the strongly typed
m => m.FirstName
when creating the input.
That's fine. However, if I have the ViewModel populated with data then it also shows this data in the input field, this is by design and I get that.
Now, imagine you wanted the strength of the strongly typed aspect but without the automatic filling of the data.
The only option appears to be:
#Html.TextBox("myTextBox", "value goes here", new { #class = "form-control" })
Which is brittle where the name / id values are concerned. I prefer the strongly typed nature of the TextBoxFor but can't have that without also automatically showing whatever the ViewModel data is.
It sounds like you're talking about the browser behaviour here, so you want to turn off autocomplete. You can use this on a per input basis:
#Html.TextBoxFor(m => m.FirstName, new { ..., autocomplete = "off" })
Or you can do it on the whole form:
#Html.BeginForm(action, controller, FormMethod.Post, new { autocomplete="off" })
EDIT:
Reading your comments it seems you're populating the view model with the values and seeing them when you render the view. In which case, the answer is simple, reset the model before passing it to the view:
return ViewModel(new TModel());
While passing the viewmodel to view in controller you can set those properties to blank value or null for which you want empty textboxes in UI.
I'm using Kendo UI for MVC and am experiencing strange behavior with the ComboBox when I do the following:
Select an item in the ComboBox.
Navigate away from the page.
Use the browser's Back button to return to the page.
Upon returning, the ComboBox is blank (or the placeholder appears if I configured that). However, when I open the ComboBox, the item I had originally selected is highlighted. Thus, it appears that the value was retained though the text was not.
Normally, data entry items retain the value they had when you left the page, so this behavior seems non-standard.
Is there any way to make sure the ComboBox retains its text when returning to the page?
Thanks,
Ken
Seeing some of your code would help, but I will throw this out. This is a bit undocumented to use the Kendo().DropDownListFor but I find it to be the best. I have also found that it is best to go ahead and convert your list options from whatever your data looks like to IList of SelectListItem, saves a lot of grief later. This is a partial that gets dropped in a bunch of views.
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "ChangeProject" }))
{
#(Html.Kendo().DropDownListFor(x => x.CurrentProjectId)
.HtmlAttributes(new { style = "width: 450px;" })
.OptionLabel("Project...")
.BindTo(Model.ProjectSelectList)
.Events(events => events.Change("submitProjectForm"))
)
<script type="text/javascript">
function submitProjectForm() {
$('#ChangeProject').submit();
}
</script>
}
Model applied to page...
public class ThisViewModel
{
public int CurrentProjectId { get; set; }
public IList<SelectListItem> ProjectSelectList { get; set; }
...
}
You can set value of control equal text of commboBox before sumit or go to new page:
$("#controlid").val($("#controlid").data("kendoComboBox").text());
I have a model with various properties but the one of interest is a List of another type of Model.
For example:
public class User
{
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<UserInterest> Interests { get; set; }
}
I then use an Editor Template within my view to render out a view for each item of the model items.
#Html.EditorFor(x => x.Interests)
The EditorFor template looks something like:
#model Interest
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.InterestText)
#Html.CheckBoxFor(x => x.Delete)
....
</div>
Something very similar to the accepted answer here: Model Containing List of Models (MVC-3, Razor)
My question is - how would you from the client-side (jQuery) create a new item within the property without going back to the server. I currently have a rough way of doing it whereby I post the data back to my controller which returns the model back with a new blank item within the Interests property.
This seems to be overkill making a HTTP request and not very elegent. I was thinking of using jQuery .Clone() but not entirely sure on what I'd need to do in terms of naming the elements and clearing existing values.
So does anybody have any suggestions. I'm hoping to get more opinions and different approaches.
You can simply create the Textbox and checkbox on the fly and add that to the DOM. When saving it, Use jQuery ajax to post that data ( new record data) to an action method and save it there. Return a status back (Succcess /Falied) from your action method to your client side code ( your callback function of jQuery ajax/post) and check it there. If it is success, Show a success message to the user and append the new item to the existing list.
Sample jSFiddle : http://jsfiddle.net/carwB/2/
If you want to return some complex data ( ex : All new records with its id etc..) along with the status, you may return JSON from your action method.
EDIT : To keep your Model binding works with the newly added dynamic elements, you need to follow the naming convention of the elements.
The trick is to keep the id property value of the html element in this format.
CollectionName_ItemIndex__PropertyName
and name property value in this format
CollectionName[ItemIndex].PropertyName
I created a sample working program and explained it how it works Here based on your requirements.
In such situations I prefer to use client templating. You send data to server with ajax and then receive JsonResult. Look at JsRender this is javascript lib without jQuery dependency.
1.Create two partial view one is for list item and second one is creation
2.First partail view should be inside the div which has id 'divMdeolList'
3.and Creation view will have the code like that
#using (Ajax.BeginForm("SubmitData", new AjaxOptions { UpdateTargetId = "divMdeolList" }))
{
#Html.TextBoxFor(x => x.InterestText)
<p>
<input type="submit" value="Create" />
</p>
}
4. And then create a ActionResult type action on controller that will render the partialview
public ActionResult SubmitData(YourModel model)
{
//Do : save the record
return PartialView("FirstPartailView", model);
}
This will update the View without postback
I have a controller with two actions:
[AcceptVerbs("GET")]
public ActionResult Add()
{
PrepareViewDataForAddAction();
return View();
}
[AcceptVerbs("POST")]
public ActionResult Add([GigBinderAttribute]Gig gig, FormCollection formCollection)
{
if (ViewData.ModelState.IsValid)
{
GigManager.Save(gig);
return RedirectToAction("Index", gig.ID);
}
PrepareViewDataForAddAction();
return View(gig);
}
As you can see, when the form posts its data, the Add action uses a GigBinder (An implemenation of IModelBinder)
In this binder I have:
if (int.TryParse(bindingContext.HttpContext.Request.Form["StartDate.Hour"], out hour))
{
gig.StartDate.Hour = hour;
}
else
{
bindingContext.ModelState.AddModelError("Doors", "You need to tell us when the doors open");
}
The form contains a text box with id "StartDate.Hour".
As you can see above, the GigBinder tests to see that the user has typed in an integer into the textbox with id "StartDate.Hour". If not, a model error is added to the modelstate using AddModelError.
Since the gigs property gigs.StartDate.Hour is strongly typed, I cannot set its value to, for example, "TEST" if the user has typed this into the forms textbox.
Hence, I cant set the value of gigs.StartDate.Hour since the user has entered a string rather than an integer.
Since the Add Action returns the view and passes the model (return View(gig);) if the modelstate is invalid, when the form is re-displayed with validation mssages, the value "TEST" is not displayed in the textbox. Instead, it will be the default value of gig.StartDate.Hour.
How do I get round this problem? I really stuck!
I think the problem is that your ViewModel does not match closely enough with your View. It's really important in MVC that your ViewModel matches your View as closely as possible.
In your ViewModel you're assuming an integer, but in your View you're using a TextBox to render the property, which will allow any kind of text. There's a mismatch here and the difficulties you are experiencing trying to map them is a symptom of the mismatch.
I think you should either:
1. Change the type of the ViewModel property to string and then do validation in your controller to ensure the string entered is actually a number or:
2. Change the control that the View renders to a control that will only allow a number to be entered via a custom control or Javascript validation (as #Qun Wang recommends)
Personally, I'd recommend option 1. That way the ViewModel is not dependent on the View implementation.
Could you do this in your PrepareViewDataForAddAction method?..
if (!ViewData.ModelState.IsValid)
{
ViewData["StartDate.Hour"] = "Error";
}
The other fields on the form will still populate based on the properties of the Gig object.
I think you need to do some basic client side validation first.
don't allow it to post to the server.