Как создать вложенные ViewComponents в Monorail и NVelocity?

Меня попросили обновить меню на веб-сайте, который мы поддерживаем. Сайт использует Castle Windors Monorail и NVelocity в качестве шаблона. В настоящее время меню отображается с использованием пользовательских подклассов ViewComponent, которые отображают элементы li. На данный момент существует только один (горизонтальный) уровень, поэтому с текущим механизмом все в порядке.

Меня попросили добавить выпадающие меню в некоторые из существующих меню. Поскольку я впервые вижу Монорельс и NVelocity, я немного растерялся.

Что в настоящее время существует:

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

Возможно ли иметь вложенные MenuComponents (или новый SubMenuComponent) что-то вроде:

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

Мне нужно нарисовать подменю (элементы ul и li) внутри переопределенного метода Render на MenuComponent, поэтому использование вложенных производных ViewComponent может не работать. Я хотел бы, чтобы метод оставался в основном декларативным методом для создания меню, если это вообще возможно.

редактировать: я могу использовать Context.RenderBody() для рендеринга вложенных производных ViewComponent, но они отображаются перед родителем. Я предполагаю, что разметка подменю должна каким-то образом подключаться к тому же выводу, что и родительский?

2 ответа

Решение

Мой оригинальный метод рендеринга выглядел как

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

Мне нужно было подключиться к 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>");    
}

Я могу использовать Context.RenderBody() для рендеринга вложенных производных ViewComponent

в переопределении метода Render вы можете использовать что-то вроде

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

и, возможно, использовать RenderSection может быть полезно, чтобы иметь возможность переопределить некоторые части из шаблона, который вы используете компонент

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

Также возможно изменить и изменить контекст:

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

все эти решения причудливы, но я, как правило, предпочитаю иметь видовой компонент, который принимает целевую модель представления (скажем, HierarchicalMenuViewModel) в качестве параметра и сохраняет логику шаблонов простой, ее проще использовать, и происходит выходная настройка

по крайней мере, для простых элементов управления (которые когда-нибудь заслуживают только макрос или частичное в зависимости от viewengine).

В конце концов, представленные выше концепции viewcomponent все еще хороши, когда вы выполняете управление, которое требует дополнительной настройки. Совет должен позаботиться о документировании логики рендеринга или о ее простоте (<= 10 строк в методе рендеринга)

Другие вопросы по тегам