MVC Razor @foreach

82

Escuché que tener @foreach dentro de una vista es un no-no. Es decir, la vista no debería tener ninguna lógica. ¿Cuál es la mejor práctica sobre dónde debería estar la lógica de @foreach?

    @foreach.. 
Mascota Nate
fuente
5
¿Dónde has leído eso? ¡La lógica es para lo que está diseñada la navaja!
Nicholas King
4
por favor lea el siguiente tutorial de ms asp.net/web-pages/tutorials/basics/…
Nicholas King

Respuestas:

162

¿Cuál es la mejor práctica sobre dónde debería estar la lógica de @foreach?

En ninguna parte, simplemente deshazte de él. Puede utilizar un editor o plantillas de visualización.

Así por ejemplo:

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

perfectamente bien podría ser reemplazado por una plantilla de visualización:

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

y luego definirás la plantilla de visualización correspondiente (si no te gusta la predeterminada ). Por lo tanto, definiría una plantilla reutilizable ~/Views/Shared/DisplayTemplates/Foo.cshtmlque el marco procesará automáticamente para cada elemento de la colección Foos ( IEnumerable<Foo> Foos { get; set; }):

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

Obviamente, se aplican exactamente las mismas convenciones para las plantillas de editor que deben usarse en caso de que desee mostrar algunos campos de entrada que le permitan editar el modelo de vista en contraste con mostrarlo como de solo lectura.

Darin Dimitrov
fuente
3
@DarinDimitrov, eso es cierto si se trata de un modelo MVC estricto contra el que está codificando; sin embargo, si surge una circunstancia en la que es necesario recorrer una colección que no está en el modelo, no veo ningún problema con el uso de foreach
Nicholas King
6
@NicholasKing, tal circunstancia nunca debería surgir. Una vista no debe tocar nada más que lo que está presente en el modelo de vista pasado por la acción del controlador. Si no está en el modelo de vista, debe colocarlo allí si la vista lo necesita. Eso es exactamente para lo que están destinados los modelos de vista.
Darin Dimitrov
1
@MihaiLabo, sí, eso es lo que estoy diciendo.
Darin Dimitrov
2
¿Qué pasa si el tipo de elemento de la colección no es suficiente para determinar la visualización? Por ejemplo, mi modelo tiene una colección de cadenas, pero a veces quiero tener una cadena por línea y, en otro caso, las quiero separadas por comas.
Marc Stober
6
Entonces, ¿cuál es exactamente el problema foreach? Como mínimo, una plantilla de visualización (aunque es un enfoque perfectamente aceptable, independientemente) requiere que se renderice una nueva vista, que no es gratuita. La mayoría de las veces no afectará el tiempo de carga de su sitio de manera notable, pero si se hace lo suficiente, podría afectar el rendimiento. Un foreachpoco de HTML es y siempre será prácticamente instantáneo. Como dije, no es un gran problema de cualquier manera, pero en todo caso hay un argumento para usar foreach.
Chris Pratt
98

Cuando las personas dicen que no pongan lógica en las vistas, generalmente se refieren a la lógica empresarial, no a la lógica de procesamiento. En mi humilde opinión, creo que usar @foreach en las vistas está perfectamente bien.

JonoW
fuente
22
Convenido. Me recuerda a los viejos debates semánticos de HTML, que eventualmente llevaron a la gente a intentar usar divs y CSS para crear una "tabla" para datos tabulares reales, porque eran muy anti-tablas.
Chris Pratt
1
Estoy de acuerdo. Los seres humanos somos malos para desviarnos. ¿Realmente necesito una nueva carpeta, con una nueva vista, solo para mostrar cosas en una lista en mi modelo de vista?
Don Cheadle
1
¿No sería necesario usar div / css para crear una vista de datos tabulares para crear una vista receptiva?
frostshoxx
13

Estoy usando @foreachcuando envío una entidad que contiene una lista de entidades (por ejemplo, para mostrar 2 cuadrículas en 1 vista)

Por ejemplo, si envío como modelo la entidad Foo que contiene Foo1(List<Foo1>)yFoo2(List<Foo2>)

Puedo referirme a la primera Lista con:

@foreach (var item in Model.Foo.Foo1)
{
    @Html.DisplayFor(modelItem=> item.fooName)
}
Mihai Labo
fuente
11

una respuesta a @DarinDimitrov para un caso en el que he usado foreach en una vista de navaja.

<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
fuente
6
WOW hombre, ¿escribirías algo como esto en una vista? ¿Por qué no escribir un ayudante personalizado reutilizable a la Html.DropDownListForque simplemente tendrá en cuenta el título? Es trivial y no convierte sus puntos de vista en código espagueti: stackoverflow.com/a/7938038/29407
Darin Dimitrov
7
@DarinDimitrov sí, trabajamos en un entorno muy ágil, lo que significa que escenarios como este a veces nos impiden usar cosas como DropDownFor ya que no siempre tenemos un requisito claramente definido. Creo que en este caso, el menú desplegable inicialmente no necesitaba "todo", luego lo hizo, pero solo en un menú desplegable en la vista. Como esta página usa ajax para actualizar, no es un patrón MVC estricto y no puede cargar un producto en todas las categorías según el requisito. No es ideal, pero a veces es inevitable.
Nicholas King
Quizás un mejor ejemplo sería usar esto para representar optgroupelementos en la lista de selección, ya que no hay soporte para eso en HtmlHelpers. Si solo necesita agregar un elemento adicional a la lista de selección, hay mejores formas de lograrlo y luego seguir usando el asistente.
Chris Pratt
Esa no sería mi definición de ágil, tengo que estar de acuerdo con @DarinDimitrov
Luis Filipe
3

La respuesta no funcionará cuando se usa la sobrecarga para indicar la plantilla @Html.DisplayFor(x => x.Foos, "YourTemplateName).

Parece estar diseñado de esa manera, vea este caso . Además, la excepción que da el marco (sobre el tipo que no ha sido el esperado) es bastante engañosa y me engañó en el primer intento (gracias @CodeCaster)

En este caso tienes que usar@foreach

@foreach (var item in Model.Foos)
{
    @Html.DisplayFor(x => item, "FooTemplate")
}
Tiberiu Craciun
fuente
Esa respuesta está escrita por alguien que trabaja en MVC, así que supongo que saben lo que están diciendo. MVC iterará IEnumerable<T>y llamará a la plantilla para el tipo Tde cada elemento.
CodeCaster
Con respecto a su edición: es su código el que está mal o un error en esa versión específica de MVC (en 5.2.2 me funciona). Se supone que funciona como se describe en la respuesta aceptada. En lugar de decir que está mal, abra su propia pregunta sobre el problema si lo desea.
CodeCaster
@CodeCaster Creo que mi respuesta agrega algunos avisos para ese caso específico (perdí parte de mi tiempo para descubrir qué salió mal). ¿Podría agregar alguna explicación para mantener el voto negativo? (gracias por su tiempo por cierto, solo quiero llegar al fondo de las cosas)
Tiberiu Craciun
No creo que sea una buena respuesta ya que no verifica el error, esto ayuda a los rumores en el mundo como "Un DisplayTemplate no puede iterar" - se supone que debe hacerlo, por lo que debería funcionar. Si realmente no es así, presente un error y, en su lugar, abra una pregunta separada donde reproduzca el problema y mencione esta versión específica, en lugar de en una sección de preguntas y respuestas que maneja lo que normalmente debería suceder.
CodeCaster
@CodeCaster Tuve otra edición mientras tanto después de entender mi caso específico (cambié toda la publicación). En mi última edición indiqué la condición exacta cuando la respuesta aceptada no se aplica con el enlace de respaldo a una pregunta relevante respondida por el mismo tipo, lo que demuestra que es por diseño y no un error.
Tiberiu Craciun