Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve the name of variable associated with TemplateRef

Tags:

angular

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.

like image 531
ssougnez Avatar asked Mar 03 '17 14:03

ssougnez


People also ask

How do I get the template reference variable in component?

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 .

What is the difference between ElementRef and TemplateRef?

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.

What is TemplateRef?

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() .

What is a template reference variable?

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.


2 Answers

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;
    }
}
like image 188
Elvynia Avatar answered Oct 14 '22 05:10

Elvynia


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.

like image 34
JoG Avatar answered Oct 14 '22 07:10

JoG