MVC Razor @foreach

82

Я слышал, что использовать @foreach внутри представления - это недопустимо. Это означает, что в представлении не должно быть никакой логики. Какова наилучшая практика того, где должна быть логика для @foreach?

    @foreach.. 
Нейт Пет
источник
5
Где ты это читал? Логика - вот для чего нужна бритва!
Николас Кинг
4
пожалуйста, прочтите следующий учебник ms asp.net/web-pages/tutorials/basics/…
Николас Кинг

Ответы:

162

Каков наилучший способ определения логики @foreach?

Некуда, просто избавься от этого. Вы можете использовать редактор или шаблоны отображения.

Так например:

@foreach (var item in Model.Foos)
{
    <div>@item.Bar</div>
}

вполне может быть заменен шаблоном отображения:

@Html.DisplayFor(x => x.Foos)

а затем вы определите соответствующий шаблон отображения (если вам не нравится шаблон по умолчанию ). Таким образом, вы должны определить шаблон многократного использования, ~/Views/Shared/DisplayTemplates/Foo.cshtmlкоторый будет автоматически отображаться фреймворком для каждого элемента коллекции Foos ( IEnumerable<Foo> Foos { get; set; }):

@model Foo
<div>@Model.Bar</div>

Очевидно, что точно такие же соглашения применяются к шаблонам редакторов, которые следует использовать в случае, если вы хотите отобразить некоторые поля ввода, позволяющие редактировать модель представления, в отличие от простого отображения ее только для чтения.

Дарин Димитров
источник
3
@DarinDimitrov, это правда, если вы кодируете строгую модель MVC, однако, если возникает обстоятельство, когда необходимо зациклить коллекцию, которой нет в модели, я не вижу проблемы с использованием foreach
Николас Кинг,
6
@NicholasKing, таких обстоятельств не должно возникнуть. Представление не должно касаться ничего, кроме того, что присутствует в модели представления, переданной действием контроллера. Если его нет в модели представления, вы должны поместить его туда, если это необходимо для представления. Именно для этого и предназначены модели представления.
Darin Dimitrov
1
@MihaiLabo, да, это то, что я говорю.
Дарин Димитров
2
Что делать, если типа элемента в коллекции недостаточно для определения отображения? Например, в моей модели есть набор строк, но иногда я хочу иметь по одной строке на строку, а в другом случае я хочу, чтобы они были разделены запятыми?
Марк Стобер,
6
Итак, в чем именно проблема foreach? По крайней мере, шаблон отображения (в любом случае вполне приемлемый подход) требует визуализации нового представления, что не является бесплатным. В большинстве случаев это не окажет заметного влияния на время загрузки вашего сайта, но, сделав достаточно, это может привести к снижению производительности. foreachВокруг немного HTML и всегда будет практически мгновенно. Как я уже сказал, в любом случае это не имеет большого значения, но во всяком случае есть аргумент в пользу использования foreach.
Крис Пратт
98

Когда люди говорят, что не помещайте логику в представления, они обычно имеют в виду бизнес-логику, а не логику рендеринга. По моему скромному мнению, использование @foreach в представлениях - это нормально.

JonoW
источник
22
Согласовано. Напоминает мне старые дебаты о семантическом HTML, которые в конечном итоге привели людей к попыткам использовать div и CSS для создания «таблицы» для фактических табличных данных, потому что они были настолько антитабличными.
Крис Прэтт
1
Я согласен. Люди плохо ориентируются. Мне действительно нужна новая папка с новым представлением, чтобы отображать элементы в списке в моей модели просмотра?
Дон Чидл,
1
Разве использование div / css для создания табличного представления данных не требуется для создания отзывчивого представления?
frostshoxx
13

Я использую, @foreachкогда отправляю объект, содержащий список объектов (например, для отображения 2 сеток в одном представлении)

Например, если я отправляю в качестве модели объект Foo, содержащий Foo1(List<Foo1>)иFoo2(List<Foo2>)

Я могу сослаться на первый список с:

@foreach (var item in Model.Foo.Foo1)
{
    @Html.DisplayFor(modelItem=> item.fooName)
}
Михай Лабо
источник
11

ответ на @DarinDimitrov для случая, когда я использовал foreach в режиме бритвы.

<li><label for="category">Category</label>
        <select id="category">
            <option value="0">All</option>
            @foreach(Category c in Model.Categories)
            {
                <option title="@c.Description" value="@c.CategoryID">@c.Name</option>
            }
        </select>
</li>
Николас Кинг
источник
6
Ничего себе человек, ты бы написал что-то подобное в представлении? Почему бы не написать настраиваемый помощник многократного использования Html.DropDownListFor, который просто учитывает заголовок? Это тривиально и не превращает ваши взгляды в спагетти-код: stackoverflow.com/a/7938038/29407
Дарин Димитров
7
@DarinDimitrov: да, мы работаем в очень гибкой среде, а это означает, что подобные сценарии иногда не позволяют нам использовать такие вещи, как DropDownFor, поскольку у нас не всегда есть четко определенные требования. Я считаю, что в этом случае для раскрывающегося списка изначально не нужно было "все", а только в одном DropDown в представлении. Поскольку эта страница использует ajax для обновления своего не строгого шаблона MVC, и вы не можете загрузить продукт во все категории в соответствии с требованиями. Не идеально, но иногда неизбежно.
Николас Кинг
Возможно, лучшим примером будет использование этого для визуализации optgroupэлементов в списке выбора, поскольку в HtmlHelpers это не поддерживается. Если вам просто нужно добавить дополнительный элемент в список выбора, есть лучшие способы добиться этого, а затем по-прежнему использовать помощник.
Крис Пратт
Это не было бы моим определением Agile - я должен согласиться с @DarinDimitrov
Луис Филипе
3

Ответ не сработает при использовании перегрузки для указания шаблона @Html.DisplayFor(x => x.Foos, "YourTemplateName).

Кажется, так устроено, см. Этот случай . Также исключение, которое дает фреймворк (о типе, отличном от ожидаемого), довольно вводит в заблуждение и обмануло меня с первой попытки (спасибо @CodeCaster)

В этом случае вы должны использовать@foreach

@foreach (var item in Model.Foos)
{
    @Html.DisplayFor(x => item, "FooTemplate")
}
Тибериу Крачун
источник
Этот ответ написан кем-то, кто работает над MVC, так что я думаю, они знают, что говорят. MVC выполнит итерацию IEnumerable<T>и вызовет шаблон для типа Tдля каждого элемента.
CodeCaster
Что касается вашего редактирования: либо ваш код неправильный, либо ошибка в этой конкретной версии MVC (в 5.2.2 это работает для меня). Предполагается, что он будет работать, как описано в принятом ответе. Вместо того, чтобы говорить, что это неправильно, при желании задайте свой вопрос о проблеме.
CodeCaster
@CodeCaster Я считаю, что мой ответ добавляет некоторые предостережения для этого конкретного случая (я потратил некоторое время, чтобы выяснить, что пошло не так). Не могли бы вы добавить какие-нибудь объяснения для сохранения отрицательного голоса? (кстати, спасибо за ваше время, просто хочу разобраться в сути вещей)
Тибериу Крачун
Я не думаю, что это хороший ответ, поскольку он не проверяет ошибку, это помогает слухам в мире вроде «DisplayTemplate не может повторяться» - так и должно быть, поэтому он должен работать. Если это действительно не так, сообщите об ошибке и откройте отдельный вопрос, в котором вы воспроизводите проблему и упоминаете эту конкретную версию, а не в разделе вопросов и ответов, который описывает то, что обычно должно происходить.
CodeCaster 06
@CodeCaster У меня было еще одно изменение после того, как я понял свой конкретный случай (я изменил весь пост). В моем последнем редактировании я указал точное условие, когда принятый ответ не применяется с резервной ссылкой на соответствующий вопрос, на который ответил тот же парень, доказывающий, что это сделано намеренно, а не является ошибкой.
Tiberiu Craciun