How to update the textbox value #Html.TextBoxFor(m => m.MvcGridModel.Rows[j].Id) - asp.net-mvc-3

I have problem, that the text box value doesn't get updated with the new value in the model.
#Html.TextBoxFor(m => m.MvcGridModel.Rows[j].Id)
First the collection MvcGridModel.Rows get populated with some data, then when press button and submit the form it get new data successfully, but it doesn't update the textbox's value.
Do you have any ideas?
Thank u in advance

That's because HTML helpers such as TextBoxFor first look in the ModelState when binding their values and only after that in the model. So if in your POST action you attempt to modify some value that was part of the initial POST request you will have to remove it from the ModelState as well if you want those changes to take effect in the view.
For example:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// we change the value that was initially posted
model.MvcGridModel.Rows[0].Id = 56;
// we must also remove it from the ModelState if
// we want this change to be reflected in the view
ModelState.Remove("MvcGridModel.Rows[0].Id");
return View(model);
}
This behavior is intentional and it is by design. This is what allows for example to have the following POST action:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// Notice how we are not passing any model at all to the view
return View();
}
and yet inside the view you get the values that the user initially entered in the input fields.
There's also the ModelState.Clear(); method that you could use to remove all keys from the modelstate but be careful because this also removes any associated modelstate errors, so it is recommended to remove only values from the ModelState that you intend to modify inside your POST controller action.
All this being said, in a properly designed application you should not need this. Because you should use the PRG pattern:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
// there was some error => redisplay the view without any modifications
// so that the user can fix his errors
return View(model);
}
// at this stage we know that the model is valid.
// We could now pass it to the DAL layer for processing.
...
// after the processing completes successfully we redirect to the GET action
// which in turn will fetch the modifications from the DAL layer and render
// the corresponding view with the updated values.
return RedirectToAction("Index");
}

Related

Retaining failed validation data

I've got a requirement to keep and display an incorrect entry on the view. It isn't possible to go to the next page without passing all validation.
For example, the use has to enter a date in text field in a certain format. Currently if the model binding fails it doesn't keep the invalid date text entry. I'd like to keep t and display it back to user with a vanadium failed message.
I'd like to know if this is achievable without creating a data holder type which temporarily hold the entered game and parsed value.
Yes, it is possible. Because you haven't posted any code, I'll just give you an example of how this can be achieved using server side validation.
Here is the [HttpGet] action method that serves up the form allowing the user to enter data.
[HttpGet]
public ActionResult Create()
{
// The CreateViewModel holds the properties and data annotations for the form
CreateViewModel model = new CreateViewModel();
return View(model);
}
Here is the [HttpPost] action method that receives and validates the form.
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if (!ModelState.IsValid)
{
return Create(); // This will return the form with the invalid data
}
// Data is valid, process the form and redirect to whichever action method you want
return RedirectToAction("Index");
}
You can also use return View(model); in the [HttpPost] action method instead of return Create();.

How to disable validation before calling UpdateModel in MVC3 Controller

I'm looking to enable "saving" of form data prior to submission.
I want users to be able to save form progress, even when the form is in an invalid state, and then come back to it at a later time.
Now, my only problem is that I want to be able to use UpdateModel method to update my model. However, as the form is potentially invalid or just partially complete, this will throw an error.
Is there a way to annotate the following method such that validation is ignored in the SAVE instance?
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
UpdateModel(model);
}
I want to save having to write a 1-1 mapping for the elements being saved - which is my fallback option, but isn't very maintainable.
Give TryUpdateModel(model) a try, should fit your needs.
This won't throw an exception, and it will update the model and then return false if there are validation errors.
If you care about the errors, you check the ModelState in the false instance.
Hence, you can use it as so to always save changes:
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
TryUpdateModel(model);
model.saveChanges();
}

Using modelstate.isvalid to validate data from inside the controller in MVC3

I am pretty new to ASP.NET MVC3 but i have about 4 years of experience with PHP frameworks.
I am trying to build an MVC3 web app, but i am having issues with validationg my model.
Here is a test controller to show you what i am trying without success to do.
I am trying to pass a value to my model inside the controller, but it doesnt take it into account the parameter.
I tried using modelstate.setmodelvalue, for junk.sentence, but it keeps the value from the POST request which is invalid an that i want to change by default (for test purposes) in the controller.
Can anyone help?
Thanks in advance.
Michael
[HttpPost]
public ActionResult Create(Junk junk)
{
//ModelState.Clear();
junk.sentence = "coucou";
ModelState.SetModelValue("sentence", new ValueProviderResult(junk.sentence, junk.number, null));
//ModelState
if (ModelState.IsValid)
{
db.Junks.Add(junk);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(junk);
}
//
// GET: /Junk/Edit/5
public ActionResult Edit(int id)
{
Junk junk = db.Junks.Find(id);
return View(junk);
}
Try removing it from modelstate:
[HttpPost]
public ActionResult Create(Junk junk)
{
junk.sentence = "coucou";
//ModelState
if (ModelState.IsValid)
{
db.Junks.Add(junk);
db.SaveChanges();
return RedirectToAction("Index");
}
ModelState.Remove("sentence");
return View(junk);
}
This assumes that in your view you have a corresponding input field that was generated using some of the Html helpers such as EditorFor for example:
#Html.EditorFor(x => x.sentence)
or:
#Html.TextBoxFor(x => x.sentence)
ModelState.IsValid returns false when there are model errors added to the model state. MVC validates the properties on your model and creates a list of errors for you in the ModelState. You must remove the errors from the model state that you wish to be ignored from inside your controller action. You can then update the actual values on the model. (Darin Dimitrov shows you an example of doing this)

Using ViewData to pass string from Controller to View in ASP.NET MVC3

I am trying to pass a random string from my Controller to the View.
Here is my Controller code:
[HttpPost]
public ActionResult DisplayForm(UserView user)
{
//some data processing over here
ViewData["choice"] = "Apple";
return RedirectToAction("Next", "Account");
}
Now I want to pass that data value "Apple" to my view Next.cshtml which is created as follows:
//View: Next.cshtml
#{
ViewBag.Title = "Thanks for registering";
Layout = "~/Content/orangeflower/_layout.cshtml";
}
<p>Your favorite fruit is:</p>#ViewData["choice"]
But I am not able to see my data in the browser when the project runs.
Here is the snapshot:
1) On debug, the controller showing the value:
2) The browser view doesn't show the value "Apple"
3) On further debug to my Next.cshtml View:
Why is the value not getting passed to the View correctly. Both my controllers for Next and DisplayForm are within the same Controller AccountController.cs , still value not getting displayed.
Can someone help me solve this ?
You are not rendering a view, you are redirecting. If you wanted to pass some information tyo the view you need to return this view after adding it to ViewData:
[HttpPost]
public ActionResult DisplayForm(UserView user)
{
//some data processing over here
ViewData["choice"] = "Apple";
return View();
}
If you want to pass a message that will survive after a redirect you could use TempData instead of ViewData.
[HttpPost]
public ActionResult DisplayForm(UserView user)
{
//some data processing over here
TempData["choice"] = "Apple";
return RedirectToAction("Next", "Account");
}
then inside the Next action you could fetch the data from TempData and store it inside ViewData so that the view can read it.
You are performing a post - redirect - get. The ViewData is being set for this request, which returns a redirect, clearing the ViewData, then another request happens which does not have the data. Use TempData instead and it will be added to the ViewData automatically on the next request.

Persisting model data on form post when redisplaying the view for an invalid ModelState

Simplified example:
[HttpGet]
public ActionResult Report(DateTime? date)
{
if (!date.HasValue)
{
date = DateTime.Now;
}
// Clear the ModelState so that the date is displayed in the correct format as specified by the DisplayFormat attribute on the model
ModelState.Clear();
// Expensive call to database to retrieve the report data
ReportModel model = DataAdapter.Convert(this.reportService.GetReport((DateTime)date));
// Store in TempData in case validation fails on HttpPost
TempData["ReportModel"] = model;
return View(model);
}
[HttpPost]
public ActionResult Report(ReportModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Report", new { date = model.Date });
}
else
{
// Load the report from TempData, and restore again in case of another validation failure
model = TempData["ReportModel"] as ReportModel;
TempData["ReportModel"] = model;
// Redisplay the view, the user inputted value for date will be loaded from ModelState
return View(model);
}
}
My question is, am I going about this the right way by storing the report data in TempData? The code seems a little strange especially reading from and then writing back to TempData in the HttpPost action method to ensure it persists the next request.
Other options I can think of are:
(1) Make another call to the service layer from the HttpPost action (I'd rather not make another database call because of a validation failure just to redisplay the form as it seems inefficient). I guess I could implement caching at the service layer to avoid the database round trip...
(2) Use hidden inputs in the form (yuk!).
(3) Store the most recently viewed report in session permanently.
How is everyone else doing this sort of thing? What's the recommended practice?
My question is, am I going about this the right way by storing the report data in TempData?
No, absolutely not. Store something into TempData if and only if you redirect immediately afterwards as TempData survivces only a single redirect. If you store something into TempData in your GET action and then render a view, an AJAX request for example from this view would kill the TempData and you won't get the values back on your POST request.
The correct pattern is the following (no TempData whatsoever):
public ActionResult Report(DateTime? date)
{
if (!date.HasValue)
{
date = DateTime.Now;
}
// Clear the ModelState so that the date is displayed in the correct format as specified by the DisplayFormat attribute on the model
ModelState.Clear();
// Expensive call to database to retrieve the report data
ReportModel model = DataAdapter.Convert(this.reportService.GetReport((DateTime)date));
return View(model);
}
[HttpPost]
public ActionResult Report(ReportModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Report", new { date = model.Date });
}
else
{
// Redisplay the view, the user inputted value for date will be loaded from ModelState
// TODO: query the database/cache to refetch any fields that weren't present
// in the form and that you need when redisplaying the view
return View(model);
}
}
(1) Make another call to the service layer from the HttpPost action
(I'd rather not make another database call because of a validation
failure just to redisplay the form as it seems inefficient). I guess I
could implement caching at the service layer to avoid the database
round trip...
That's exactly what you should do. And if you have problems with optimizing those queries or concerns of hitting your or something on each POST request cache those results. Databases are hyper optimized nowadays and are designed to do exactly this (don't abuse of course, define your indexes on proper columns and performance should be good). But of course caching is the best way to avoid hitting the database if you have a very demanding web site with lots of requests and users.
(2) Use hidden inputs in the form (yuk!).
Yuk, I agree but could work in situations where you don't have lots of them.
(3) Store the most recently viewed report in session permanently.
No, avoid Session. Session is the enemy of scalable and stateless applications.

Resources