2

I have a problem while passing an object with HttpPost... Once the form is submitted, the model is set "null" on the controller side, and I don't know where is the issue..

Here is my controller :

    public ActionResult AddUser(int id = 0)
    {
        Group group = db.Groups.Find(id);
        List<User> finalList = db.Users.ToList() ;

        return View(new AddUserTemplate()
            {
                group = group,
                users = finalList
            });
//Everything is fine here, the object is greatly submitted to the view
    }



    [HttpPost]
    public ActionResult AddUser(AddUserTemplate addusertemplate)
    {
//Everytime we get in, "addusertemplate" is NULL
        if (ModelState.IsValid)
        {
//the model is null
        }
        return View(addusertemplate);

    }

Here is AddUserTemplate.cs :

    public class AddUserTemplate
{
    public Group group { get; set; }
    public User selectedUser { get; set; }
    public ICollection<User> users { get; set; }
}

Here is the form which return a null value to the controller (note that the dropdown list is greatly populated with the good values) :

@using (Html.BeginForm()) {
<fieldset>
    <legend>Add an user</legend>
    @Html.HiddenFor(model => model.group)
    @Html.HiddenFor(model => model.users)
    <div class="editor-field">
//Here, we select an user from Model.users list
        @Html.DropDownListFor(model => model.selectedUser, new SelectList(Model.users))
    </div>
    <p>
        <input type="submit" value="Add" />
    </p>
</fieldset>
}

Thanks a lot for your help

3
  • 1
    Could you add breakpoint to controller and watch what is in Request.Form variable? Show it please. Commented Nov 18, 2012 at 12:55
  • also, show that code for the User and Group classes Commented Nov 18, 2012 at 12:57
  • is the addusertemplate object null, or just its properties? Commented Nov 18, 2012 at 13:11

2 Answers 2

2

I tried your code and in my case the addusertemplate model was not null, but its properties were all null.

That's because of a few model binding issues: Html.HiddenFor and Html.DropDownListFor do not work with complex types (such as Group or User) (at least that's how it is by default).

Also, Html.HiddenFor cannot handle collections.

Here's how to solve these issues:

  • instead of @Html.HiddenFor(model => model.group) there should be one @Html.HiddenFor for each property of the group that you need bound

  • instead of @Html.HiddenFor(model => model.users) you need to iterate through the list of users and for each object add @Html.HiddenFor for each property of the user that you need bound

  • instead of @Html.DropDownListFor(model => model.selectedUser [...], create a property like int SelectedUserId {get;set;} and use that in the DropDownList (as it cannot handle complex types).

Here's the code that works:

1. The User and Group classes, as I imagined them to be:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Group
{
    public int Id { get; set; }
    public string Name { get; set; }
}

2. The adjusted AddUserTemplate class:

public class AddUserTemplate
{
    public Group Group { get; set; }
    public IList<User> Users { get; set; }

    public int SelectedUserId { get; set; }

    public User SelectedUser
    {
        get { return Users.Single(u => u.Id == SelectedUserId); }
    }
}

The adjustments:

  • Users was changed from ICollection to IList, because we'll need to access elements by their indexes (see the view code)
  • added SelectedUserId property, that will be used in the DropDownList
  • the SelectedUser is not a readonly property, that returns the currently selected User.

3. The adjusted code for the view:

@using (Html.BeginForm())
{
    <fieldset>
        <legend>Add an user</legend>

        @*Hidden elements for the group object*@
        @Html.HiddenFor(model => model.Group.Id)
        @Html.HiddenFor(model => model.Group.Name)

        @*Hidden elements for each user object in the users IList*@
        @for (var i = 0; i < Model.Users.Count; i++)
  {
            @Html.HiddenFor(m => m.Users[i].Id)
            @Html.HiddenFor(m => m.Users[i].Name)
  }
        <div class="editor-field">
            @*Here, we select an user from Model.users list*@
            @Html.DropDownListFor(model => model.SelectedUserId, new SelectList(Model.Users, "Id", "Name"))
        </div>
        <p>
            <input type="submit" value="Add" />
        </p>
    </fieldset>
}
Sign up to request clarification or add additional context in comments.

Comments

0

Another option that does not require a bunch of hidden fields is to simply specify that you want the model passed to the controller. I think this is much cleaner.

@using(Html. BeginForm("action","controller", Model, FormMethod.Post)){
...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.