I am making a re-usable Angular2 component and I want the user to be able to specify a template string or templateUrl which the component will then use, either by an attribute or by setting it via some service method.
// somewhere else in app
myService.setTemplateUrl('path/to/template.html');
// directive definition
function myDirective(myService) {
  return {
    template: function(element, attrs) {
      return attrs.templateUrl || myService.getTemplateUrl();
    }
    // ...
  };
}
@Component({
  selector: 'my-component',
  template: '...' // cannot see `mySerivce` from here, nor access the element attributes
})
export class MyComponent {
  constructor(private myService: MyService) {}
}
While my issue specifically relates to how to implement a dynamic template, the broader question is whether it is possible to access the injected dependency instances from the various decorators.
So I finally figured out a way to do what I wanted with custom templates.
I think the answer to the actual question must be no, injectables are not available in a decorator. This is per my understanding of the life cycle of an Angular 2 component.
For those interested, here is what I came up with for implementing user-defined custom templates:
Given a directive, SimpleTimer, we can provide a custom template like this:
<!-- app.ts template -->
<div *simpleTimer="#timer=timerApi">
  <div class="time">{{ timer.getTime() }}</div>
  <div class="controls">
    <button (click)="timer.toggle()">Toggle</button>
    <button (click)="timer.reset()">Reset</button>
  </div>
</div>
And then make use of the TemplateRef and ViewContainerRef injectables like this:
// SimpleTimer.ts
constructor(private templateRef: TemplateRef,
          private viewContainer: ViewContainerRef,
          private cdr: ChangeDetectorRef) {}
ngOnInit() {
  // we need to detach the change detector initially, to prevent a
  // "changed after checked" error.
  this.cdr.detach();
}
ngAfterViewInit() {
  let view = this.viewContainer.createEmbeddedView(this.templateRef);
  let api = {
    toggle: () => this.toggle(),
    reset: () => this.reset(),
    getTime: () => this.getTime()
  }
  view.setLocal('timerApi', api);
  setTimeout(() => this.cdr.reattach());
}
For a walk-through of how and why this works, please see this blog post I wrote up on the topic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With