0

Hello I am not able to retain values on multiple posts and “Post1” action function in my in my controller always has MyViewModelObj.Field2 as null .I expect it to retain the old value in the 2nd post How to I make the MyViewModel model class object persist the values ?

Mymodels.cs ( Model)

namespace RetainTest.Models
{

    public class MyViewModel
    {

        public string Field1 { get; set; }
        public string Field2 { get; set; }
        public string Field3 { get; set; }
        public string Field4 { get; set; }
    }
}

RetainView.cshtml ( View )

@model RetainTest.Models.MyViewModel

@{
    ViewBag.Title = "RetainView";
}

<h2>RetainView</h2>


@using (Html.BeginForm("Post1", "Retain", FormMethod.Post,
                             new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    @Html.HiddenFor(model => model.Field1);
    @Html.HiddenFor(model => model.Field2);
    @Html.HiddenFor(model => model.Field3);
    @Html.HiddenFor(model => model.Field4);

    <div class="form-horizontal">
        <h4>MyViewModel</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Field1, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Field1, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Field1, "", new { @class = "text-danger" })
            </div>
        </div>

        @{ 
            if (  Model.Field2 == "Val2")
            { 

                <div class="form-group">
                    @Html.LabelFor(model => model.Field2, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Field2, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Field2, "", new { @class = "text-danger" })
                    </div>
                </div>

            }
        }
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

RetainController.cs ( Controller )

namespace RetainTest.Models
{
    public class RetainController : Controller
    {
        // GET: Retain
        public ActionResult Index()
        {
            MyViewModel MyViewModelObj = new MyViewModel();

            MyViewModelObj.Field1 = "Val1";
            return View("RetainView", MyViewModelObj);
        }
        [HttpPost]
        public ActionResult Post1(MyViewModel MyViewModelObj)
        {
            if (string.IsNullOrEmpty(MyViewModelObj.Field2 ))
            {
                MyViewModelObj.Field2 = "Val2";
            }
            return View("RetainView", MyViewModelObj);
        }

    }
}
AnnaDaKhokha
  • 59
  • 1
  • 1
  • 5
  • Your view has a hidden input for each property of your model. The `DefaultModelBinder` reads the first name/value pair for each property in the request and ignores all others. Since the hidden inputs are first, the values of your `EditorFor()` methods are ignored. You only ever bind the initial vales of your model, not the edited values (making your form rather pointless). Remove the hidden inputs! –  Oct 04 '16 at 22:15
  • And `MyViewModelObj.Field2 = "Val2";` will do nothing (refer the 2nd part of [this answer](http://stackoverflow.com/questions/26654862/textboxfor-displaying-initial-value-not-the-value-updated-from-code/26664111#26664111) for an explanation). If you want to display a view with different values, then follow the PRG pattern (or use `ModelState.Clear();`) –  Oct 04 '16 at 22:17
  • Thanks @StephenMuecke , removing these three statements fixed it. @Html.HiddenFor(model => model.Field1); @Html.HiddenFor(model => model.Field2); @Html.HiddenFor(model => model.Field3); @Html.HiddenFor(model => model.Field4); I though the hidden value is for one post , i realize it retains it for ever – AnnaDaKhokha Nov 10 '16 at 09:33

2 Answers2

0

Try this:

namespace RetainTest.Models
{

    public class MyViewModel
    {
        [Required(ErrorMessage = "Field1 is required")]
        public string Field1 { get; set; }

        [Required(ErrorMessage = "Field2 is required")]
        public string Field2 { get; set; }

        public string Field3 { get; set; }

        public string Field4 { get; set; }
    }
}

@model RetainTest.Models.MyViewModel

@{
    ViewBag.Title = "RetainView";
}

<h2>RetainView</h2> 
@using (Html.BeginForm("Post1", "Retain", FormMethod.Post, new { id = "form", enctype = "multipart/form-data"}))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.Field1);
@Html.HiddenFor(model => model.Field2);
@Html.HiddenFor(model => model.Field3);
@Html.HiddenFor(model => model.Field4);

<div class="form-horizontal">
    <h4>MyViewModel</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    <div class="form-group">
        <Label for="Field1" class="control-label col-md-2">Field1 </label>
        <div class="col-md-10">
            @if(Model.Field1 == "Val1")
            {
                <input type="text" name="Field1" id="Field1" class="form-control" value="@Model.Field1">
            }
            else 
            {
                <input type="text" name="Field1" id="Field1" class="form-control">
            }
            <span class="field-validation-valid text-danger" 
                    data-valmsg-for="Field1" 
                    data-valmsg-replace="true">
            </span>
        </div>
    </div> 
    <div class="form-group">
        <Label for="Field2" class="control-label col-md-2">Field2 </label>
        <div class="col-md-10">
            @if(Model.Field2 == "Val2")
            {
                <input type="text" name="Field2" id="Field2" class="form-control" value="@Model.Field2">
            }
            else 
            {
                <input type="text" name="Field2" id="Field2" class="form-control">
            }
            <span class="field-validation-valid text-danger" 
                data-valmsg-for="Field2" 
                data-valmsg-replace="true">
            </span>
        </div>
    </div>              
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
     @Scripts.Render("~/bundles/jqueryval")
}


namespace RetainTest.Models
{
    public class RetainController : Controller
    {
        private MyViewModel MyViewModelObj = new MyViewModel();

        // GET
        public ActionResult Index()
        {       
            if (Session["MyObj"] != null)               
                MyViewModelObj = (MyViewModel)Session["MyObj"];             
            else 
                MyViewModelObj.Field1 = "Val1";             

            return View("RetainView", this);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateInput(false)]
        public ActionResult Post1(FormCollection form)
        {
            MyViewModelObj.Field1 = form.Get("Field1");
            MyViewModelObj.Field2 = form.Get("Field2");

            if (string.IsNullOrEmpty(MyViewModelObj.Field2))                
                MyViewModelObj.Field2 = "Val2";             

            // here you need to store the data somewhere! 
            // session, database.
            // just an example: 
            Session.Add("MyObj", MyViewModelObj);

            return View("RetainView", this);
        }
    }
}

I used the session to retain the values of the object but there are a few other ways to store the data. The above code will post the user input to the controller and retain he values in the session.

V.T.
  • 19
  • 6
0

Got answer from Stephen Muecke

"Your view has a hidden input for each property of your model. The DefaultModelBinder reads the first name/value pair for each property in the request and ignores all others. Since the hidden inputs are first, the values of your EditorFor() methods are ignored. You only ever bind the initial vales of your model, not the edited values (making your form rather pointless). Remove the hidden inputs!"

AnnaDaKhokha
  • 59
  • 1
  • 1
  • 5