Strange Form behavior with Submit ActionResult - asp.net-mvc

Doesn't seem like this should work so I have no idea why it does. I LIKE the result, but worried I can't depend on it because I have no idea how it is working.
[HttpGet]
public ActionResult Modify(System.Guid id)
{
return View("Modify", LoadFromDatabase(id));
}
[HttpPost]
public ActionResult Modify(CaseModel myModel)
{
//So odd behavior. If I redirect to the actual GET Modify,
//I loose any changes on the form. however if I perform the
//same actions but here in the Post... all my unsaved changes
//stick..why?
//This one wipes any edits
return RedirectToAction("Modify", new { id = myModel.ID});
//This one actually leaves all my changes, even though
//I am re-creating the model from the database just like
//the other ActionResult
return View("Modify", LoadFromDatabase(myModel.ID));
}

As Stephen Muecke mentions in the two comments above,
You can depend on it. RedirectToAction() is redirecting to a new page which initializes a new instance of CaseModel based on ID property (i.e. reads the values from your repository. return View() is returning the current instance of CaseModel and if you using the HtmlHelper methods to generate form controls, it will use the values from ModelState which are added by the DefaultModelBinder when you submit the form. Refer this answer for more explanation of the behavior). #Html.TextBoxFor(m => m.someProperty) will display the value you posted, whereas #Model.someProperty will display the 'updated' value you set in the post method
works perfectly. The HTML helpers overwrite my Model values with the ModelState (aka ViewState).

Related

Clear All Fields on MVC4 Razor View on both: Loading and after Back to Page

What is the most effective method in order to make all the fields on an MVC4 Razor empty when loading it (for example first loading or after backing to the page again)? If it is possible could you please suggest me a way with the help of Razor properties instead of using Javascript/jQuery.
It's a little difficult to make out what you're trying to do, but let's see if I can help.
Firstly, if you simply wanted to clear out a form's values after it's been posted, you can do that like so:
[HttpPost]
public ActionResult Index(ViewModel model)
{
ModelState.Clear();
model = new ViewModel();
return View(model);
}
Simply creating a new ViewModel isn't enough, as the ModelState dictionary will try to repopulate the form with the old values. This does what you want, but isn't really leveraging MVC to do what you want.
The better way to do it would be to redirect back to the action you use to display your form. Something like this:
public ActionResult Create()
{
var model = new ViewModel();
return View(model);
}
This is simply passing in an empty model to your form. Once the user fills out the form, and it's posted back to the server, you can handle it like so:
[HttpPost]
public ActionResult Create(ViewModel model)
{
if (ModelState.IsValid)
{
// Form data is valid so redirect back to display the form again
return RedirectToAction("Create");
}
// If we get here, redisplay the form with the fields filled in
// and errors shown
return View(model);
}
Simply calling a ModelState.Clear() will do the trick. You shouldn't have to instantiate the view model again.
A view displays (or collect) information about a Model.
So, if you pass an Empty model (that is: properties in null or blank or default values) it will "clear all fields".
All you have to do is invoking again the Action that displays the view, now passing an empty model.
EDIT:
You can do it in javascript, but then you have to duplicate and maintain the logic of what are default values.

Html.Hidden field not getting set

I have a hidden field in my view like this:
using (Html.BeginForm("Action", "Schedule"))
{
#Html.Hidden("Id", Model.Schedule.Id)
...
}
And an action method that takes in the information like this:
public ActionResult AddEventToSchedule(Event NewEvent, Guid Id)
{
// Do something
}
I keep getting an empty Guid passed in, even when Model.Schedule.Id is not empty. I checked the html source and the hidden field is an empty Guid as well (used a breakpoint to verify that Model.Schedule.Id is not empty).
The strange thing is when I tried to access the Id value through the model like below, the html hidden field was populated correctly with the guid, but the model passed into the action method was empty.
public ActionResult AddEventToSchedule(Event NewEvent, ScheduleModel model)
{
// model.Schedule is null!
}
Figured this out with the help of this question:
MVC3 Model Binding - List to Hidden fields
Apparently HTML helpers check ModelState for a value before they check Model.
The reason why I only saw this behavior when I added the Id as a parameter to the action method was that this invoked the model binder to populate ModelState with the Id. And the reason why the Id was always an empty Guid was because that's the value the first time the action method is called.
I added this line to my action method and everything works fine now:
ModelState.Remove("Id")

Why is a value that I update in my model inside an MVC3 controller not rendered on the client?

I have a controller action UpdateCustomer(CustomerDto customer) that returns a PartialViewResult with a model that is also a CustomerDto:
[HttpPost]
public PartialViewResult UpdateCustomer(CustomerDto customer)
{
CustomerDto updatedCustomer = _customerService.UpdateCustomer(customer);
updatedCustomer.Name = "NotThePostedName";
return PartialView("CustomerData", updatedCustomer);
}
In my view, I have the following line:
#Html.TextBoxFor(model => model.Name)
So far, so good. In my view I do an asynchronous post to this action method, the model binder does its work and I can update a customer in the database. Then I want to render the updated customer to the client. For example, I'd like to change the customer name in my controller. However, what gets rendered is always the properties from the posted customer, not the properties from updatedCustomer.
I decided to include the MVC3 source code in my project to see what really happens. It appears to be a feature (bug?) of MVC3 that it always takes the value from ViewData.ModelState instead of the value from ViewData.Model.
This happens at lines 366-367 of System.Web.Mvc.Html.InputExtensions:
string attemptedValue =
(string) htmlHelper.GetModelStateValue(fullName, typeof(string));
tagBuilder.MergeAttribute("value",
attemptedValue ?? ((useViewData)
? htmlHelper.EvalString(fullName)
: valueParameter), isExplicitValue);
As you can see, attemptedValue comes from ModelState. It contains the old value for CustomerDto.Name (the value that was posted to the controller action).
If this is a feature, why does it work this way? And is there a way to work around it? I would expect that if I update my model, the update gets rendered, not the old value I posted.
Well yes it's a feature (ModelState is always checked before actual Model), you can clear the ModelState, or update just the value you need:
ModelState["Name"].Value = updatedCustomer.Name;

ASP.NET MVC Model contents disappears randomly

Update following initial comments
The model has an object in it called 'Account', of which there is an int propety (Account.AccountID)
ViewB has a form which collects some additional information - but also has a textbox which is populated with Model.Account.AccountID.
When I submit ViewB however, Model.Account becomes null.
Its probably easier to show a simplified version of what I have before I explain the issue:
[HttpGet]
public ActionResult ViewA()
{
return View(new BlahModel());
}
[HttpPost]
public ActionResult ViewA(BlahModel model)
{
if(there_was_a_problem)
return View("ViewA", model);
else
return View("ViewB", model);
}
// have tried both httppost, httpget and no attribute here
public ActionResult ViewB(BlahModel model)
{
return View(model);
}
I load up ViewA via a GET, fill in the strongly-typed form and submit - then the following View (either ViewA again or ViewB if the request had no problems) is fine ... it has full access to the whole model and can display the properties within it.
The problem is that if I then submit a form in ViewB (which posts to the ActionResult ViewB) - the model suddenly has null properties throughout, even though its using the same model - and prior to the post has picked up all the values sucessfully.
Any ideas?
Many thanks
Most likely - ViewB view does not render enough and model binder can't find values to bind.
Action argument is binded from form values. It does not matter if You pass model to view correctly. Rendering things out is what matters (or passing through query string/cookies).
Complementing Arnis L.'s answer, you can use a tool like Firebug to check that your model's parameters (or any parameters at all) are being sent along the request.

ASP.NET MVC Unbind Action Parameter

Is it possible to disable a certain action parameter from retaining its value across requests?
[HttpPost]
public ActionResult MyAction(string value1, string value2)
{
if(value1=="hi")
ModelState.AddModelError("value1", "Can't have hi");
//do stuff
if(ModelState.IsValid)
return RedirectToAction("Finish");
else
return View()
}
[HttpGet]
public ActionResult MyAction()
{
return View()
}
The view consists of a simple form with two input boxes (value1 and value2). Once submitted and validation fails, the view is returned. I want to always have the value of the textbox in the view to be empty.
The value for the textbox "value1" is retained if the the model is invalidated.
I tried to declare the textbox as <%= Html.TextBox("value1", null) %> but the value is still retained. I also tried to use [Bind(Exclude="value1")] but that dosen't work on a single variable.
Update 2:
I'm doing this for a textbox that is used for Captcha (custom solution) input. I want the textbox to be cleared any time the page is loaded, but I want validation to remain.
Try calling
ModelState["value1"].Value
= new ValueProviderResult(null, string.Empty, CultureInfo.InvariantCulture);
before you return the view from within your controller action.
What this does is keep all the errors associated with the key "value1", but replaces the value with an empty value.
What are you doing that's causing it to be retained? There isn't anything like ViewState in MVC that will persist a value over multiple requests unless you're writing code or using form fields to make it do so.
What does the view look like? Is this action method being called via GET or POST? What's the "do stuff" contained in your method?
Edit: You're still showing //do stuff in your example code. Does that stuff contain any references to ViewData? Your question is about binding, but I don't see any binding happening. Maybe this is beyond my understanding.
Edit 2: Glad Phil saw this one! The original question didn't mention the ModelState.

Resources