views:

306

answers:

3

OK, let's start with the Html.Textbox. It is supposed to contain text read from a file. The file read is based on what the user picks from a dropdown list.

The first time it is fine. The user picks a value from the dropdown list. The controller uses that value to read some text from a file, and returns that text to the view via the view model. Everything is fine.

THen the user picks another value from the dropdown list. The controller reads a new value from a file and returns it via the view model. Debugging to the LINE BEFORE THE HTML.TEXTBOX is set in the view shows that the model contains the correct value. However, the textbox itself still shows the PREVIOUS value when the page displays!

If I switch from Html.Textbox to a plain input, type="text" html control, everything works fine. That's not so hard, but the same thing happens with my dropdown list -- I can't set the selected value in code. It always reverts to whatever was chosen last. Rendering a "select" tag with a dynamically-generated option list is a pain. I would love to be able to use Html.Dropdown.

What am I missing here?? This is such a simple thing in webforms!

A: 

The biggest issue I see here is that you are trying to do a postback within MVC. That model is really not supported, and is actually way more trouble than it is worth (as it seems you are finding out). I would recommend using Ajax to update the contents of the dropdown dynamically.

ckramer
I disagree heartily. You can do anything in MVC that you can do with ordinary HTML, including postbacks. However, as I am finding out, you can't use the HTML helpers if you want to set the value of controls programmatically. But I have written almost an entire application without AJAX, and it's entirely possible to do it.
Cynthia
A POST is an HTTP thing, not specific to either of MVC or WebForms.AJAX is an extension to the browser to do POSTs (and other requests) asynchronously.The 'postback' you're referring to is probably WebForms postback with the ViewState being sent back and forth. MVC doesn't have this meaning you have to maintain state manually, whether it's going through a standard HTTP POST or an asynchronous one...i.e. I also disagree heartily.
jamiebarrow
+1  A: 

When you post a form, the values that are posted are put into ModelState. When the HtmlHelper renders an html iunput element, e.g. Html.TextBoxFor(x => x.FirstName), it'll search various locations to get the value for the textbox... ModelState is before ViewData.Model in the list of locations. So there for, the previously posted value will appear in your textbox.

To fix this you could clear the ModelState value or update the ModelState value. BUT I would kinda view that as a hacky way of getting around the problem.

The real issue has more to do with the flow of the posts and requests. I would personally look into that and maybe implement the PRG (Post Redirect Get) pattern.

HTHs,
Charles

Charlino
A: 

Following on from what Charles/Charlino said:

Model binding updates the ModelState object, which contains validation and model binding errors that are collected during model binding.

Inside an action method, model binding has occurred already to update the model, and generated the ModelState object. If you now update the value on the model inside the action, you must also manually update the model state (since the helpers use it to generate their HTML). Below is an example:

model.CaptchaIsValid = CaptchaService.ValidateAndExpireCaptcha(model.CaptchaAttempt);
if (!model.CaptchaIsValid)
{
    ModelState.AddModelError("CaptchaAttempt", "Incorrect - please try again");
}

// I'll clear the value on each attempt, to force them to re-enter a CAPTCHA.
model.CaptchaAttempt = string.Empty;

// Since I updated the model, I must create a new ValueProvider result...
ValueProviderResult clearedValue = new ValueProviderResult(
      model.CaptchaAttempt, 
      model.CaptchaAttempt, 
      CultureInfo.CurrentCulture);

// ... and update the ModelState's value.
ModelState.SetModelValue("CaptchaAttempt", clearedValue);
jamiebarrow