MVC Razor @foreach

82

Słyszałem, że posiadanie @foreach wewnątrz widoku to nie-nie. Oznacza to, że widok nie powinien zawierać żadnej logiki. Jaka jest najlepsza praktyka dotycząca tego, gdzie powinna znajdować się logika @foreach?

    @foreach.. 
Nate Pet
źródło
5
Gdzie to przeczytałeś? Logika jest tym, do czego służy brzytwa!
Nicholas King
4
przeczytaj następujący samouczek ms asp.net/web-pages/tutorials/basics/…
Nicholas King

Odpowiedzi:

162

Jaka jest najlepsza praktyka dotycząca tego, gdzie powinna znajdować się logika @foreach?

Nigdzie, po prostu się go pozbądź. Możesz użyć edytora lub szablonów wyświetlania.

Na przykład:

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

można by idealnie zastąpić szablonem wyświetlania:

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

a następnie zdefiniujesz odpowiedni szablon wyświetlania (jeśli nie podoba ci się domyślny ). Więc zdefiniowałbyś szablon wielokrotnego użytku, ~/Views/Shared/DisplayTemplates/Foo.cshtmlktóry będzie automatycznie renderowany przez framework dla każdego elementu kolekcji Foos ( IEnumerable<Foo> Foos { get; set; }):

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

Oczywiście dokładnie te same konwencje dotyczą szablonów edytorów, których należy używać w przypadku, gdy chcesz wyświetlić niektóre pola wejściowe umożliwiające edycję modelu widoku, w przeciwieństwie do wyświetlania go jako tylko do odczytu.

Darin Dimitrov
źródło
3
@DarinDimitrov to prawda, jeśli jest to ścisły model MVC, przeciwko któremu kodujesz, jednak jeśli zajdzie okoliczność, w której kolekcja musi zostać zapętlona, ​​której nie ma w modelu, nie widzę problemu z używaniem foreach
Nicholas King
6
@NicholasKing, taka okoliczność nigdy nie powinna mieć miejsca. Widok nie powinien dotykać niczego innego niż to, co jest obecne w modelu widoku przekazanym przez akcję kontrolera. Jeśli nie ma go w modelu widoku, należy go tam umieścić, jeśli widok tego potrzebuje. Właśnie do tego służą modele widoku.
Darin Dimitrov
1
@MihaiLabo, tak, to właśnie mówię.
Darin Dimitrov
2
A jeśli typ elementu w kolekcji nie wystarczy do określenia sposobu wyświetlania? Na przykład mój model ma kolekcję ciągów, ale czasami chcę mieć po jednym ciągu w wierszu, aw innym przypadku chcę, aby były oddzielone przecinkami?
Marc Stober
6
Więc w czym dokładnie jest problem foreach? Przynajmniej szablon wyświetlania (choć jest to całkowicie akceptowalne podejście, niezależnie od tego) wymaga renderowania nowego widoku, który nie jest bezpłatny. W większości przypadków nie wpłynie to zauważalnie na czas ładowania witryny, ale zrobione wystarczająco, może spowodować spadek wydajności. Odrobina foreachkodu HTML jest i zawsze będzie praktycznie natychmiastowa. Jak powiedziałem, nie jest to jednak wielka sprawa, ale jeśli już, jest argument za użyciem foreach.
Chris Pratt,
98

Kiedy ludzie mówią, że nie umieszczaj logiki w widokach, zwykle odnoszą się do logiki biznesowej, a nie renderowania logiki. Moim skromnym zdaniem uważam, że używanie @foreach w widokach jest w porządku.

JonoW
źródło
22
Zgoda. Przypomina mi o starych semantycznych debatach dotyczących HTML, które ostatecznie doprowadziły ludzi do próby użycia elementów div i CSS do stworzenia „tabeli” dla rzeczywistych danych tabelarycznych, ponieważ były one tak antytabelowe.
Chris Pratt,
1
Zgadzam się. Ludzie są źli w złym kierunku. Czy naprawdę potrzebuję nowego folderu z nowym widokiem, aby wyświetlić elementy na liście w moim modelu widoku?
Don Cheadle,
1
Czy użycie div / css do tworzenia tabelarycznego widoku danych nie byłoby konieczne do utworzenia responsywnego widoku?
frostshoxx
13

Używam, @foreachgdy wysyłam encję zawierającą listę jednostek (na przykład w celu wyświetlenia 2 siatek w 1 widoku)

Na przykład, jeśli wysyłam jako model jednostkę Foo, która zawiera Foo1(List<Foo1>)iFoo2(List<Foo2>)

Mogę odnieść się do pierwszej listy z:

@foreach (var item in Model.Foo.Foo1)
{
    @Html.DisplayFor(modelItem=> item.fooName)
}
Mihai Labo
źródło
11

odpowiedź dla @DarinDimitrov na przypadek, w którym użyłem foreach w widoku brzytwy.

<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>
Nicholas King
źródło
6
WOW stary, napisałbyś coś takiego w widoku? Dlaczego nie napisać niestandardowego pomocnika wielokrotnego użytku à la Html.DropDownListFor, który po prostu uwzględni tytuł? To trywialne i nie zmienia twoich poglądów w kod spaghetti: stackoverflow.com/a/7938038/29407
Darin Dimitrov
7
@DarinDimitrov tak, pracujemy w bardzo zwinnym środowisku, co oznacza, że ​​scenariusze takie jak ten czasami uniemożliwiają nam korzystanie z rzeczy takich jak DropDown For, ponieważ nie zawsze mamy jasno zdefiniowane wymagania. Uważam, że w tym przypadku lista rozwijana początkowo nie potrzebowała „wszystkiego”, a potem potrzebowała, ale tylko w jednym menu rozwijanym w widoku. Ponieważ ta strona używa Ajax do aktualizacji, nie jest to ścisły wzorzec MVC i nie można przesłać produktu do wszystkich kategorii zgodnie z wymaganiami. Nie idealne, ale czasami nieuniknione.
Nicholas King
Być może lepszym przykładem byłoby użycie tego do renderowania optgroupelementów na liście wyboru, ponieważ nie ma tego wsparcia w HtmlHelpers. Jeśli potrzebujesz tylko dodać dodatkowy element do listy wyboru, są lepsze sposoby, aby to osiągnąć, a następnie nadal korzystać z pomocnika.
Chris Pratt,
To nie byłaby moja definicja Agile - muszę się zgodzić z @DarinDimitrov
Luis Filipe
3

Odpowiedź nie zadziała, gdy użyjesz przeciążenia do wskazania szablonu @Html.DisplayFor(x => x.Foos, "YourTemplateName).

Wydaje się być zaprojektowany w ten sposób, zobacz ten przypadek . Również wyjątek, który daje framework (o typie nie był zgodny z oczekiwaniami) jest dość mylący i oszukał mnie za pierwszym razem (dzięki @CodeCaster)

W takim przypadku musisz użyć@foreach

@foreach (var item in Model.Foos)
{
    @Html.DisplayFor(x => item, "FooTemplate")
}
Tiberiu Craciun
źródło
Ta odpowiedź jest napisana przez kogoś, kto pracuje nad MVC, więc myślę, że wie, co mówi. MVC dokona iteracji IEnumerable<T>i wywoła szablon typu Tdla każdego elementu.
CodeCaster,
Jeśli chodzi o twoją edycję: albo twój kod jest zły, albo błąd w tej konkretnej wersji MVC (w 5.2.2 działa dla mnie). Ma działać zgodnie z opisem w zaakceptowanej odpowiedzi. Zamiast mówić, że jest źle, otwórz własne pytanie dotyczące problemu, jeśli chcesz.
CodeCaster,
@CodeCaster Myślę, że moja odpowiedź dodaje trochę ostrzeżenia do tego konkretnego przypadku (zmarnowałem trochę czasu, aby dowiedzieć się, co poszło nie tak). Czy może Pan dodać jakieś wyjaśnienie dotyczące utrzymania głosu przeciw? (dzięki za poświęcony czas przy okazji, po prostu chcę dojść do sedna sprawy)
Tiberiu Craciun
Nie sądzę, żeby to była dobra odpowiedź, ponieważ nie weryfikuje błędu, pomaga to plotkom na świecie, jak „A DisplayTemplate nie może iterować” - tak powinno, więc powinno działać. Jeśli tak nie jest, zgłoś błąd i raczej otwórz osobne pytanie, w którym powielasz problem i wspomnij, że ta konkretna wersja, w przeciwieństwie do pytań i odpowiedzi, które dotyczą tego, co normalnie powinno się wydarzyć.
CodeCaster
@CodeCaster Miałem kolejną edycję w międzyczasie po tym, jak zrozumiałem mój konkretny przypadek (zmieniłem cały post). W mojej ostatniej edycji wskazałem dokładny stan, w którym zaakceptowana odpowiedź nie ma zastosowania, z linkiem zapasowym do odpowiedniego pytania, na które odpowiedział ten sam facet, udowadniając, że jest to zgodne z projektem, a nie błąd.
Tiberiu Craciun