octref
octref

Reputation: 6801

Model and ItemModel bindings in ASP.NET MVC4 Views

I used the Scaffolding Option to create a SubscriptionController.
Here are some code snippets:

In Controller:

public ActionResult Index()
{
    var subscriptions = db.Subscriptions;
    return View(subscriptions.ToList());
}

In View:

@model IEnumerable<Liquidity.Domain.Entity.Subscription>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Amount)
        </th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Amount)
        </td>    
        </td>
    </tr>
}
</table>

For my subscription model, I have:

public decimal Amount { get; set; }

Here are my questions:

  1. I thought Model is bound to a LIST of Subscription rather than one Subscription identity, as this sugeests:

    @foreach (var item in Model)

    is this correct?

  2. If 1 is correct, why in this line:

    @Html.DisplayNameFor(model => model.Amount)

    model would have Amount? This suggests that model is bound to a single "Subscription".

  3. For this line:

    @Html.DisplayFor(modelItem => item.Amount) What is modelItem and item bound to? Why the lambda accepts modelItem but does not use it?

Please explain what exactly are Model, model and modelitem bound to.
Thanks.

Upvotes: 1

Views: 934

Answers (1)

cadrell0
cadrell0

Reputation: 17307

  1. That is correct.
  2. It is using this overload of DisplayNameFor. This allows you to get a single item from an IEnumerable model to display things like column headers.
  3. modelItem is your model. item is from the foreach. This is kinda sorta cheating to allow you to use the DisplayFor helper.

More on item 3. This is fine for DisplayFor. However, this will cause issues if you want to make an editable grid and EditorFor/TextboxFor. Razor will assign the name to the generated HTML based on the expression passed in. Because of this, all items in the table will have the same name. However, to post a collection, the names must include an index, such as Item[0].Amount, Item[1].Amount....

There are two options to make this work.

First, use a for instead of a foreach. You will need to change your model to something that supports an indexer, IList, ICollection, etc. This will look like.

@for(int i = 0; i < Model.Count; i++)
{
    <tr>
        <td>
            @Html.EditorFor(model=> model[i].Amount)
        </td>    
        </td>
    </tr>
}

Or, what I prefer, create an editor template. Make a new folder in your views name "EditorTemplates". Then make a file named "Subscription.cshtml". This file should look like this

@model Subscription
<tr>
    <td>
        @Html.EditorFor(model=> model.Amount)
    </td>    
    </td>
</tr>

Now in your view, you can have

@Html.EditorFor(model => modle)

or

@Html.EditorForModel()

The razor engine is smart enough to recornize that your model is a collection, and it will render your EditorTemplate for each item in the collection. Additionally, it will generate names correctly, so you don't have to worry about that. Using this method, you can keep using an IEnumerable model, too.

This will also work with DisplayTemplates. You just need to change the name of the folder to DisplayTemplates and use DisplayFor instead of EditorFor.

Upvotes: 2

Related Questions