Angular 2 TemplateRef Usage

Mike BerrymanAngular 2 is the hot new thing, but like any other new technology out there, documentation and examples are really sparse and there’s a lot of head scratching trying to figure out why things that seem like they should work…aren’t.

I spent some time recently working with TemplateRefs in Angular 2, trying to access templates defined via a <template> tag in my HTML from the component code.

 Here’s what my component and html originally looked like:

&amp;lt;div id=&amp;quot;my-container&amp;quot;&amp;gt;
  &amp;lt;template #tmplMyTemplate&amp;gt;
    &amp;lt;div (click)=&amp;quot;templateClick()&amp;quot;&amp;gt;I'm a custom template!&amp;lt;/div&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/div&amp;gt;
import { Component, ViewChild, TemplateRef, ViewContainerRef } from '@angular/core';

@Component({
  selector: 'my-selector',
  templateUrl: './my-component.component.html',
})
export class MyComponent {
  @ViewChild(&amp;quot;tmplMyTemplate&amp;quot;) tmplMyTemplate: TemplateRef&amp;lt;any&amp;gt;;

  constructor(private vcRef: ViewContainerRef) {
    this.vcRef.createEmbeddedView(this.tmplMyTemplate);
  }

  templateClick() {
    alert(&amp;quot;Success!&amp;quot;);
  }
}

I’m trying to get and use a TemplateRef to my <template> when the component is instantiated.  However when this is ran, the TemplateRef is always undefined and thus an error is thrown when I attempt to use it.  I know using the @ViewChild() decorator allows the component to fetch a template from the DOM (it can fetch other component/directives used inside the parent component as well) and assign it to a variable for use later in the code.  But it wasn’t working!

As is usually the case when things look syntactically correct but are still failing, the problem ended up being one of timing.  I dug through the Angular documentation for the component lifecycle and was able to piece together that at the time I’m trying to use the TemplateRef in my code (instantiation), the TemplateRefs haven’t yet been loaded.  Obvious once I realized it, but such is the way of programming.

Reading the lifecycle documentation, you’d think that ngAfterViewInit is when all the child views of the component are loaded and thus accessible, and while that is true I found in my testing that my templates were loaded by the time ngOnInit fires.  In most cases either method is probably going to be sufficient for whatever your code is trying to do with the TemplateRef (YMMV of course).

Here’s what my component looked like after adjustment:

import { Component, OnInit, ViewChild, TemplateRef, ViewContainerRef } from '@angular/core';

@Component({
  selector: 'my-selector',
  templateUrl: './my-component.component.html',
})
export class MyComponent implements OnInit {
  @ViewChild(&amp;quot;tmplMyTemplate&amp;quot;) tmplMyTemplate: TemplateRef&amp;lt;any&amp;gt;;

  constructor(private vcRef: ViewContainerRef) {
    // tmplMyTemplate is not yet loaded here
    //this.vcRef.createEmbeddedView(this.tmplMyTemplate);
  }

  ngOnInit() {
    this.vcRef.createEmbeddedView(this.tmplMyTemplate);
  }

  templateClick() {
    alert(&amp;quot;Success!&amp;quot;);
  }
}

And it works like a charm!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s