Using An Item Template With An HTML Dropdown Menu Component In Angular 2 RC 3
A while ago, I played around with trying to create an HTML Dropdown menu component in Angular 2. This was quite a non-trivial task, but very worthwhile. Recently, however, I discovered that you could pass Template references into components for dynamic rendering. And, while not exactly the same thing, I wanted to see if the use of a TemplateRef could significantly decrease the complexity of a custom HTML dropdown component in Angular 2 RC 3.
The idea behind a custom HTML dropdown menu component is that the rendering of each item can be controlled by the calling context. In my previous HTML downdown post, I did this by nesting Item components inside the Dropdown component. In that approach, each Item component had to inject and then communicate with the parent Dropdown component. This worked, but required intricate communication, management of the component life-cycle, and imperative references to the HTML markup.
If we could replace that nested Item component with a simple TemplateRef, it could remove a lot of complexity around that communication and component life-cycle management. Not to mention that we might be able to remove any imperative manipulation of the HTML. To explore this idea, I put together a simple HTML dropdown menu that looks for a TemplateRef in its content children:
Notice that we are passing in the [value] and [items] input properties to the HtmlDropdownComponent; but, that we are also providing a TemplateRef as the definition of how to render each item - in this case, with an avatar and a name. From a component consumption standpoint, this is really close to where we want to be, at least for a simple HTML dropdown menu.
Once the HtmlDropdownComponent has a reference to this item TemplateRef, it can use it to render both the options in the item collection as well as the selected option in the dropdown menu root. The fact that it can use the TemplateRef to render both of these means that we no longer have to worry about cloning HTML, which is a huge win!
This isn't a fully-featured dropdown menu; but, for the brevity of the code, it actually accomplishes quite a bit. Notice that we are gathering the TemplateRef through a ChildContent query. Then, we use that TemplateRef to render both the items and the menu root. This makes the entire view declarative - no manually manipulating the HTML; no cloning of LI item content into the root. The entire state of the view is managed directly by Angular.
There's a lot of dropdown menu functionality that we're not implementing for the sake of simplicity. But, when we run this code, you can see the items being rendered based on the template:
As you can see, both the dropdown menu root as well as the dropdown menu items are being rendered by the TemplateRef that was supplied by the calling context.
I'm rather enamored with this idea of being able to pass TemplateRefs around in Angular 2. I think it can really simplify some solutions. Plus, the TemplateRefs have the added benefit of not existing until they are rendered. In comparison, ng-content components exist regardless of whether or not they are being rendered in the DOM (Document Object Model). So, to some degree, TemplateRef may have less overhead. Definitely a topic worth noodling on.