1

I'm going round in circles now so if anyone can identify the issue here I would greatly appreciate it.

I have a partial I'm using to list out items and that works fine. The post back to the controller works if the items passed back is just set as an ienumerable of the items but I need to pass back a model as it contains more information than just the list.

On doing this the list is empty each time and I cannot see why.

The partial:

@model RequestModel

@section Scripts {
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<style>
    .btn span.glyphicon {
        opacity: 0;
    }

    .btn.active span.glyphicon {
        opacity: 1;
    }

</style>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.min.js"></script>
}
@for (var i = 0; i < Model.Requests.Count(); i++)
{
    <div @(Model.Requests.Count == 3 ? "class=col-md-4" : Model.Requests.Count() == 2 ? "class=col-md-6" : "class=col-md-12")>
        @Html.HiddenFor(m => Model.Requests[i].RequestID)
        <table class="table table-responsive img-rounded">
            <thead>
                <tr class="alert-cascade">
                    <th colspan="2">
                        <div class="btn-group btn-group-sm pull-right" role="group" data-toggle="buttons">
                            <button class="btn btn-success" data-toggle="tooltip" title="Accept" id="acceptradio">
                                @Html.RadioButtonFor(m => Model.Requests[i].AcceptChecked, Model.Requests[i].AcceptChecked, new { @id = Model.Requests[i].RequestID, @style = "display:none" })
                                <span>Accept </span>
                                <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
                            </button>
                            <button class="btn btn-warning" data-toggle="tooltip" title="Reject" id="rejectradio">
                                @Html.RadioButtonFor(m => Model.Requests[i].RejectChecked, Model.Requests[i].RejectChecked, new { @id = Model.Requests[i].RequestID, @style = "display:none" })
                                <span>Reject </span>
                                <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
                            </button>
                        </div>
                        @Model.Requests[i].EmployeeDescription
                    </th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td><strong>Request Type</strong></td>
                    <td class="text-right">@Model.Requests[i].RequestType</td>
                </tr>
                <tr>
                    <td><strong>Duration</strong></td>
                    <td class="text-right">@Model.Requests[i].DurationDescription</td>
                </tr>
                <tr>
                    <td><strong>Dates</strong></td>
                    <td class="text-right">@Model.Requests[i].DatesDescription</td>
                </tr>
            </tbody>
        </table>

    </div>
}

The view:

    @model RequestPageModel
@{
    ViewData["Title"] = "Requests";
    ViewData["SubTitle"] = "Welcome to Cascade Mobile";
}
@{Layout = "~/Views/Shared/_MainLayout.cshtml";}
@section Scripts {
    <script type="text/javascript">
        //Submit count setter
        $(document).ready(function () {
            var accepted = 0;
            var rejected = 0;
            $("#acceptradio").click(function () {
                console.log("ready 2!");
                $("#acceptCount").text("4");
            });
        });
</script>
}

<div class="container">
    <h3>@ViewBag.Warning</h3>
        @*Existing requests*@
        <h4><strong>Requests</strong> <span class="badge alert-cascade">@Model.Pager.TotalRecords</span></h4><br />
    @using (Html.BeginForm("Index", "Request", new { @id = "requestsform" }))
    {
        <div class="row">
            @Html.Partial("_MultiSelectPartial", Model.RequestModel)
        </div>
        <div>
            <textarea class="span6" rows="3" placeholder="Comments.." required></textarea>
        </div>
        <input type="submit" value="submit"/>
    }

    <button id="submitbtn" class="btn btn-primary pull-right" type="button">
        Accept 
        <span class="badge" id="acceptCount">0</span>
        Reject 
        <span class="badge" id="rejectCount">0</span>
    </button>
    @Html.Partial("_Pager", Model.Pager)
</div>

The controller action:

[HttpPost]
        public IActionResult Index(RequestModel requests)
        {
            ViewBag.Warning = "We have: ";
            foreach (var request in requests.Requests)
            {
                ViewBag.Warning +=  request.RequestID + " : ** : ";
            }
            var requestModel = GetRequestPageModel(3,1);
            //requestModel.Requests[0].AcceptChecked = true;
            return View("~/Views/User/Requests.cshtml", requestModel);
        }

The model:

using System;
using System.Collections.Generic;

namespace Mobile.Models
{
    /// <summary>
    /// Request data model for the requests page
    /// </summary>
    public class RequestModel
    {
        public List<Request> Requests;
        public RequestModel(List<Request> requests)
        {
            Requests = requests;
        }
        public RequestModel()
        {
            Requests = new List<Request>();
        }
    }
}

Again, if the post method takes just a list of request items its fine, but I will need to pass more information and cannot get it to post the list as part of the model. Can anyone see whats wrong here?

Monolithcode
  • 598
  • 8
  • 19

1 Answers1

1

The model in your partial is RequestModel and your loop is generating controls with

name="Requests[0].RequestID"
name="Requests[1].RequestID"

etc, but the model in your POST method should be RequestPageModel so the correct name attributes would need to be

name="RequestModel.Requests[0].RequestID"
name="RequestModel.Requests[1].RequestID"

which will post back to

[HttpPost]
public IActionResult Index(RequestPageModel model)

You need to change the model in the partial to @model RequestPageModel (and adjust the HtmlHelper methods accordingly) and in the main view, use @Html.Partial("_MultiSelectPartial", Model).

In addition, change the name of the parameter to (say) RequestModel model so there is no conflict with the equivalent property name (refer this answer for an explanation).

I would however recommend that you use custom EditorTemplate's for your types rather than a partial, so that you controls are correctly named (refer the 2nd part of this answer for an example)

Side notes:

  1. Your radio buttons do not makes sense, since they have different names so you can select both (and once selected, you cannot un-select them)
  2. Your have a <textarea> without a name attribute so it will not submit a value.
Community
  • 1
  • 1
  • Hi, RequestPageModel is what the main pages uses and contains RequestModel which is just the list of requests plus a text field shortly. Would your answer still be the same? The partial view has no need for the extra stuff that comes from the RequestPageModel and just needs the list of requests and text value from RequestModel. Would I really still need to pass the whole thing through? – Monolithcode Aug 10 '16 at 07:17
  • Yes, I know. But in order to post back the value of the `textarea` it needs a `name` attribute and the model in the POST method must be `RequestPageModel`. And you must change the name of the parameter. Even if you want to keep the existing POST method and partial, you must still change the method to `public IActionResult Index(RequestModel anyNameExceptRequests, string theNameOfYourTextArea)` –  Aug 10 '16 at 07:21
  • Hi, I finally have this working and had to pass through the RequestPageModel. I was fine to keep the action as RequestPage and the form still just uses RequestPage data but as the model is RequestPageModel the controls are now labled as: name="RequestModel.Requests[0].RequestID" name="RequestModel.Requests[1].RequestID" This will be why it is working on the postback now as the names are fully qualified. So I was wondering, how can you modify the naming convention to force the model to be included by default? Or even the ability to add a prefix so I could set the name with the model. – Monolithcode Aug 11 '16 at 09:52
  • Yes, that what my answer says :) –  Aug 11 '16 at 09:56