0

I have a list, then i want to pass it to view. I use model binding method. Here is my code: Model:

public class Group
{
    public string Name { get; set; }
    public string GroupID { get; set; }
}

Controller:

public ActionResult Index()
{
    return View(ListGroup);
}

[HttpPost]
public ActionResult Index(List<Group> listModel)
{
    @ViewBag.Success = "Update Suceess";
    return View(listModel);
}

[HttpPost]
public ActionResult Search(Group modelSearch)
{
    List<Group> listResult = listGroup.Where(m=>m.GroupID == modelSearch.GroupID).ToList();

    if (modelSearch.GroupID == null)
        return View("Index", listGroup);

    return View("Index", listResult);
}

In view i want to display list by group of GroupID

@using (Html.BeginForm("Index", "DisplayTable", FormMethod.Post))
{
    <table>
        <tr>
            <td>Name</td>
            <td>GroupID</td>
        </tr>
        @foreach(var items in Model.GroupBy(m => m.GroupID).Select(g => g.ToList())){
            <tr><td colspan="2">@items.ElementAt(0).GroupID</td></tr>
            for (int i = 0; i < items.Count; i++) { 
            <tr>
                <td>@Html.TextBoxFor(m => items[i].Name)</td>
                <td>@Html.TextBoxFor(m => items[i].GroupID)</td>
            </tr>
            }
        }
    </table>
        <input type="submit" value="SAVE" />
    }

My problem is: If I use the code above, the view display data in group correctly, but when i click button SAVE, It didn't send any data to controller, ActionResult Index(List<Group> listModel) received a null list.

MORE EXPLANATION: I have list like this:

  • NAME | GROUPID Pen; A Ink; B Pecil; A Book ; C Ruler; B NoteBook ; C

I want to display it in view like this:

  • GroupID: A - Pen: A | Pencil: A
  • GroupID: B - Ruler: B | Ink: B
  • GroupID: C - Book: C | NoteBook: C

If i use this code in view:

<td>@Html.TextBoxFor(m => items[i].Name)</td>
<td>@Html.TextBoxFor(m => items[i].GroupID)</td>

The result in view is correct, but this cannot send data back to controller. I found that the name of each field Name GroupID didnt match with the name of textfield in html.

I use group by in view and i dont know what is the best way in this case: Group by in controller or in View. and What is the structure of list if I group by in controller.

Tran Duy Linh
  • 221
  • 1
  • 9
  • you should user a List<> instead of IEnumerable, for, and EditFor, have you searched before? http://stackoverflow.com/questions/12696988/mvc-editing-a-list-of-objects – Murilo Jul 16 '15 at 18:02

2 Answers2

2

From your edit, I see so you have a list of these objects and you must group them by id, so many objects like pen and pencil have the same id 'A', if we group them together, they belong to group with ID 'A'.

Your best practice is to have the correct business logic, and model from controller to view. Like in this answer, actually have the List'group' in your model, if you want to separate business and UI, then create a view model, which holds your 'group' business object. The ui part will hold a list of child groups. Then serialize from viewmodel to business model.

For how to populate the correct model from only having a list of child items. See the controller.

First, you can check the source of the page to see if razor is building the correct html that you expect.

Your problem is due to how list binding works. At the end your page source must appear as below:

Notice the hidden for fields, they bind your original objects and their id's. Each input on the page must have a unique name or ID.

<tr>
    <td colspan="2">
     0 - name - 0
     <input name="[0].GroupID" type="hidden" value="0" />
     <input name="[0].Name" type="hidden" value="0 - name" />
    </td>
   </tr>   
   <tr>
    <td><input class="text-box single-line" name="[0].ListGroup[0].Name" type="text" value="0 - name0 - subname" /></td>
    <td><input name="[0].ListGroup[0].GroupID" type="text" value="00" /></td>
   </tr>
   <tr>
    <td><input class="text-box single-line" name="[0].ListGroup[1].Name" type="text" value="0 - name1 - subname" /></td>
    <td><input name="[0].ListGroup[1].GroupID" type="text" value="01" /></td>
   </tr>

Use the code below to make the solution work. Please don't allow people to edit the groupID as once this is saved it will effect the behavior, IDs must remain unique. This is also true within your DB structure.

Your Controller

public class homeController : Controller
 {
  //
  // GET: /home/

  [HttpGet]
  public ActionResult Index()
  {
   List<Group> lst = new List<Group>();
   for (var i = 0; i < 5; i++)
   {
    lst.Add(new Group()
    {
     GroupID = i.ToString(),
     Name = i.ToString() + " - name",
     ListGroup = new List<Group>()
    });
   }

   foreach (var g in lst)
   {
    for (var i = 0; i < 3; i++)
    {
     g.ListGroup.Add(new Group()
     {
      GroupID = g.GroupID + i.ToString(),
      Name = g.Name + i.ToString() + " - subname",
      ListGroup = new List<Group>()
     });
    }
   }
   
   return View(lst);
  }

  [HttpPost]
  public ActionResult Save(List<Group> listModel)
  {
   @ViewBag.Success = "Update Suceess";
   return View(listModel);
  }

  public class Group
  {
   public string Name { get; set; }
   public string GroupID { get; set; }
   public List<Group> ListGroup { get; set; }
  }
 }

Your View

@model List<TestMVC.Controllers.homeController.Group>

@{
 ViewBag.Title = "Index";
}

<h2>Index</h2>

@using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
 <table>
  <tr>
   <td>Name</td>
   <td>GroupID</td>
  </tr>
  @for (var g = 0; g < Model.Count; g++)
  {
   <tr>
    <td colspan="2">
     @Model[g].Name - @Model[g].GroupID
     @Html.HiddenFor(x => Model[g].GroupID)
     @Html.HiddenFor(x => Model[g].Name)
    </td>
   </tr>   
   for (var i = 0; i < Model[g].ListGroup.Count; i++)
   {
   <tr>
    <td>@Html.EditorFor(x => Model[g].ListGroup[i].Name)</td>
    <td>@Html.TextBoxFor(x => Model[g].ListGroup[i].GroupID)</td>
   </tr>
   }
  }

 </table>
 <input type="submit" value="SAVE" />
}

Runtime: enter image description here

Please take a look at this answer here for a different approach using Model Editor For: MVC Form not able to post List of objects

Further reading, excelent article on List Binding in MVC: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

Community
  • 1
  • 1
Denys
  • 443
  • 3
  • 10
  • @foreach(var item in Model){ @item.Name @item.GroupID } How can group with this code – Tran Duy Linh Jul 17 '15 at 02:30
  • but I want to display it in group and edit text in each input – Tran Duy Linh Jul 17 '15 at 02:51
  • Thanks for very clear explanation, but there are something that different from my code. I declare IEnumerable ListGroup { get; set; } but actually i don't use it. My understand about your code is, In each object from class 'Group' we have Name, GroupID and List, so we automatically have a group list, but in my list, there are only two fields Name, GroupID, this is reason why i have to group them in view. – Tran Duy Linh Jul 17 '15 at 04:47
  • I see, thanks. Can you please expand your question in 2 ways. How do you populate the 'ListGroup' in your index view? And, what is the end result - why exactly you are grouping them in view, instead of controller? You must have multiple groups with same ID to group them by, and then present editors for each group individually? (this means we will still have to have ID per each instance unique. Please update, i'll take a look tomorrow and see if i can help. We can then delete the comments to have clear answer on here. – Denys Jul 17 '15 at 05:18
0

Try to use Html.TextBox:

@{ var index = 0; }
@foreach(var items in Model.GroupBy(m=>m.GroupID).Select(g=>g.ToList())){
   Html.TextBox("items[" + index + "].Name", items.Name);
   Html.TextBox("items[" + index + "].GroupID", items.GroupID);
   index++;
}
Striter Alfa
  • 1,378
  • 1
  • 12
  • 28