I have an ASP.NET MVC page that has a list of items...
//a vague representation
Model.someValue[0] Model.someHiddenValue[0]
Model.someValue[1] Model.someHiddenValue[1]
Model.someValue[2] Model.someHiddenValue[2]
All fields are optional, but they do have some validation, of which I am showing validation messages.
The problem is, when I submit once and return the original view instead of a redirect, the hidden fields are not getting their new value, but their new value is in the view model being passed to the view.
I am thinking the ModelState might be overriding the model as part of validation. I know I can do a redirect and bypass the problem, but I want be able to save part of the form and show validation errors for the rest. If there are no other solutions, I will simply validate the whole form and only save when all items are valid.
If values are present in ModelState, it shows them instead of the values in your model, since they represent the "last" known state provided from the user.
You should clear out ModelState if you're not actually intending the user to re-do their edits.
Did you try this?:
var newValue = new ValueProviderResult("value", "value", System.Globalization.CultureInfo.CurrentUICulture);
ModelState.SetModelValue("someHiddenValue", newValue);
Related
I have an dot net 5.0 mvc page which takes a model object (ClassFoo) which when constructed generates an instance of (classBar). ClassBar has various properties (FieldA, FieldB, FieldC) all are strings.
The Views content is all in a form and the form's input fields are all from ClassFoo.ClassBar's various properties. Initially, when the page is accessed, all form input values are empty. However when I put data into them and then submit the form, the form values are still there when the page loads. However I don't understand why this is because I'm explicitly creating a new model during the controller operation but I am not actually populating the Model.ClassBar with the content from the post before I return model to the View for generation.
What I would expect is that all of the form fields would be empty however that is not the case. I know if asp.net the form values are stored and restored automatically but I didn't think that happened in mvc.
After looking into ModelState recommended by Nick Albrech in the comments I reviewed the hint associated w/ the HtmlHelper.TextBoxFor() which states the following:
... Adds a "value" attribute to the element containing the first non-null value found in: the ActionContext.ModelState entry with full name, or the expression evaluated against ViewDataDictionary.Model. See [IHtmlHelper.NameFor] for more information about a "full name".
So effectively what's happening is similar to what I thought asp.net mvc wasn't doing in that it populates the ModelState from a get/post request with the name and values of the form being submitted. Then based on the use of these helper functions (and also asp-for attributes used in razor views views), it either provides values from the saved model state form values OR the model passed to the view. Note: this does not seem to work if you set the value of an input element = #Model.[someProperty]
The take away from this is that you do not necessarily need to update your model object with content from the previous form submit in order to have the page populate the form contents back to the screen. Allow asp.net mvc to do the manual tasks by making use of these razor helpers.
Shoutout to Nick for the assist on this one. A solid member of the stackOverflow community.
My model does not really represent what my form is posting. Example my Orgs Model which holds orgs helps me generate a treeview the users selects several nodes of the orgs tree and submits a form. The form posts an array[] or org ids.
(maybe i'm doing this all wrong, please let me know tried binding to models and that was confusing when dealing with trees grids etc and using partial views and ajax returning partial views and editorfor's etc.. the default model binding was useless)
anyways back to my point, since I want to validate if any orgs get selected:
if (SelectedOrgs == null) //array[]
{
ModelState.AddModelError("OrgsNotSelected",IValidationErrors.OrgsNotSelected);
}
my question is how do i retrieve this random key that i just made up from my view? my model and even my viewmodel do not have an array for the selection this is just the result of the post.
I'm not sure what to do in the view to get the value for "OrgsNotSelected".
Thank you!
Bilal
If you were doing normal submit actions to your controller, you would need to use the ValidationSummary to display errors that are not attached to a specific property.
As you are using Ajax, you would be better off returning a json result from your controller and you can define this so that it includes your errors in a format you can use in the success function to display your messages.
I have an entity with, amongst others, the fields: CreatedBy, CreatedAt, ChangedAt
Now in the view, I do not want the user to fill these fields.
My approach was to fill them on the HTTP POST Action, before ModelState.IsValid check is made and before saving the data to the database.
However, ModelState.IsValid keeps returning false no matter what. What is the right way to implement this? Should I take the validation (ModelState.IsValid) from the POST action?
One problem, many solutions (from best to worst, in my opinion).
First solution
The best way would be to use a ViewModel (a class containing only the fields which must be edited by user and / or validated).
In this case, the CreatedBy, CreatedAt, ChangedAt fields would not appear in your ViewModel class.
Second solution
If you don't want ViewModel, you can put the "not mutable by user" fields as hidden fields in your view
#Html.HiddenFor(m => m.CreatedAt)
of course, hidden fields can be changed by user, so it's always an open door to unwanted datas...
Third solution
Clean ModelState from undesired errors before checking if it's valid (should become really boring if you've many fields concerned).
if (ModelState.ContainsKey("CreatedAt")) {
ModelState["CreatedAt"].Errors.Clear();
}
//then test for ModelState.IsValid()
Other solutions
WhiteList, BlackList for model binding, or... anything I forgot !
Many projects like to separate the View Model from the Domain Model. This allows you to create a View-Model class specifically tailored for the data you want to render and/or receive in a certain action while keeping the domain model correct/consistent.
In your View-Model class you would either not define any such property as the created date (since it is not supposed to be posted but is determined in the action). Or if you use one and the same View-Model for rendering and posting, and you want to render the date, you can make the date nullable (see Alexey's answer) on the View-Model while keeping it mandatory on the domain model.
DateTime is not nullable. If you want to keep those fields as null during model binding, if there is no values for them use:
Nullable<DateTime>
or shortcut:
DateTime?
I'm working on an ASP.NET MVC2 app. I've come to realize a very surprising, yet amazing thing that MVC does behind the scenes having to do with the ModelState and model binding. I have a ViewModel which has a whole bunch of data - some fields being part of a form while others are simply part of the UI. On HttpPost, my Action method uses the DefaultModelBinder which attempts to bind the whole model, but only fields which were part of the form are successfully deserialized - all others remain null. That's fine and understandable. If the ModelState is invalid, I need to refresh the model from the db and bind those particular form fields before returning to the same edit view to display those associated ModelState validation errors.
Here's where my amazement and curiosity comes. It was my assumption that in order for me to bind the form fields with the refreshed model, I needed to make a call to either UpdateModel() or TryUpdateModel<>(), passing in the newly refreshed model. For example:
[HttpPost]
public ActionResult EditDetail(EditDetailItemModel model)
{
if (model.IsValid)
{
// Save the results to the db
return RedirectToAction(...)
}
// Can't simply "return View(model)". Not all fields in EditDetailItemModel
// were part of the form - thus they returned null. Have to refresh
// model from the db.
var refreshedModel = RefreshModelFromDB();
// Is this line necessary?????
TryUpdateModel<EditDetailItemModel>(refreshedModel);
return View(refreshedModel);
}
But, what I found was that if I simply returned refreshedModel to the view WITHOUT making a call to TryUpdateModel<>(), the refreshed model was automatically bound with the form field values posted!! Hence, the TryUpdateModel<>() is not needed here!
The only way I can make any sense of it is that since the ModelState is in an invalid state, once I returned the view with the refreshed model, the "MVC rendering engine" looped through the ModelState errors and bound those property values with my refreshed model. That is simply AWESOME! But, I want proof as to this assumption. I can't find documentation regarding this anywhere on the web. Can anyone either confirm my hypothesis of WHY/HOW this AWESOME auto binding behavior is occuring and/or educate me as to why/how it's happening, hopefully backed up with some online documentation links so I understand more fully what's going on under the covers?
public ActionResult EditDetail(EditDetailItemModel model)
That line will perform model binding. Think of ActionMethod parameters as always being populated by a call to UpdateModel.
You are not seeing refreshedModel's values in the view, you are seeing the ModelState entries and values from EditDetailItemModel.
After a user clicks the submit button of my page, there is a textbox that is validated, and if it's invalid, I show an error message using the ModelState.AddModelError method. And I need to replace the value of this textbox and show the page with the error messages back to the user.
The problem is that I can't change the value of the textbox, I'm trying to do ViewData["textbox"] = "new value"; but it is ignored...
How can I do this?
thanks
You can use ModelState.Remove(nameOfProperty) like:
ModelState.Remove("CustomerId");
model.CustomerId = 123;
return View(model);
This will work.
I didn't know the answer as well, checked around the ModelState object and found:
ModelState.SetModelValue()
My model has a Name property which I check, if it is invalid this happens:
ModelState.AddModelError("Name", "Name is required.");
ModelState.SetModelValue("Name", new ValueProviderResult("Some string",string.Empty,new CultureInfo("en-US")));
This worked for me.
I have a situation where I want to persist a hidden value between POST's to the controller. The hidden value is modified as other values are changed. I couldn't get the hidden element to update without updating the value manually in ModelState.
I didn't like this approach as it felt odd to not be using a strongly typed reference to Model value.
I found that calling ModelState.Clear directly before returning the View result worked for me. It seemed to then pick the value up from the Model rather than the values that were submitted in the previous POST.
I think there will likely be a problem with this approach for situations when using Errors within the ModelState, but my scenario does not use Model Errors.