0

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");
        }

    }
tereško
  • 58,060
  • 25
  • 98
  • 150
rikket
  • 2,357
  • 7
  • 46
  • 74
  • What happens when you remove that constructor with all the logic in it? – Simon Whitehead May 12 '14 at 00:35
  • 1
    Why are you using @Model instead of Model. You can just do fine with Model – qamar May 12 '14 at 03:16
  • `AssignRole(PageDetailsModel model)` model parameter cannot be null even View has different model type. Do you use any custom model binder? – cem May 12 '14 at 06:49
  • And what happens if you follow @qamar advice by using Model instead of @Model? –  May 12 '14 at 07:01

3 Answers3

1

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

Community
  • 1
  • 1
Yogiraj
  • 1,952
  • 17
  • 29
0

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.

Colby Cavin
  • 492
  • 2
  • 6
  • I tried this out and only 1Key (RoleId) is being passed to the controller. why could this be happening? – rikket May 12 '14 at 09:00
0

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>  
Tomi Lammi
  • 2,096
  • 1
  • 18
  • 12