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