Nesting Angular Directives

I've been learning how to write directives, and it's really tough to grok how to do it. This blog post's been rewritten three times so I don't come off as totally ignorant about the "right way" or "the Angular way" to do things.

What I'm trying to do it implement a menu display with lightweight, role-based control over what is displayed.

My first iteration was done entirely with controllers, HTML, and jQuery and plain Javascript. That was clearly not the Angular way, so I revised it a little bit - CSS animations, and let's use directives.

The second iteration was a directive that used a data structure that contained the menu as well as the roles that could see each item. The HTML template got a lot leaner. The directives were even hierarchical - one directive was used within another directive's templates. That was better, because it allowed for individual handlers on each button. There was no more jQuery or DOM manipulation in the controller. Recursive menu trees are built using $compile rather than the 'template' property. However, it's still not quite right.

For one thing, I was still generating HTML code within the link function or controllers.

The third iteration is going to follow the lead of Angular-UI, and use more HTML for the configuration. The configuration object will go away. This is a significant difference, because the logic for the role-based display, and the logic for the menu labels and linking, will be decoupled.

In the old code there was a bit of code that looked like:

if (item.roles.contains(user.role)) {
  ... show the item ...
} else {
  ... don't show it...
}

There's nothing really wrong with that - but you can make HTML like this:

<ebu-menu-item ebu-role-matches="Admin User Provider" url="foo"/>Label</ebu-menu-item>

Yes, it's a little more verbose than JSON or YAML. It's basically the same size as XML.

The big difference is that the logic for the two different directives is disaggregated and function independently of each other. That's a big win for re-usability, testability, and separation of concerns. It's also just a little like aspect-oriented programming.

In the HTML code, we can now pass expressions in as values. Expressions can include function references, and that means we can write controllers for menus to fill up the content of a menu. In the old system, I was going to add a "loader" property that would be resolved to a function, which would be called to fill the menu. That's no longer necessary.

Object Construction and Composition

It gets easier if you think of HTML as an object construction and composition language. You're instantiating objects, and mixing in different behaviors via directives on attributes. So, you no longer need to either fetch a JSON or YAML or XML file and deserialize it to objects, and you don't need to instantiate any objects within the code. You do it in HTML, and let the browser create objects.

The slight difference from specifying things in code is that the HTML is built after the JS code is loaded, so you don't have to deal with forward references to methods. Everything already exists, so references to functions are always available. But that's not a big deal - a bigger feature is that you can bind controllers to blocks of HTML, but also mix in behavior from other directives. It doesn't have any kind of "special call" or weird syntax -- it's just HTML with Angular-style extensions, and not really much more syntax.

It's even better than traditional coding, because directives will behave the same across different HTML objects. They only touch the CSS and maybe insert some of their own HTML markup, and attach a controller to that. In traditional coding, you'd add methods to multiple disparate objects, and perhaps alter the logic and effect for each implementation; it's like implementing an interface in Java. With Angular-style HTML, you just have the one implementation, and the behavior across elements is the same; it's less flexible, but easier for all involved.

Update - arguments for using markup

So, I kept studying the Angular-UI Boostrap code, which seems really good. I'm learning a lot from it. The design principles look really nice (and influenced the above writing). So, I went looking for a tree view control... and did't find anything I could use for my purposes of making dynamic menus.

I came across an interesting article that reinforces some feelings I expressed the last section: Trees in AngularJS by stackfull.