locked
Not able to get Entire View Model Data in Controller RRS feed

  • Question

  • User1360833881 posted

    Hi All,

    I can easily able to bind 2 models into a single view model and able to bind that view model to a view.

    One model contains details of customer and another is a list of his family details (in a grid)

    But When I post the form, I can able to get only first section details and not the family details.

    my code is show below

    View Model Code:

    public class CustomerDetails
    {
    public string NID { get; set; } 
    public string CustomerName { get; set; } 
    }
    
    public class FamilyDetails
    {
    public string Name { get; set; }
    public string Relation { get; set; } 
    }
    
    
    public class CommonViewModel
    {
    public CustomerDetails CustomerDetailsVM { get; set; }
    public List<FamilyDetails> FamilyDetailsVM { get; set; }
    } 

    View Code:

    @{
        ViewBag.Title = "Test";
        Layout = "~/Views/Shared/_Layout.cshtml";
    }
    
    @model REDF_DataContracts.CommonViewModel
    <h3>Print Details</h3>
    
    <body>
         <div class="row">
            <div class="col-sm-12 col-lg-10">
                <div>
                    <table style="width: 100%; margin-top: -33px;">
                        <tbody>
                            
                            </tbody>
                        </table>
                    @using (Html.BeginForm("PrintOffPlanContractList", "PrintOffPlanContract"))
                    {
                    <table style="width: 100%;">
                        <tbody>
                             <tr>
                                <td>
                                     @Html.TextBoxFor(model => model.CustomerDetailsVM.CustomerName, new { @class= "form-control",})
                                </td>
                                <td>
                                    <label for="name" class="pull-right">CustomerName</label></td>
                                 <td>
                                      @Html.TextBoxFor(model => model.CustomerDetailsVM.NID, new {@class= "form-control"})
                                </td>
                                <td>
                                    <label for="name" class="pull-right">NID</label></td>
                            </tr>
                             </tbody>
                        </table>
                        
                        <!--Family Details-->
                        if (Model.FamilyDetailsVM.Count > 0)
                {
            <table class="table table-bordered table-sm">
                <tr>
                    <th class="danger text-primary" style="text-align:center">Name</th>
                    <th class="danger text-primary" style="text-align:center">Relation</th>
                    
                </tr>
                @foreach (var item in Model.FamilyDetailsVM)
                {
                <tr>
                    <td class="active">@Html.DisplayFor(modelitem => item.Name)</td>
                    <td class="active" >@Html.DisplayFor(modelitem => item.Relation)</td>
                </tr>
                }
            </table>
                         <!--Family Details-->
                }
                else
                {
                    <b>No Details Found.</b>
                }
                 <!--Family Details-->
                
                           <table style="width: 100%;">
                        <tbody>
                            <tr>
                                <td style="width: 22%;">
                                    <p style="font-size:medium;" class="pull-right">
                                     <input type="submit" value="Print Off Plan Contract" class="btn btn-info btnStyle" style="font-size:medium" />
                    </p>
                                </td>
                            </tr>
                            </tbody>
                        </table>
                              }
                    </div>
                </div>
             </div>                                     
    </body>

    Controller Code:

     public ActionResult PrintOffPlanContractDetails(string legalID, string quotationID)
            {
               
                    var objApplicationInputBL = new REDF_BL();
                    CommonViewModel commonViewModel = objApplicationInputBL.GetCommonViewModel(legalID, quotationID);
                    CommonViewModel model = new CommonViewModel();
                    model.CustomerDetailsVM = commonViewModel.CustomerDetailsVM;
                    model.FamilyDetailsVM = commonViewModel.FamilyDetailsVM;
                    return View(model);
                
               
            }
     [HttpPost]
     public ActionResult PrintOffPlanContractList(CommonViewModel ViewModelParameter)
            {
                try
                {
                    if (ModelState.IsValid)
                    {
                        var objApplicationInputBL = new REDF_BL();
                        objFinanceClient.ClientCredentials.UserName.UserName = Common_Methods.RedfName;
                        objFinanceClient.ClientCredentials.UserName.Password = Common_Methods.Password;
                        Credential objCredentials = new Credential()
                        {
                            UserName = Common_Methods.UserName,
                            Password = Common_Methods.Password
                        };
                        List<FamilyPercent> objFamilyPercentList = new List<FamilyPercent>();
                        FamilyPercent objFamilyPercent = null;
                        foreach (var value in ViewModelParameter.FamilyDetailsVM)
                        {
                            objFamilyPercent = new FamilyPercent();
                            objFamilyPercent.Name = value.Name;
                            objFamilyPercent.Relation= value.Relation;
                            objFamilyPercentList.Add(objFamilyPercent);
                        }
                        
    OffPlanContract objOffPlanContract = new OffPlanContract()
                        {
                            Name = ViewModelParameter.CommonViewModel.Name,
                            NID = ViewModelParameter.CommonViewModel.NID,
                            FamilyPercent = objFamilyPercentList.ToArray(),
                        };
                        var responsePrintOffPlanContract = objFinanceClient.printOffPlanContract(objCredentials, objOffPlanContract);
                    return View("PrintContractDetails", ViewModelParameter);
    
                }
                catch (Exception ex)
                {
                    objWriteLogs.WriteError(ex);
                    throw ex;
                }
            }
    
        }

    Note : Even when I write 

    @using (Html.BeginForm("PrintOffPlanContractList", "PrintOffPlanContract", FormMethod.Post, new { ViewModelParameter = Model }))

    its not working. I mean in 'ViewModelParameter', only CustomerDetailsVM is coming, where FamilyDetailsVM is coming as null.  Sorry for the long code. Any help would be highly appreciated. 

    Thursday, July 16, 2020 11:26 PM

Answers

  • User1686398519 posted

    Hi alibasha202@gmail.com,

    • In the controller, the value passed by the page is received according to the name.
      • When using @Html.DisplayFor, the output html does not have a name attribute, so you can use @Html.HiddenFor to generate a html tag with a name.
    • You should use a for loop instead of a foreach loop.
      • When using a foreach loop, it creates the same input again and again because it does not know that they should be different.So you can use for loop and indexes to differ input.

    The two problems mentioned above cause the value of FamilyDetailsVM to be null. Just modify your page code as follows.

    @for (int i = 0; i < Model.FamilyDetailsVM.Count(); i++)
         {
            @Html.HiddenFor(model => model.FamilyDetailsVM[i].Name)
            @Html.HiddenFor(model => model.FamilyDetailsVM[i].Relation)
            <tr>
                <td class="active">@Html.DisplayFor(model => model.FamilyDetailsVM[i].Name)</td>
                <td class="active">@Html.DisplayFor(model => model.FamilyDetailsVM[i].Relation)</td>
           </tr>
    }

    Here is the result.

    Best regards,

    Yihui Sun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, July 17, 2020 6:05 AM
  • User348806598 posted

    Hi,

    In your case, CustomerModels should be getting submitted properly. But List is not getting submitted. In your case, you have displayed the content of the list, but these are not getting submitted while posting. To get complex data getting submitted, you need to have submittable controls with a proper name. You can change the loop to get it submitted-

                @for (var i = 0; i < Model.FamilyDetailsVM!.Count; i++)
                {
                    <tr>
                        <td class="active">@Html.DisplayFor(modelitem => modelitem.FamilyDetailsVM[i].Name)</td>
                        <td class="active">
                            @Html.DisplayFor(modelitem => modelitem.FamilyDetailsVM[i].Relation)
                            @Html.HiddenFor(modelitem => modelitem.FamilyDetailsVM[i].Name)
                            @Html.HiddenFor(modelitem => modelitem.FamilyDetailsVM[i].Relation)
                        </td>
                    </tr>
                }

    This will generate the HTML as-

            <table class="table table-bordered table-sm">
                <tr>
                    <th class="danger text-primary" style="text-align:center">Name</th>
                    <th class="danger text-primary" style="text-align:center">Relation</th>
    
                </tr>
                    <tr>
                        <td class="active">Fname</td>
                        <td class="active">
                            rel
                        <input id="FamilyDetailsVM_0__Name" name="FamilyDetailsVM[0].Name" type="hidden" value="Fname" />
                        <input id="FamilyDetailsVM_0__Relation" name="FamilyDetailsVM[0].Relation" type="hidden" value="rel" />
                    </td>
                </tr>
                    <tr>
                        <td class="active">Fname2</td>
                        <td class="active">
                            rel2
                        <input id="FamilyDetailsVM_1__Name" name="FamilyDetailsVM[1].Name" type="hidden" value="Fname2" />
                        <input id="FamilyDetailsVM_1__Relation" name="FamilyDetailsVM[1].Relation" type="hidden" value="rel2" />
                    </td>
                </tr>
            </table>

    There is an explanation of the same in the below post-

    http://growingtech.blogspot.com/2012/03/posting-and-binding-generic-list-or.html

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, July 17, 2020 8:15 AM

All replies

  • User-474980206 posted

    The model is not posted back, the form fields (input, select and textarea) values which are string values are posted back. The binder updates an actions  parameters with these values.

    You are only posting a couple of form fields. If you set a url parameter to an object,

       new { ViewModelParameter = Model }))

    When Model is converted to a string value, the you just get the class name.

    While it’s poor design, you could put all model properties into individual hidden fields. But a better approach would be a post back model with the input values and the two keys In hidden fields. Then just read the data again and do the update logic.

    Friday, July 17, 2020 3:46 AM
  • User1686398519 posted

    Hi alibasha202@gmail.com,

    • In the controller, the value passed by the page is received according to the name.
      • When using @Html.DisplayFor, the output html does not have a name attribute, so you can use @Html.HiddenFor to generate a html tag with a name.
    • You should use a for loop instead of a foreach loop.
      • When using a foreach loop, it creates the same input again and again because it does not know that they should be different.So you can use for loop and indexes to differ input.

    The two problems mentioned above cause the value of FamilyDetailsVM to be null. Just modify your page code as follows.

    @for (int i = 0; i < Model.FamilyDetailsVM.Count(); i++)
         {
            @Html.HiddenFor(model => model.FamilyDetailsVM[i].Name)
            @Html.HiddenFor(model => model.FamilyDetailsVM[i].Relation)
            <tr>
                <td class="active">@Html.DisplayFor(model => model.FamilyDetailsVM[i].Name)</td>
                <td class="active">@Html.DisplayFor(model => model.FamilyDetailsVM[i].Relation)</td>
           </tr>
    }

    Here is the result.

    Best regards,

    Yihui Sun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, July 17, 2020 6:05 AM
  • User348806598 posted

    Hi,

    In your case, CustomerModels should be getting submitted properly. But List is not getting submitted. In your case, you have displayed the content of the list, but these are not getting submitted while posting. To get complex data getting submitted, you need to have submittable controls with a proper name. You can change the loop to get it submitted-

                @for (var i = 0; i < Model.FamilyDetailsVM!.Count; i++)
                {
                    <tr>
                        <td class="active">@Html.DisplayFor(modelitem => modelitem.FamilyDetailsVM[i].Name)</td>
                        <td class="active">
                            @Html.DisplayFor(modelitem => modelitem.FamilyDetailsVM[i].Relation)
                            @Html.HiddenFor(modelitem => modelitem.FamilyDetailsVM[i].Name)
                            @Html.HiddenFor(modelitem => modelitem.FamilyDetailsVM[i].Relation)
                        </td>
                    </tr>
                }

    This will generate the HTML as-

            <table class="table table-bordered table-sm">
                <tr>
                    <th class="danger text-primary" style="text-align:center">Name</th>
                    <th class="danger text-primary" style="text-align:center">Relation</th>
    
                </tr>
                    <tr>
                        <td class="active">Fname</td>
                        <td class="active">
                            rel
                        <input id="FamilyDetailsVM_0__Name" name="FamilyDetailsVM[0].Name" type="hidden" value="Fname" />
                        <input id="FamilyDetailsVM_0__Relation" name="FamilyDetailsVM[0].Relation" type="hidden" value="rel" />
                    </td>
                </tr>
                    <tr>
                        <td class="active">Fname2</td>
                        <td class="active">
                            rel2
                        <input id="FamilyDetailsVM_1__Name" name="FamilyDetailsVM[1].Name" type="hidden" value="Fname2" />
                        <input id="FamilyDetailsVM_1__Relation" name="FamilyDetailsVM[1].Relation" type="hidden" value="rel2" />
                    </td>
                </tr>
            </table>

    There is an explanation of the same in the below post-

    http://growingtech.blogspot.com/2012/03/posting-and-binding-generic-list-or.html

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, July 17, 2020 8:15 AM
  • User1360833881 posted

    Dear YihuiSun,

    Many Many Thanks for your kind help and excellent explanation. I have not worked much on MVC, now I am getting interested. I was able to resolve this issue with the help of your explanation. Thanks again.

    Saturday, July 18, 2020 11:14 AM