2009-06-02 20 views
23

Mam zagnieżdżony obiekt danych dla zestawu elementów w obrębie kategorii. Każda kategoria może zawierać podkategorie i nie ma ustalonego limitu głębokości podkategorii. (System plików miałby podobną strukturę.) Wygląda to mniej więcej tak:Rekursja w widoku 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; 
} 

jestem przechodzącej listę kategorii do moim zdaniem jak IQueryable<category>. Chcę wypisać kategorie jako zestaw bloków zagnieżdżonych nieuporządkowanych (<ul>). Mógłbym zagnieździć pętle foreach, ale wtedy głębokość podkategorii byłaby ograniczona przez liczbę zagnieżdżonych bloków foreach. W WinForms zrobiłem podobne przetwarzanie przy użyciu rekurencji do zapełnienia TreeView, ale nie widziałem żadnych przykładów użycia rekursji w widoku ASPV MVC.

Czy rekursję można wykonać w widoku ASPX? Czy są inne silniki widoku, które zawierają rekurencję dla danych wyjściowych widoku?

+1

Kiedy pisałem to pytanie, nie sądzę Zrozumiałem różnicę pomiędzy 'IQueryable' i' IEnumerable'. Używałbym 'IEnumerable' teraz, ponieważ widok nie wykonuje żadnych kwerend i powinien wyliczać tylko dane. – CoderDennis

Odpowiedz

33

Stwórz własną metodę rozszerzenia HtmlHelper tak:

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

Funny należy zadać, bo rzeczywiście stworzył jedną z nich właśnie wczoraj.

+0

Podoba mi się ten kod.Wydaje się dziwne, że można napisać taką wyspecjalizowaną funkcję pomocnika, ale czy to działałoby lepiej niż rekursywny widok częściowy w odpowiedzi Tomasa? – CoderDennis

+1

Jestem w 99% pewien, że wydajność będzie tutaj lepsza. "RenderPartial" ma trochę narzutów, ponieważ jako prosta metoda pomocnicza ma tak niewiele na głowie, jak to tylko możliwe. – Charlino

+5

Jest to również dobre rozwiązanie - jednak użyłbym klasy TagBuilder do wygenerowania kodu HTML. Lub przynajmniej StringBuilder, zamiast tylko łączenia łańcuchów ...;) –

25

Możesz to łatwo zrobić, korzystając z każdej listy <ul> w PartialView, a dla każdej nowej listy, którą chcesz uruchomić, po prostu zadzwoń pod numer Html.RenderPartial("myPartialName");.

Więc Category PartialView mógłby wyglądać następująco:

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

w widoku, wystarczy wysłać "root" kolekcję jako model częściowego widzenia:

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

EDIT:

  • Zapomniałem drugiego parametru do połączenia Html.RenderPartial() - oczywiście kategorię h jako przekazany jako model.
  • Oczywiście masz rację co do błędu SUCHEGO, który popełniłem - odpowiednio zaktualizowałem swój kod.
+0

W widoku częściowym w jaki sposób ustawiać cat.categories jako model dla częściowego widoku kategorii podrzędnej? – CoderDennis

+0

Metoda RenderPartial może przyjąć drugi parametr, który zostanie użyty jako model. Osobiście nie robiłbym pętli na twojej stronie, po prostu przekaż jej kolekcję kategorii i uruchom tam pętlę - w ten sposób jest bardziej SUCHA. – Charlino

6

można ponownie użyć elementów HTML z lambdas

przykładu


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

Jest to o wiele ładniejsze rozwiązanie. dobrze pomyśleli o –

18

można użyć metody pomocnika.

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

Więcej informacji można znaleźć pod tym linkiem: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx

+4

+1 za ciekawe alternatywne podejście. Jeszcze tego nie widziałem. – CoderDennis

Powiązane problemy