Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC3, Razor view, EditorFor, query string value overrides model value

I have a controller action that takes a DateTime? via the query string as part of a post-redirect-get. The controller looks like e.g.

public class HomeController : Controller
{
    [HttpGet]
    public ActionResult Index(DateTime? date)
    {
        IndexModel model = new IndexModel();

        if (date.HasValue)
        {
            model.Date = (DateTime)date;
        }
        else
        {
            model.Date = DateTime.Now;
        }

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(IndexModel model)
    {
        if (ModelState.IsValid)
        {
            return RedirectToAction("Index", new { date = model.Date.ToString("yyyy-MM-dd hh:mm:ss") });
        }
        else
        {
            return View(model);
        }
    }
}

My model is:

public class IndexModel
{
    [DataType(DataType.Date)]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd MMM yyyy}")]
    public DateTime Date { get; set; }
}

And the Razor view is:

@model Mvc3Playground.Models.Home.IndexModel

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m.Date);
    <input type="submit" />
}

My problem is two fold:

(1) The date formatting applied on the model using the [DisplayFormat] attribute does not work if the query string contains a value for date.

(2) The value held in the model appears to be overwritten with whatever the query string value contains. E.g. if I set a break point inside my Index GET action method, and manually set the date equal to today say, if the query string contains e.g. ?date=1/1/1, then "1/1/1" is displayed in the textbox (the plan is to validate the date and default it if the query string one isn't valid).

Any ideas?

like image 260
magritte Avatar asked Dec 09 '22 05:12

magritte


1 Answers

Html helpers first use ModelState when binding so if you ever intend to modify some value which is present in the model state inside a controller action make sure that you remove it from the ModelState first:

[HttpGet]
public ActionResult Index(DateTime? date)
{
    IndexModel model = new IndexModel();

    if (date.HasValue)
    {
        // Remove the date variable present in the modelstate which has a wrong format
        // and which will be used by the html helpers such as TextBoxFor
        ModelState.Remove("date");
        model.Date = (DateTime)date;
    }
    else
    {
        model.Date = DateTime.Now;
    }

    return View(model);
}

I must agree that this behavior is not very intuitive but it is by design so people should really get used to it.

Here's what happens:

  • When you request /Home/Index there is nothing inside ModelState so the Html.EditorFor(x => x.Date) helper uses the value of your view model (which you have set to DateTime.Now) and of course it applies proper formatting
  • When you request /Home/Index?date=1/1/1, the Html.EditorFor(x => x.Date) helper detects that there is a date variable inside ModelState equal to 1/1/1 and it uses this value, completely ignoring the value stored inside your view model (which is pretty much the same in terms of DateTime value but no formatting is applied of course).
like image 106
Darin Dimitrov Avatar answered Jun 07 '23 03:06

Darin Dimitrov