MVC/EPiServer model-binding

Vote:
 

I am rather confused on MVC model-binding and how it interacts with Episerver binding.

The situation is this: I have an episerver page which also has a form that the user will enter data into. I would like to save this user data for a later redisplay of the page.

This works:
--------------------
The episerver model:

   

public class MockPressReleasePage : SitePageData {

    /* whatever episerver properties are needed */

    /* the extra parameters */
        [Ignore]
        public string parm1 { get; set; }
        [Ignore]
        public string parm2 { get; set; }

    }



A model for the extra parameters:
   

public class MockPressReleaseParms {
        public  string parm1 { get; set; }
        public  string parm2 { get; set; }
    }




The controller:

   

public class MockPressReleasePageController : PageController
    {
        public ActionResult Index(MockPressReleasePage currentPage)
        {
            return View("Index", currentPage);
        }

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Submit(MockPressReleasePage currentPage, MockPressReleaseParms parms) {
            var cp = (MockPressReleasePage)currentPage.CreateWritableClone();
            cp.parm1 = parms.parm1;
            cp.parm2 = parms.parm2;
            return View("Index", cp);
        }
    }




The view:

@model MockPressReleasePage

@using (Html.BeginForm("Submit",null,FormMethod.Post)) {
    
  1. @Html.LabelFor(m => m.parm1) @Html.TextBoxFor(m => m.parm1)
  2. @Html.LabelFor(m => m.parm2) @Html.TextBoxFor(m => m.parm2)
}




The key point is that when the Submit action is called, the parms object is bound to the contents of the form textboxes.
-------------------------------------

Of course, this is not how I tried to do it at first. Rather than duplicating all the parameters of MockPressReleaseParms in the episerver model, it would make more sense just to add the whole object (MockPressReleaseParms).
To do this a few changes are needed:

The episerver model becomes:

   

public class MockPressReleasePage : SitePageData {

    /* whatever episerver properties are needed */

    /* the extra parameters */
        [Ignore]
        public MockPressReleaseParms allParms { get; set; }
    }




The controller's Submit action becomes:

       

public ActionResult Submit(MockPressReleasePage currentPage, MockPressReleaseParms parms) {
            var cp = (MockPressReleasePage)currentPage.CreateWritableClone();
            cp.allParms = parms;
            return View("Index", cp);
        }

And the View becomes:

      

 
  • @Html.LabelFor(m => m.allParms.parm1) @Html.TextBoxFor(m => m.allParms.parm1)
  • @Html.LabelFor(m => m.allParms.parm2) @Html.TextBoxFor(m => m.allParms.parm2)
  • ----------------------------------
    It seems like a trivial change, however now it doesn't work. The key point is that when Submit is called, the binding does not work: in currentPage the 'allParms' property is null, and in 'parms' both 'parm1' and 'parm2' are null.

    Some helpful people may ask what I'm trying to do and point out an easier/better way of doing it, and that is fine and welcomed. However what I'm really after is an understanding of how model-binding works so that I can answer this question (and future similar questions) myself.

    So why does the model-binding work in the first case and not in the second?

    #91239
    Sep 30, 2014 17:44
    Vote:
     

    In your 1st approach are "currentPage.parm1" and "currentPage.parm2" filled in as well from the incoming reuqest?

    If I'm not mistaken "currentPage" argument to your action is actually a content loaded from the repository. So if "allParams" is ignored initially it should not be filled at all then content is instantiated and passed to controller action.

    My personal preference is to have a viewmodel designed for that particular page/view. I would "duplicate" in your case parameters from page into viewmodel and not bind properties from page directly to view.

    #91251
    Sep 30, 2014 21:30
    Vote:
     

    Thank you for your reply Valdis.

    To answer your question, in the 1st approach 'currentPage.parm1' and 'currentPage.parm2' are NOT filled in.

    I really still don't understand why simply including a property in the page of the same type as the second argument, would result in NEITHER of them being bound.

    -----

    As to other methods, it certainly may be better to create a viewmodel but that doesn't solve what I'm trying to do. Basically, these parameters may be carried through an iter of several pages, registration->message->login->etc.->back to first page, and it would be much better to have the parameters contained in a separate object rather than having to duplicate and copy each of them in every page. Each page would have some of it's own properties (and may be a viewmodel) but would also need to store these parameters and pass them on.

    There are still other methods, of course; it has been suggested that I save them in a session variable for example. However I would still like to understand the binding process, and know why the second method doesn't bind!

    #91263
    Oct 01, 2014 9:37
    Vote:
     

    OK, got it. The name of the parameter in the action method is significant. Since (in the 2nd version) the Index used 

    TextBoxFor(x => m.allParms.parm1)


    which creates a control with

    id="allParms_parm1" name="allParms.parm1" 

    , and the binding only works if the parameter has the same name, so:

    public ActionResult Submit(MockPressReleasePage currentPage, MockPressReleaseParms allParms) 

    ----
    As usual, the problem had nothing to do with EpiServer. Since I'm learning both Episerver and Asp.Net MVC at the same time it's not yet clear to me who's doing what when.

    I assume it's frowned upon to mark your own reply as The Answer...seems like a cheap way to score points.

    #91293
    Oct 01, 2014 16:58
    Vote:
     

    I'm not here for the points :)

    Anyway, EPiServer will make sure that you will get correct content from repository to your controller's action. Most of the other stuff around controllers and how parameters are binded from incoming requested I guess is delegated to standard Mvc capabilities.

    #91294
    Oct 01, 2014 17:15
    This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
    * You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.