2009-06-02 17 views
23

Ho un oggetto dati nidificato per un insieme di elementi all'interno di categorie. Ogni categoria può contenere sottocategorie e non esiste un limite impostato per la profondità delle sottocategorie. (Un file system dovrebbe avere una struttura simile.) Sembra qualcosa di simile:Ricorsione in 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; 
} 

sto passando un elenco di categorie a mio punto di vista come IQueryable<category>. Voglio produrre le categorie come un insieme di blocchi non ordinati nidificati (<ul>). Potrei annidare foreach loop, ma la profondità delle sottocategorie sarebbe limitata dal numero di blocchi foreach nidificati. In WinForms, ho eseguito un'elaborazione simile utilizzando la ricorsione per popolare uno TreeView, ma non ho visto alcun esempio di utilizzo della ricorsione in una visualizzazione ASPC MVC.

La ricorsione può essere eseguita all'interno di una vista ASPX? Esistono altri motori di visualizzazione che includono la ricorsione per l'output di visualizzazione?

+1

Quando ho scritto questa domanda, non mi sembra di aver capito la differenza tra '' IQueryable' e IEnumerable'. Userei 'IEnumerable' ora dato che la vista non sta facendo alcuna query e dovrebbe solo enumerare i dati. – CoderDennis

risposta

33

Crea il tuo metodo di estensione HtmlHelper in questo modo:

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; 
     } 
    } 
} 

divertente si dovrebbe chiedere perché io in realtà creato uno di questi proprio ieri.

+0

Mi piace questo codice.Sembra strano scrivere una funzione di aiuto così specializzata, ma ciò andrebbe meglio della visione parziale ricorsiva nella risposta di Tomas? – CoderDennis

+1

Sono sicuro al 99% che le prestazioni sarebbero migliori qui. 'RenderPartial' ha un sovraccarico in cui un semplice metodo di supporto ha un sovraccarico che potresti avere. – Charlino

+5

Questa è anche una buona soluzione, tuttavia, utilizzerei la classe TagBuilder per generare l'HTML. O per lo meno, un StringBuilder, invece di concatenare semplicemente stringhe ...;) –

25

Si potrebbe facilmente farlo con ogni lista <ul> in un PartialView, e per ogni nuovo elenco è necessario avviare solo chiamare Html.RenderPartial("myPartialName");.

Così il PartialView Category potrebbe assomigliare a questo:

<%@ 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> 
<% } %> 

a suo avviso, è sufficiente inviare la collezione "root" come modello per la vista parziale:

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

EDIT:

  • Avevo dimenticato il secondo parametro per la chiamata Html.RenderPartial() - ovviamente la categoria h da passare come il modello.
  • Ovviamente si ha ragione sull'errore DRY che ho fatto - Ho aggiornato il mio codice di conseguenza.
+0

All'interno della vista parziale, come si imposta cat.categories come modello per la vista parziale della sottocategoria? – CoderDennis

+0

Il metodo RenderPartial può utilizzare un secondo parametro come modello. Personalmente non farei il loop sulla tua pagina, semplicemente passalo alla tua collezione di categorie e inizia il loop lì - è più ASCIUTTO in questo modo. – Charlino

6

È possibile riutilizzare le parti html con lambda

Esempio


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

QUESTA è una soluzione molto più bella. ben pensato di –

18

È possibile utilizzare metodi di supporto.

@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> 

Maggiori informazioni si possono trovare su questo link: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

+4

+1 per un interessante approccio alternativo. Non l'avevo ancora visto. – CoderDennis

Problemi correlati