简体   繁体   中英

ASP.Net MVC Postback and Models

This is mostly a follow-up to a comment in this issu, but I don't have enough reputation to comment ...

ASP.Net MVC Postback a label value to your controller

Let's say I have a simple model:

public class SimpleClass
{
    public String Label { get; set; }
    public String FirstName { get; set; }
}

Label is changed based on user/client so it can't be a DataAttribute. If when posted back processing problems occur, we need to redraw the entire page. This is the crux of the problem of the previous post. The accepted solution is to do this:

@Html.DisplayTextFor(model => model.Label)
@Html.HiddenFor(model => model.Label)
@Html.EditorFor(model => model.FirstName)

That makes sense in that it works. But our models are much more complicated and extensive. This method will result in a ton of hidden fields which seems like a very dirty solution.

This brings me to JP's comment:

ASP.Net MVC Postback a label value to your controller

The solution there is to reload the model. But it's not just a reload, it's also a merge since you want to preserve any client-side data changes.

default: SimpleClass { Label="TheLabel", FirstName="Rob"}
postedback: SimpleClass { Label="", FirstName="Steve" }
we want: SimpleClass { Label="TheLabel", "FirstName="Steve" }

My question is does MVC have a good way to know what fields were postedback so it merges correctly? We would need to only merge postedback fields not blank properties.

Or is it better to just ajaxify the entire postback and not do a form submit? This avoids all model reload issues on submit.

Update

To give Pablo credit I accepted his solution. To see my simple example of his solution, check Robert Harvey's comment in the Answers below:

ASP.Net MVC Postback and Models

The main problem here is in trying to fit WebForms' PostBack concepts into MVC. There is no such thing as a stateful postback where things just automatically retain their state.

You only have ViewModels that are bound to the view, and ViewModels that are posted by the view to the Controller. They don't even necessarily need to be of the same Type. Meaning, the controller should only receive the data that the user indeed can change, not large objects with many properties that were part of the initial ViewModel but are read-only.

Labels commonly represent read-only texts and they are not editable form elements. Which is why you have to use hidden fields for that.

And yes, sometimes that implies that you have to reload the original data in the controller, and sync up with new data that you posted, which isn't necessarily a bad thing. If you bind read-only data to a view, which the user can't manually edit, you shouldn't really trust that data coming back in a post afterwards. Just because your html might try to make it read-only doesn't mean I can't manipulate the post and ultimately change your "read-only" data without you knowing.

I just read the second question you mentioned, and from the looks of it, his main problem was that he was trying to reuse the same ViewModel again, so all the data was missing and the model wasn't valid. The solution to that is indeed quite simple, ONLY post what you need, as a new ViewModel type, and have the controller take care of the rest.

[Moved from OP]

I think this is what Pablo is suggesting for those who are wondering. It seems to be a good pattern to resolve this problem.

Models:

    public class SimpleClass : SimpleClassPostBack
    {
        public String Label { get; set; }

        public SimpleClass()
        {
            // simulate default loading
            Label = "My Label";
            FirstName = "Rob";
        }

    }

    // contains only editable by the user fields
    public class SimpleClassPostBack
    {
        public String FirstName { get; set; }
    }

Controller Actions:

    [HttpGet]
    public ActionResult SimpleClassExample3()
    {
        SimpleClass simpleClass = new SimpleClass();
        return View(simpleClass);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SimpleClassExample3(SimpleClassPostBack postBackSimpleClass)
    {
        Boolean errorOccurred = true;

        if (!errorOccurred)
        {
            // do whatever success action is necessary
        }

        // redraw the page, an error occurred

        // reload the original model
        SimpleClass simpleClass = new SimpleClass();

        // move the posted back data into the model
        // can use fancy reflection to automate this
        simpleClass.FirstName = postBackSimpleClass.FirstName;

        // bind the view
        return View(simpleClass);
    }

View:

@model SimpleClass

@{
    ViewBag.Title = "Simple Class Example3";
}

<h2>Simple Class Example3</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <label for="FirstName">@Html.DisplayFor(m => m.Label)</label>
    @Html.EditorFor(m => m.FirstName)

    <br/>

    <button>Submit</button>

}

You should only send data from the client to the server that the server can't "figure out" on its own. If the server knows what the labels were when the user first navigated to that view then if the user cannot modify them, the server will be able to know what the labels are when reloading the view.

Use hidden fields to identify the database objects. So your SimpleClass should probably have some sort of Id which you will use in the hidden input. Use the EditorFor for FirstName . Now when the form is posted, use the sent Id to find the correct SimpleClass from the database and modify its FirstName property with the value posted. The Label property will be null which is ok since you don't need to save it. Now if there's a problem in the post and you want to send the same view back like it was, you need to repopulate the Label the same way you did when the user arrived to the view for the first time. The values of Id and FirstName properties will be automatically sent back to the view with the model state.

In summary:

  • Only post data that is needed to identify something and what the user can edit in that view.
  • Don't trust the client to send you anything valid. The user can change the values of the hidden field labels to anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM