Create more than one object of the same type in the same view

advertisements

in my create view I want to give the user the possibility to create a list of objects (of the same type). Therefore I created a table in the view including each inputfield in each row. The number of rows respective "creatable" objects is a fixed number.

Lets say there is a class Book including two properties title and author and the user should be able two create 10 or less books.

How can I do that?

I don't know how to pass a list of objects (that are binded) to the controller. I tried:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ICollection<Book> bookList)
    {
        if (ModelState.IsValid)
        {
            foreach(var item in bookList)
                db.Books.Add(item);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(articlediscounts);
    }

And in the view it is:

<fieldset>
    <legend>Book</legend>

    <table id="tableBooks" class="display" cellspacing="0" width="100%">
            <thead>
                <tr>
                    <th>Title</th>
                    <th>Author</th>
                </tr>
            </thead>
            <tbody>
                @for (int i = 0; i < 10 ;i++ )
                {
                    <tr>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(model => model.Title)
                                @Html.ValidationMessageFor(model => model.Title)
                            </div>
                        </td>
                        <td>
                            <div class="editor-field">
                                @Html.EditorFor(model => model.Author)
                                @Html.ValidationMessageFor(model => model.Author)
                            </div>
                        </td>
                    </tr>
                }
            </tbody>
        </table>

    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

As booklist is null, it doesn't work and I don't know how to put all created objects in this list.

If you have any suggestions I would be very thankful.


The fact you use @Html.EditorFor(model => model.Title) suggests that you have declared the model in the view as

@model yourAssembly.Book

Which allows to to post back only one Book so the POST method would need to be

public ActionResult Create(Book model)

Note that you current implementation create inputs that look like

<input id="Title" name="Title" ... />

The name attributes do not have indexers (they would need to be name="[0].Title", name="[1].Title" etc.) so cannot bind to a collection, and its also invalid html because of the duplicate id attributes.

If you want to create exactly 10 books, then you need initialize a collection in the GET method and pass the collection to the view

public ActionResult Create()
{
  List<Book> model = new List<Book>();
  for(int i = 0; i < 10;i++)
  {
    model.Add(new Book());
  }
  return View(model);
}

and in the view

@model yourAssembly.Book
@using (Html.BeginForm())
{
  for(int i = 0; i < Model.Count; i++)
  {
    @Html.TextBoxFor(m => m[i].Title)
    @Html.ValidationMessageFor(m => m[i].Title)
    .... // ditto for other properties of Book
  }
  <input type="submit" .. />
}

which will now bind to your collection when you POST to

public ActionResult Create(List<Book> bookList)

Note the collection should be List<Book> in case you need to return the view.

However this may force the user to create all 10 books, otherwise validation may fail (as suggested by your use of @Html.ValidationMessageFor()). A better approach is to dynamically add new Book items in the view using either the BeginCollectionItem helper method (refer example) or a client template as per this answer.