wtyneb
wtyneb

Reputation: 188

How do I create/use a form and model-binder to pass collections from View => Controller

I have an MVC 4 Razor view that I passed a view model with a collection of User and a collection of Entities.

For the purposes of this question, let's say I'm trying to add a new User to each of the Entities. So I have a text form field where you enter a username and then the that username and the entities is sent to a controller method AddUser(User user, IEnumerable<Entity> ents) this method could alternatively be AddUser(long UserId, IEnumerable<TPkey> keys). AddUser gets the User and Entitys and takes care of adding the User to each of the Entitys

So, how should I go about passing the the IEnumerable<TPkey> to the controller method?

This is also somewhat complicated by the fact that the the controller needs to handle different types of primary keys.

Right now I'm doing this:

    @using (Html.BeginForm("AddUser", ViewContext.RouteData.Values["Controller"].ToString()))
    {

        <label for="user"> Username (or Email) to grant access:</label>
        <input type="text" name="user" />
        <input type="submit" value="Grant Access" />

        foreach (var item in Model.Entities)
        {
            <input type="hidden" name="keys" value="@item.PKey" />
        }
    }

Does that make sense? I would prefer to be using a more strongly typed method of passing the keys to the controller, because right now I'm having to do some sketchy parsing of the string of keys, and I would prefer to be able to sen over a strongly/generic typed object.

Upvotes: 1

Views: 157

Answers (2)

Bruno V
Bruno V

Reputation: 1731

Please try following code. To use a more strongly typed approach, you should use a User model with a list of Entity models.

Models:

public class User
{
    public string UserName { get; set; }
    public List<Entity> Entities { get; set; }
}

public class Entity {
    public string PKey { get; set; }
}

Controller

public ActionResult Index()
{
    User u = new User();
    u.Entities = new List<Entity>();
    u.Entities.Add(new Entity { PKey = "A" });
    u.Entities.Add(new Entity { PKey = "B" });
    u.Entities.Add(new Entity { PKey = "C" });
    return View(u);
}

public ActionResult AddUser(User user) {
    // Add save logic here
    return View(u);
}

View:

@model MVCSandbox.Models.User

@using (Html.BeginForm("AddUser", "User")) 
{ 

    <label for="user"> Username (or Email) to grant access:</label>
    @Html.TextBoxFor(u=>u.UserName)
    <input type="submit" value="Grant Access" />
    // The index is used to allow model binding the list of Entities
    for (int i = 0; i < Model.Entities.Count; i++) 
    { 
        @Html.TextBoxFor(m=>Model.Entities[i].PKey)
    }
}

Upvotes: 1

M&#225;rcio Gonzalez
M&#225;rcio Gonzalez

Reputation: 1040

To list a model property that is a collection of objects of another class, try this:

Class Person {
   public int id {get; set;}
   public string name {get; set;}
   public List<Car> Cars {get; set;}
}

Class Car {
   public int id {get; set; }
   public string name {get; set;}
}

in view, you must list the car objects from person object as inputs named with the property name and a index

 @using (Html.BeginForm("AddUser", ViewContext.RouteData.Values["Controller"].ToString()))
    {

        <label for="name"> </label>
        <input type="text" name="name" />



int counter = 0;

foreach (var item in Model.Entities)
    {
        <input type="hidden" name="Cars[@counter.ToString()].id" value="@item.PKey" />
        <input type="hidden" name="Cars[@counter.ToString()].name" value="@item.name" />

        counter++;
    }

        <input type="submit" value="Grant Access" />
    }

Using this, your model automatically populate the property Cars with the inputs sent from the form

Upvotes: 2

Related Questions