2010-05-05 25 views
0

On m'a demandé de mettre à jour le menu sur un site Web que nous maintenons. Le site utilise Castle Windors Monorail et NVelocity comme modèle. Le menu est actuellement rendu en utilisant des sous-classes personnalisées de ViewComponent, qui rendent les éléments li. Pour l'instant il n'y a qu'un seul niveau (horizontal), donc le mécanisme actuel est bien.Comment créer des ViewComponents imbriqués dans Monorail et NVelocity?

J'ai été invité à ajouter des menus déroulants à certains des menus existants. Comme c'est la première fois que j'ai vu Monorail et NVelocity, je suis un peu perdu.

Ce qui existe actuellement:

<ul> 
    #component(MenuComponent with "title=Home" "hover=autoselect" "link=/") 
    #component(MenuComponent with "title=Videos" "hover=autoselect") 
    #component(MenuComponent with "title=VPS" "hover=autoselect" "link=/vps")         
    #component(MenuComponent with "title=Add-Ons" "hover=autoselect" "link=/addons")      
    #component(MenuComponent with "title=Hosting" "hover=autoselect" "link=/hosting")       
    #component(MenuComponent with "title=Support" "hover=autoselect" "link=/support")       
    #component(MenuComponent with "title=News" "hover=autoselect" "link=/news") 
    #component(MenuComponent with "title=Contact Us" "hover=autoselect" "link=/contact-us") 
</ul> 

Est-il possible d'avoir MenuComponents imbriquées (ou un nouveau SubMenuComponent) quelque chose comme:

<ul> 
    #component(MenuComponent with "title=Home" "hover=autoselect" "link=/") 
    #component(MenuComponent with "title=Videos" "hover=autoselect") 
    #blockcomponent(MenuComponent with "title=VPS" "hover=autoselect" "link=/vps")         
     #component(SubMenuComponent with "title="Plans" "hover=autoselect" "link=/vps/plans") 
     #component(SubMenuComponent with "title="Operating Systems" "hover=autoselect" "link=/vps/os") 
     #component(SubMenuComponent with "title="Supported Applications" "hover=autoselect" "link=/vps/apps") 
    #end 
    #component(MenuComponent with "title=Add-Ons" "hover=autoselect" "link=/addons")      
    #component(MenuComponent with "title=Hosting" "hover=autoselect" "link=/hosting")       
    #component(MenuComponent with "title=Support" "hover=autoselect" "link=/support")       
    #component(MenuComponent with "title=News" "hover=autoselect" "link=/news") 
    #component(MenuComponent with "title=Contact Us" "hover=autoselect" "link=/contact-us") 
</ul> 

Je dois tirer le sous-menu (éléments ul et li) à l'intérieur de la méthode Render substituée sur MenuComponent, l'utilisation de dérivés ViewComponent imbriqués peut ne pas fonctionner. Je voudrais une méthode garder la méthode fondamentalement déclarative pour créer des menus, si possible.

edit: Je peux utiliser Context.RenderBody() pour rendre les dérivés ViewComponent imbriqués, mais ils sont rendus avant le parent. Je suppose que le déchirement des sous-menus doit en quelque sorte s'accrocher dans la même sortie que le parent?

Répondre

0

Ma méthode render originale ressemblait

public override void Render() 
{ 
    var buffer = new StringBuilder();   
    var extraClass = _hoverState == MenuHoverState.Selected ? "class=\"Selected\"" : ""; 

    // Menu Item Start 
    buffer.Append("<li><a href=\"" + ComponentParams["link"] + "\"" + extraClass + ">"); 

    // Menu Text 
    buffer.Append(ComponentParams["title"]); 

    // Menu Item End 
    buffer.Append("</a></li>");      
    RenderText(buffer.ToString()); 
} 

je avais besoin de connecter le Context.Writer:

public override void Render() 
{ 
    var buffer = new StringBuilder();   
    var extraClass = _hoverState == MenuHoverState.Selected ? "class=\"Selected\"" : ""; 

    // Menu Item Start 
    buffer.Append("<li><a href=\"" + ComponentParams["link"] + "\"" + extraClass + ">"); 

    // Menu Text 
    buffer.Append(ComponentParams["title"]); 

    // Menu Item End 
    buffer.Append("</a><ul class=\"subMenu\" style=\"display:none;\">");       
    Context.Writer(buffer.ToString()); 
    Context.RenderBody(Context.Writer); 
    Contet.Writer("</ul></li>");  
} 
0

Je peux utiliser Context.RenderBody() pour rendre les derivitives de ViewComponent imbriquées

dans votre override méthode de rendu, vous pouvez utiliser quelque chose comme

RenderView("header"); 
RenderBody(); 
RenderView("footer"); 

et peut-être utiliser RenderSection pourrait être utile de pouvoir passer outre certaines parties du modèle que vous utilisez le composant

if(HasSection("header")){ 
    RenderSection("header"); 
} else { 
    RenderView("header"); 
} 

il est également possible de itterate et de modifier le contexte:

for(var item in this.SubItems){ 
    PropertyBag["item"] = item; 
    if(HasSection("item")){ 
     RenderSection("item"); 
    } else { 
     RenderView("item"); 
    } 
} 

toutes ces solutions sont de fantaisie, mais je préfère généralement avoir un composant de vue qui prend un modèle spécifique de vue spécifique (par exemple HierarchicalMenuViewModel) comme paramètre et garde le templati ng logique simple, il est plus facile à utiliser, et la personnalisation de sortie se produire

au moins pour les contrôles simples (qui ne mériteraient parfois qu'une macro ou partielle en fonction du viewengine). En fin de compte, les concepts viewcomponent illustrés ci-dessus sont toujours agréables à réaliser lors d'un contrôle nécessitant plus de personnalisation. Un conseil est de prendre soin de documenter la logique de rendu ou de la garder simple (< = 10 lignes dans la méthode de rendu)