Ticket

Help understand Shapes and Templates in Navigation.

I really feel the need to understand shapes and templates in Orchard so I can properly create my themes. I however seem to have a hard time here. First off I guess I don't have my environment correctly set up[^1] to help me with Visual Studio code completion, but I guess that doesn't matter anyways as this is all dynamic and unknown.

So the first instance of messing with shapes and templates I really run into is the navigation, you might want to do something theme specific for your main menu. I've read about shapes and I figure I understand them so this should be simple. In addition I have Shape tracing enabled so it should help me tremendously. I was wrong, I obviously didn't understand one bit and in addition Shape tracing didn't help me as it wasn't working as I hoped.

Templates

So I create the Menu.cshtml template in my theme, I add a nav element with id that I need and it works fine enough, it appears correctly on the site. So next inline is MenuItem.cshtml, here I start getting confused. Lets break it down.

if (!HasText(Model.Text)) {
    @DisplayChildren(Model)
}

So I don't really know what the model here is, the children of the Menu shape I guess based on their usage of @DisplayChildren(Model) in the Menu.cshtml but if there is no Text element just display the children. Well I'm not sure what this is for, some nameless dropdowns or something. Don't believe this has been relevant to my discoveries.

if ((bool) Model.Selected) {
    Model.Classes.Add("current");
}

if (items.Any()) {
    Model.Classes.Add("dropdown");
}

This is basic, adding classes when relevant, I guess MenuItem can have MenuItem children for a nested navigation (using dropdown in this case).

@* morphing the shape to keep Model untouched*@
Model.Metadata.Alternates.Clear();
Model.Metadata.Type = "MenuItemLink";

Wait a second, what is going on here? Manually changing the metadata into a MenuItemLink and clearing alternates, Does MenuItemLink use the same model as MenuItem?

@* render the menu item only if it has some content *@
var renderedMenuItemLink = Display(Model);
if (HasText(renderedMenuItemLink)) {
    var tag = Tag(Model, "li");
    @tag.StartElement
    @renderedMenuItemLink

    if (items.Any()) {
        <ul>
            @DisplayChildren(Model)
        </ul>
    }

    @tag.EndElement
}

The call to Display(Model) here, does it base its template on the type in the metadata, so because he change it earlier now this will result in the MenuItemLink template being used? Again assuring the item has text, which if it is the same model we can be assured of, or is it checking the result of the display, actually seeing if MenuItemLink template gave anything back?

Perhaps I'm understanding it better than I thought, or perhaps I'm even more way off than I originally thought. Why would we have a seperate shape for MenuItem and MenuItemLink if it is essentially the same Model. I can understand wanting to manipulate it seperately, but In my instance for example I want to add a class to the MenuItemLink anchor element but if I use the TagBuilder I end up with all the classes defined on the parent MenuItem.

Does this mean that MenuItemLink is never a concrete class in itself, just a name given to it in the MenuItem.cshtml definition? It has no ContentPart or whatever it is called?

Shape Tracing

Here is the other problem I had with these Navigation shapes and templates. At first I thought both Menu and MenuItem were missing, but as I realize now the MenuItems shapes have been converted to MenuItemLink I guess I get that part, but the Menu shape seems to be missing in the shape tree in the tracer, it goes

  • Zone [Navigation]
    • Widget
      • Menu (this is a Parts_MenuWidget shape) * MenuItemLink * MenuItemLink

Now, it seems obvious that the part there named Menu is the Menu Shape, the HTML fits, but the shape name, and active template just doesn't fit at all.

Key | Value --------------------|-------------------- Shape |Parts_MenuWidget Active Template |~/Core/Navigation/Views/Parts.MenuWidget.cshtml Display Type |Detail

Also the second MenuItemLink has childrem why aren't they visable in the tracer?

[^1]: I created a theme on a local git clone of the Orchard project, should I just project include the new theme inside the project?

Re: Help understand Shapes and Templates in Navigation.

You understood well. MenuItemLink.cshtmland MenuItem.cshtml use the same model. But MenuItemLink is intended to render an individual item without rendering its childs, this is the job of MenuItem which is executed recursively through DisplayChildren(Model).

To render each individual item, MenuItem uses shape morphing Model.Metadata.Type = "MenuItemLink". Indeed, the additional test is to check the result of the Display(Model), e.g you may have overridden the MenuItemLink template with some other logic.

Indeed it's correct to have the Parts_MenuWidget.cshtml template which do Display(Model.Menu), you can try to overridde it.

For the missing templates in shape tracing you're right, it's because of the shape morphing. Under debugger, in ShapeTracingFactory.cs i can see all templates, but MenuItem and MenuItemLink, as you said, are related to the same model / shape, so the last wins. Need more investigation for the missing Menu.cshtml alternate itself.

Best.

Saturday, August 20, 2016 6:39:39 AM byjtkech
  • jtkech
  • Lv. 13 Rookie
  • Total EXP: 1028

Post a reply

You need to be signed in to post a reply.

Sign In