After form submit Html editor helpers (TextBox, Editor, TextArea) display old value not a current value of model.text
Display helpers (Display, DisplayText) display proper value.
Is there any way editor helpers to display current model.text value?
Model
namespace TestProject.Models
{
public class FormField
{
public string text { get;set; }
}
}
Controller
using System.Web.Mvc;
namespace TestProject.Controllers
{
public class FormFieldController : Controller
{
public ActionResult Index (Models.FormField model=null)
{
model.text += "_changed";
return View(model);
}
}
}
View
#model TestProject.Models.FormField
#using (Html.BeginForm()){
<div>
#Html.DisplayFor(m => m.text)
</div>
<div>
#Html.TextBoxFor(m => m.text)
</div>
<input type="submit" />
}
When you submit the form to an MVC action the values of the input fields are recovered from the POSTEd values available in the form and not from the model. That makes sense right? We don't want the user to show a different value in a textbox than they have just entered and submitted to the server.
If you want to show the updated model to the user then you should have another action and from the post action you have to redirect to that action.
Basically you should have two actions one action that renders the view to edit the model and another one saves the model to database or whatever and redirect the request to the former action.
An example:
public class FormFieldController : Controller
{
// action returns a view to edit the model
public ActionResult Edit(int id)
{
var model = .. get from db based on id
return View(model);
}
// action saves the updated model and redirects to the above action
// again for editing the model
[HttpPost]
public ActionResult Edit(SomeModel model)
{
// save to db
return RedirectToAction("Edit");
}
}
When using HTML editors such as HTML.EditorFor() or HTML.DisplayFor(), if you attempt to modify or change the model values in the controller action you won't see any change unless you remove the ModelState for the model property you want to change.
While #Mark is correct, you don't have to have a separate controller action (but you usually would want to) and you don't need to redirect to the original action.
e.g. - call ModelState.Remove(modelPropertyName)...
public ActionResult Index (Models.FormField model=null)
{
ModelState.Remove("text");
model.text += "_changed";
return View(model);
}
And if you want to have separate actions for GET and POST (recommended) you can do...
public ActionResult Index ()
{
Models.FormField model = new Models.FormField(); // or get from database etc.
// set up your model defaults, etc. here if needed
return View(model);
}
[HttpPost] // Attribute means this action method will be used when the form is posted
public ActionResult Index (Models.FormField model)
{
// Validate your model etc. here if needed ...
ModelState.Remove("text"); // Remove the ModelState so that Html Editors etc. will update
model.text += "_changed"; // Make any changes we want
return View(model);
}
I had some similar problem, I hope I can help others have similar problem:
ActionExecutingContext has Controller.ViewData.
as you can see:
new ActionExecutingContext().Controller.ViewData
This ViewData contains ModelState and Model. The ModelState shows the state of model has passed to controller for example. When you have an error on ModelState the unacceptable Model and its state passed to View. So you will see the old value, yet. Then you have to change the Model value of ModelState manually.
for example for clearing a data:
ModelState.SetModelValue("MyDateTime", new ValueProviderResult("", "", CultureInfo.CurrentCulture));
Also you can manipulate the ViewData, as here.
The EditorFor, DisplayFor() and etc, use this ViewData contents.
Related
Scenario: How to update a model?
ASP MVC 6
I am trying to update a model. For passing the model information to the client(browser/app) I am using the DTO.
Question 1: For updating, should I post the whole object back?
Question 2: Is there a way I can easily pass only the information that is updated? If yes, how?
Question 3: Can I use JSON Patch for updation?
Question 2: Is there a way I can easily pass only the information that
is updated? If yes, how?
Yes. You should create a view model which should have only those properties needed for the view.
Let's assume your use case is to build a view which allows user to edit only their last name.
public class EditUserViewModel
{
public int Id {set;get;}
public string LastName {set;get;}
}
And in your Get
public ActionResult Edit(int id)
{
var user = yourUserRepository.GetUser(id);
if(user!=null)
{
var v = new EditUserViewModel { Id=id,LastName=user.LastName};
return View(v);
}
return View("NotFound");
}
And the view
#model EditUserViewModel
#using(Html.BeginForm())
{
#Html.TextBoxFor(s=>S.LastName)
#Html.HiddenFor(s=>s.Id)
<input type="submit" id="saveBtn" />
}
and your HttpPost action
[HttpPost]
public ActionResult Edit(EditUserViewModel model)
{
// Since you know you want to update the LastName only,
// read model.LastName and use that
var existingUser = yourUserRepository.GetUser(model.Id);
existingUser.LastName = model.LastName;
yourUserRepository.Save();
// TO DO: redirect to success page
}
Assuming yourUserRepository is an object of your data access classes abstraction.
Question 1: For updating, should I post the whole object back?
Depends on what you want from the end user. With this view model approach, It is going to post only the Id and LastName and that is our use case.
Can I use JSON Patch for updating?
Since you are only sending the data which needs to be updated (partial data), you should be fine.
If you want,you may simply serialize your form data(which has only Id and LastName) and use jQuery post method to send it to your server.
$(function(){
$("#saveBtn").click(function(e){
e.preventDefault(); //prevent default form submit
var _form=$(this).closest("form");
$.post(_form.attr("action"),_form.serialize(),function(res){
//do something with the response.
});
});
});
To prevent overposting, you can use a binding whitelist using Bind attribute on your HttpPost action method. But the safest strategy is to use a view model class that exactly matches what the client is allowed to send.
Instead of this
UpdateModel(model);
You now can call this
await TryUpdateModelAsync(model);
I'm trying to better understand how to properly structure my ASP.NET MVC code to handle a situation where a single view contains multiple forms. I feel that it makes sense to submit the forms to their own action methods, so that each form can benefit from its own view model parameter binding and validation, and to avoid putting all form parameters into 1 larger, monolithic view model.
I'm trying to code this pattern, but I can't seem to tie the loose ends together.
I've written some example action methods below, along with example view model classes, that I think demonstrate what I'm trying to achieve. Lets say that I've got an Item Detail action method and view. On this Detail view, I've got two forms - one that creates a new Comment and another that creates a new Note. Both Comment and Note forms POST to their own action methods - DetailNewComment and DetailNewNote.
On success, these POST handler action methods work just fine. On an invalid model state though, I return View(model) so that I can display the issues on the original Detail view. This tries to render a view named Brief though, instead of Detail. If I use the overloaded View call that allows me to specify which view to render, then now I have issues with the different view model classes that I'm using. The specific view model classes now no longer work with the original DetailViewModel.
I get the feeling that I'm doing this completely wrong. How am I supposed to be handling this scenario with multiple forms? Thanks!
public ActionResult Detail(int id)
{
var model = new ItemDetailViewModel
{
Item = ItemRepository.Get(id)
};
return View(model);
}
[HttpPost]
public ActionResult DetailNewComment(int id, ItemDetailNewCommentViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var comment = CommentRepository.Insert(new Comment
{
Text = model.Text
});
return RedirecToAction("Detail", new { id = id; });
}
[HttpPost]
public ActionResult DetailNewNote(int id, ItemDetailNewNoteViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var note = NoteRepository.Insert(new Note
{
Text = model.Text
});
return RedirectToAction("Detail", new { id = id; });
}
... with view models something like ...
public class ItemDetailViewModel
{
public Item Item { get; set; }
}
public class ItemDetailNewCommentViewModel
{
public string Text { get; set; }
}
public class ItemDetailNewNoteViewModel
{
public string Text { get; set; }
}
For your case I'd recommend to have a master model for example your
ItemDetailViewModel class to which you'll add a property for each sub-model
public class ItemDetailViewModel
{
public Item Item { get; set; }
public ItemDetailNewCommentViewModel NewCommentModel {get;set;}
public ItemDetailNewNoteViewModel NoteModel {get;set;}
}
Your Detail view will be the master view and the other two will be partial views.
Master view will receive an instance of ItemDetailViewModel as model and inside view you will render your partials by passing Model.NewCommentModel and Model.NoteModel as their corresponding models. For being able to use separate actions for each form, instead of regular forms you can use ajax forms, thus you will send to the server only relevant information without altering the rest of the master view.
The chief problem here is what happens when the user messes up and their post doesn't pass validation server-side. If you choose to take them to a page where just the one form is presented, then you can post to a different action, but if you want both forms re-displayed, then they both should point to the same action.
Really, you just have to make a choice. I've seen sites handle it both ways. Personally, I prefer to re-display the original form, which means handling both forms in the same action. It can lead to bloat, but you can factor out a lot of logic from the action such that you end up with mostly just a branch depending on which form was submitted.
Is there any way of changing the binding prefix with a value which comes from the request parameters?
I have many nested search popups, and all of them shares the same ViewModel.
I can add a binding prefix to all the fields when requesting for the Search filters, but i don't know how can i make the [Bind(Prefix = "")] to work with values coming from the request parameters.
// get the search filters with the bindingPrefix we need
public ActionResult Search(string bindingPrefix)
{
ViewData.TemplateInfo.HtmlFieldPrefix = bindingPrefix;
SearchViewModel model = new SearchViewModel
{
BindingPrefix = bindingPrefix
};
return PartialView("_SearchFilters", model);
}
// post the search filters values
[HttpPost]
public ActionResult Search([Bind(Prefix = model.BindingPrefix)]SearchViewModel model)
{
}
I don't know why you would want to do this, but this should work.
In your form on the view, have a hidden value
#Html.Hidden("BindingPrefix", Model.BindingPrefix)
Modify your action to the following
[HttpPost]
public ActionResult Search(SearchViewModel model)
{
UpdateModel(model, model.BindingPrefix);
}
Here is one of the examples that I've seen on how to do validation on Controller:
[HttpPost]
public ViewResult Create(MyModel response)
{
if (ModelState.IsValid)
{
return View("Thanks");
}
else
{
return View();
}
}
If there are validation errors, than return View() method is called without any parameters. Obviously you have #Html.ValidationSummary() in your View and Model has all required property attributes.
The data that was entered into the form was preserved and displayed again when the view was rendered with the validation summary.
My question: how is the data preserved? Since it was not passed to the View like
return View(response);
Thanks a lot.
Sincerely,
Vlad
It is because the values have been bound to the model state which is passed back along to the view. This is along the same issue/question on why you cannot change a model value and return the view again. Meaning, let's assume I have the following property on my Viewmodel
public string Name {get;set;}
Using the controller below, I cannot change the ViewModel property without also either clearing the ModelState or updating the value in the model state. Try it!
[HttpPost]
public ViewResult Create(MyModel response)
{
response.Name = response.Name + "Some Random String"
return View();
}
The name property will remain unchanged. Essentially, once the ModelBinding occurs, the values from your form (ViewModel) are bound to the model state, which is why you do not have to pass the model back to the view.
As a side note, I always pass the model back in my call to return View();, it just seems more correct and a little easier to read
I am using ASP.NET MVC3 with the razor view engine. I am also using a the Yahoo User Interface 2 (YUI2) simple editor.
My view has a view model called ProductEditViewModel. In this view model I have a property defined as:
public string LongDescription { get; set; }
In my view I would create the YUI2 simple editor from this input field. The field is defined in the view like:
<td>#Html.TextAreaFor(x => x.LongDescription, new { cols = "75", rows = "10" })<br>
#Html.ValidationMessageFor(x => x.LongDescription)
</td>
Here is a partial view of my Edit action method:
[Authorize]
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(ProductEditViewModel viewModel)
{
if (!ModelState.IsValid)
{
// Check if valid
}
// I added this as a test to see what is returned
string longDescription = viewModel.LongDescription;
// Mapping
Product product = new Product();
product.InjectFrom(viewModel);
// Update product in database
productService.Update(product);
return RedirectToRoute(Url.AdministrationProductIndex());
}
When I view the contents of the longDescription variable then it should contain the values from the editor. If I edit the contents in the editor then longDescription still only contains the original contents, not the updated contents. Why is this?
I suspect that somewhere in your POST action you have written something like this:
[Authorize]
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(ProductEditViewModel viewModel)
{
...
viewModel.LongDescription = "some new contents";
return View(viewModel);
}
If this is the case then you should make sure that you have cleared the value from the ModelState before modifying it because HTML helpers will always first use the value from model state and then from the model.
So everytime you intend to manually modify some property of your view model inside a POST action make sure you remove it from modelstate:
ModelState.Remove("LongDescription");
viewModel.LongDescription = "some new contents";
return View(viewModel);
Now when the view is displayed, HTML helpers that depend on the LongDescription property will pick the new value instead of using the one that was initially submitted by the user.