简体   繁体   中英

MVC model is being passed as null to the view

I have the following view below when is recieving a Model which is not null because the table is being populated with data, then when I click on the submit button to trigger the Controller method below, the model being passed the controller method is being passed as null.

What am I doing wrong? I have been trying to solve this for hours, why exactly is this happening and why is the model not being passed to the controller method since I am 100% sure that the Model is not null?

Controller Method:

    [HttpPost]
    public ActionResult AssignRole(PageDetailsModel model)
    {
        //controller method here which is not being executed since model being passed is null
    }

View:

    @model OnlineLearningLTD.Models.PageDetailsModel

    @{
            ViewBag.Title = "Page Details";
    }

    <h2>Page Details</h2>

    <table class="table table-hover">
    @using (Html.BeginForm("AssignRole", "Page", FormMethod.Post))
    {
        if (Model.CurrPage != null)
        {

            <tr>
                <td>Name</td><td>@Model.CurrPage.Name</td>
            </tr>
            <tr>
                <td>Url</td><td>@Model.CurrPage.Url</td>
            </tr> 
            <tr>
                <td>Assigned Roles:</td>
                @foreach(var role in @Model.CurrPage.Role)
                {
                    <td>@role.Name</td>
                }
            </tr>     

            <tr>
                <td colspan="2"><h3>Assign Role</h3></td>
            </tr>
            <tr>
                <td>Choose Role</td>
                <td>
                 @Html.DropDownListFor(model => model.RoleId, Model.RoleList)

                </td>
            </tr>       
            <tr>
                <td colspan="2" align="right">
                     @if(Model.RoleList.Count() > 0)
                    {
                        <input type="submit" value="Assign Role" id = "btnAssignRole"/>
                    }
                    else
                    {
                        <label>There are no roles to be assigned</label>
                     }                
                </td>
            </tr>


        }
    }

Model:

public class PageDetailsModel
    {

        public SelectList RoleList { get; set; }
        public int RoleId { get; set; }

        public SelectList AssignedRoleList { get; set; }
        public int DeassignRoleID { get; set; }

        public CommonLayer.Page CurrPage { get; set; }

        public string PageID { get; set; }

        public PageDetailsModel() { }

        public PageDetailsModel(string pageID)
            : this()
        {
            this.PageID = pageID;
            CurrPage = new BusinessLayer.Pages().getPageByID(pageID);

            BusinessLayer.Roles role = new BusinessLayer.Roles();
            AssignedRoleList = new SelectList(role.getAllPageRoleWithPageID(pageID), "Id", "Name");
            RoleList = new SelectList(role.getAllPageRolesByPageId(pageID), "Id", "Name");
        }

    }

You can look at a couple of things. What kind of names these properties have when you view the html? Are they following the correct convention for the default model binder? You can also take a look at form values under HttpContext.Current.Request while debugging. Do you see all the values like RoleId? If the names are not correct format, you may have to change them in the view. If that is not possible consider using custom model binder. Here's a couple of resources for that ASP.Net MVC Custom Model Binding explanation and Code Project Link

For debugging, change the controller argument to a FormCollection like this:

 public ActionResult AssignRole(FormCollection formValues)

By doing that, you can see what is actually being posted back from your form. MVC is nothing more than clever plumbing that tries to populate your model object with information being posted back through your form. If it doesn't live in the FormCollection, it won't be bound to the model's properties.

Right away, I see that the only value being posted back to the controller is the RoleID of the role selected in the dropdown. All of the other values are injected as raw text or HTML into the page and they aren't bound to the post back.

If you add @Html.HiddenFor(model=>model.PageID, Model.PageID) somewhere in the form body, the model passed to the controller will contain both the RoleID and the PageID. That should be enough to perform the function (Assign Role) and rebuild the model to be passed back to the view.

Only the RoleID will get posted in your code. The values are posted with input and select fields. The only input field you are using is the Role -selectlist. Plain text is not posted, like your text within td-tags.

You'll need to add hidden inputs for other fields you want to post. You can use HiddenFor and Hidden html helpers for this.

@model OnlineLearningLTD.Models.PageDetailsModel

@{
        ViewBag.Title = "Page Details";
}

<h2>Page Details</h2>

<table class="table table-hover">
@using (Html.BeginForm("AssignRole", "Page", FormMethod.Post))
{
    if (Model.CurrPage != null)
    {

        <tr>
            <td>Name  @Html.HiddenFor(model => model.CurrPage.Name</td>
            <td>@Model.CurrPage.Name</td>
        </tr>
        <tr>
            <td>Url @Html.HiddenFor(model => model.CurrPage.Url</td>
            <td>@Model.CurrPage.Url</td>
        </tr> 
        <tr>
            <td>Assigned Roles:</td>
            @foreach(var role in @Model.CurrPage.Role)
            {
                <td>@role.Name</td>
            }
        </tr>     

        <tr>
            <td colspan="2"><h3>Assign Role</h3></td>
        </tr>
        <tr>
            <td>Choose Role</td>
            <td>
             @Html.DropDownListFor(model => model.RoleId, Model.RoleList)

            </td>
        </tr>       
        <tr>
            <td colspan="2" align="right">
                 @if(Model.RoleList.Count() > 0)
                {
                    <input type="submit" value="Assign Role" id = "btnAssignRole"/>
                }
                else
                {
                    <label>There are no roles to be assigned</label>
                 }                
            </td>
        </tr>
    }
}

Now if you want to post CurrPage.Role collection, you will need to use for-loop instead of foreach. This will ensure the inputs are named correctly and modelbinder can parse the model.

    <tr>
            <td>Assigned Roles:</td>
            @for (int i = 0; i < Model.CurrPage.Role.Count(); i++)
            {
                <td>
                   @Html.HiddenFor(model => model.CurrPage.Role[i])
                   @role.Name
                </td>
            }
    </tr>  

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