2009-06-02 28 views
23

Tengo un objeto de datos anidados para un conjunto de elementos dentro de las categorías. Cada categoría puede contener subcategorías y no hay un límite establecido para la profundidad de las subcategorías. (Un sistema de archivos tendría una estructura similar.) Se ve algo como esto:Recursividad en una vista ASP.NET MVC

class category 
{ 
    public int id; 
    public string name; 
    public IQueryable<category> categories; 
    public IQueryable<item> items; 
} 
class item 
{ 
    public int id; 
    public string name; 
} 

estoy pasando una lista de categorías para mi punto de vista como IQueryable<category>. Quiero mostrar las categorías como un conjunto de bloques desordenados anidados (<ul>). Podría anidar bucles foreach, pero la profundidad de las subcategorías estaría limitada por la cantidad de bloques foreach anidados. En WinForms, he realizado un proceso similar utilizando la recursividad para llenar un TreeView, pero no he visto ningún ejemplo de uso de la recursividad dentro de una vista ASPX MVC.

¿Puede la recursión hacerse dentro de una vista ASPX? ¿Hay otros motores de vista que incluyan recursividad para la salida de vista?

+1

Cuando escribí esta pregunta, no creo que entendí la diferencia entre '' IQueryable' y IEnumerable'. Usaría 'IEnumerable' ahora ya que la vista no hace ninguna consulta y solo debería estar enumerando los datos. – CoderDennis

Respuesta

33

Crear su propio método de extensión HtmlHelper así:

namespace System.Web.Mvc 
{ 
    public static class HtmlHelperExtensions 
    { 
     public static string CategoryTree(this HtmlHelper html, IEnumerable<Category> categories) 
     { 
      string htmlOutput = string.Empty; 

      if (categories.Count() > 0) 
      { 
       htmlOutput += "<ul>"; 
       foreach (Category category in Categories) 
       { 
        htmlOutput += "<li>"; 
        htmlOutput += category.Name; 
        htmlOutput += html.CategoryTree(category.Categories); 
        htmlOutput += "</li>"; 
       } 
       htmlOutput += "</ul>"; 
      } 

      return htmlOutput; 
     } 
    } 
} 

curioso que lo pida porque en realidad creado uno de éstos apenas ayer.

+0

Me gusta este código.Parece extraño escribir una función auxiliar tan especializada, pero ¿funcionaría mejor que la vista parcial recursiva en la respuesta de Tomás? – CoderDennis

+1

Estoy 99% seguro de que el rendimiento sería mejor aquí. 'RenderPartial' tiene algunos gastos generales, ya que un método simple de ayuda tiene la menor sobrecarga posible. – Charlino

+5

Esta es también una buena solución; sin embargo, usaría la clase TagBuilder para generar el HTML. O al menos, un StringBuilder, en lugar de simplemente concatenar cadenas ...;) –

25

Puede hacerlo fácilmente teniendo cada lista <ul> en un PartialView, y para cada nueva lista que necesita para comenzar, simplemente llame al Html.RenderPartial("myPartialName");.

Así que la Category PartialView podría tener este aspecto:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Category>>" %> 
<% foreach(Category cat in ViewData.Model) { %> 
    <li><p><%= cat.name %></p> 
     <% if (cat.categories.Count > 0) { 
       Html.RenderPartial("Category", cat.Categories); 
      } %></li> 
<% } %> 

En su opinión, sólo tiene que enviar la colección "raíz" como el modelo para la vista parcial:

<% Html.RenderPartial("Category", ViewData.Model) %> 

EDIT:

  • Había olvidado el segundo parámetro para la llamada Html.RenderPartial() - por supuesto, la categoría h como para pasar como el modelo.
  • Por supuesto, usted tiene razón sobre el error SECO que cometí - He actualizado mi código en consecuencia.
+0

Dentro de la vista parcial, ¿cómo se establece cat.categories como el modelo para la vista parcial de subcategorías? – CoderDennis

+0

El método RenderPartial puede tomar un segundo parámetro para usarlo como modelo. Personalmente, no haría el ciclo en su página, simplemente páselo por su colección de categorías y comience a hacer bucles allí; es más seco de esa manera. – Charlino

6

Puede volver a utilizar partes html con lambdas

Ejemplo


public class Category 
    { 
     public int id; 
     public string name; 
     public IEnumerable categories; 
    } 
<% 
     Action<IEnumerable<Category>> categoriesMacros = null; 
     categoriesMacros = categories => { %> 
     <ul> 
      <% foreach(var c in categories) { %> 
       <li> <%= Html.Encode(c.name)%> </li> 
       <% if (c.categories != null && c.categories.Count() > 0) categoriesMacros(c.categories); %> 
      <% } %> 
     </ul> 
     <% }; %> 

    <% var categpries = (IEnumerable<Category>)ViewData["categories"]; %> 
    <% categoriesMacros(categpries); %> 
+1

ESTA es una solución mucho más agradable. bien pensado en –

18

Puede utilizar métodos de ayuda.

@model Models.CategoryModel 

@helper TreeView(List<Models.CategoryModel> categoryTree) 
{ 
    foreach (var item in categoryTree) 
    { 
    <li> 
     @if (item.HasChild) 
     { 
      <span>@item.CategoryName</span> 
      <ul> 
       @TreeView(item.ChildCategories) 
      </ul> 
     } 
     else 
     { 
      <span class="leaf @item.CategoryTreeNodeType.ToString()" id="@item._CategoryId">@item.CategoryName</span> 
     } 
    </li> 
    } 
} 

<ul id="categorytree"> 
    <li>@Model.CategoryName 
    @TreeView(Model.ChildCategories) 
    </li> 
</ul> 

Más información se puede encontrar en este enlace: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

+4

+1 para un enfoque alternativo interesante. No había visto eso todavía. – CoderDennis

Cuestiones relacionadas