Say I have this template:
<div class="main__container">
<ng-template #test></ng-template>
<ng-template #test2></ng-template>
</div>
And I retrieve a reference to all TemplateRef
via:
@ViewChildren(TemplateRef)
private templates;
How can I, for all of them, retrieve the name of the associated variable? So "test" for the first template and "test2" for the other one. I don't absolutely want to use variables, if it's possible by using an attribute of ng-template
, it's OK. I tried with TemplateRef
, ViewContainerRef
and ElementRef
but everytime, it returns a reference to a comment
element.
To get started using template reference variables, simply create a new Angular component or visit an existing one. To create a template reference variable, locate the HTML element that you want to reference and then tag it like so: #myVarName .
ElementRef refers to an element of the DOM, whereas TemplateRef represents an embedded template (usually a component template). So to summarize, the template ref can contain several element refs, but the element ref can not contain a template ref.
TemplateReflinkRepresents an embedded template that can be used to instantiate embedded views. To instantiate embedded views based on a template, use the ViewContainerRef method createEmbeddedView() .
A template reference variable is often a reference to a DOM element within a template. It can also refer to a directive (which contains a component), an element, TemplateRef, or a web component.
I managed to do that using TemplateRef's private API. Here's the function I use at the moment :
@ContentChildren(TemplateRef) templates: QueryList<TemplateRef<any>>;
getTemplate(id: string): TemplateRef<any> {
// FIXME: Should not use private API...
return this.templates ? this.templates.find((template: any) =>
template._def.references[id]) : null;
}
But this doesn't work with a production build, actually I found your post looking for another solution, I'll update if I find something or submit a feature request to Angular github. In the meantime I thought it would be worth sharing.
-- EDIT -- It really looks like there is a possible solution using a structural directive.
You'll learn in this guide that the asterisk (*) is a convenience notation and the string is a microsyntax rather than the usual template expression. Angular desugars this notation into a marked-up
<ng-template>
that surrounds the host element and its descendents. Each structural directive does something different with that template.
Link to documentation
-- EDIT 2 -- Ok it works with a directive that retrieve the TemplateRef, but for the moment I register it to a service using an id, I don't use ViewChildren/ContentChildren anymore. My Component which use *ngTemplateOutlet directive then retrieve the TemplateRef using the service.
Here's the directive source code :
@Directive({
selector: '[formeTemplate]'
})
export class FormeTemplateDirective {
@Input('formeTemplate') templateId;
constructor(private host: TemplateRef<any>, private service: FormeTemplateService) { }
ngOnInit() {
this.service.register(this.templateId, this.host);
}
}
The service :
@Injectable()
export class FormeTemplateService {
private templates: Array<FormeTemplate>;
constructor() {
this.templates = new Array();
}
register(id: string, ref: TemplateRef<any>) {
this.templates.push(new FormeTemplate(id, ref));
}
getTemplateRef(templateId: string) : TemplateRef<any> {
let template = this.templates.find((template) => template.id === templateId);
return template ? template.ref : null;
}
}
I also had to be able to set templateRef dynamically. The goal is to choose a template based on a dynamic type.
My solution (https://stackblitz.com/edit/angular-dynamic-templateref):
@Directive({
selector: "ng-template.[typeTemplate]"
})
export class TypeTemplateDirective {
@Input()
typeTemplate: string;
constructor(public templateRef: TemplateRef<any>) {}
}
@Component({
selector: "type-template",
template: `
<ng-container *ngTemplateOutlet="(template$ | async)"></ng-container>
<ng-template typeTemplate="typeA">Template for type A</ng-template>
<ng-template typeTemplate="typeB">Template for type B</ng-template>
`
})
export class TypeTemplateComponent implements AfterViewInit {
@ViewChildren(TypeTemplateDirective)
private typeTemplateDirectives: QueryList<TypeTemplateDirective>;
@Input()
set type(newType: string) {
this.type$.next(newType);
}
type$ = new BehaviorSubject<string>(null);
template$ = this.type$.pipe(
filter(() => !!this.typeTemplateDirectives),
map(
type =>
this.typeTemplateDirectives.find(
directive => directive.typeTemplate === type
).templateRef
)
);
ngAfterViewInit(): void {
this.type$.next(this.type$.getValue());
}
}
So when a new type is pushed to , the correct template will be used.
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