3

Im trying to select templates dynamically in a view simply by referencing them with the traditional "#" character of Angular 2. In my project i handle errors and display them to a user, I have a dialog component which should have its content html based dynamically injected, so thats why im using templates.

I read some articles which shows a way of how to do it when i already know the name of the template reference in my case i dont know the name of the reference i get the name on runtime. I followed this guide in particular: https://www.bennadel.com/blog/3101-experimenting-with-dynamic-template-rendering-in-angular-2-rc-1.htm

So currently my dialog component has the following view:

<template #err_1 let-property1="p1" let-property2="p2">
property1: {{p1}}
property2: {{p2}}

</template>

<template #err_2 let-property1="p1" let-property2="p2">
<p *ngIf="p1">{{p1}}</p>
property2: {{p2}}
</template>

<!--The code for the template directive i took from the guide in the link above-->
<tem [render]="templateRef"
     [context]="context">
</tem>

In my dialog.ts I have the following code:

@Component({
  selector: 'error-dialog',
  queries: {
    templateRef: new ViewChild("err_1")
  },
  templateUrl: './dialog.html'
})
...

"TemplateRendererDirective" directive source i took from their guide from the link above. pay attention that what confused me: templateRef basically gets an object of: "ViewChild" even though the directive eventually gets TemplateRef instance, how this is possible?

so only if i know which error template i want to render for example: "err_1" i just referencing it beforehand in dialog.ts, but its not the case, i want dynamically tell i want to render "err_1", "err_2" etc.. and give the context (which is the object to fill that template with data - p1, p2 for the example, also dynamically)

Is it possible to do it?

9
  • What do you exactly want? Change templateRef param? Commented Oct 30, 2016 at 5:27
  • Dynamically set templateRef param not when I declare the dialog component like I showed in the post. But how can I create TemplateRef from ViewChild object? I need templateRef object because the function createEmbededView that inject the content to the view expect to get templateRef Commented Oct 30, 2016 at 5:31
  • ViewChild grabs TemplateRef automatically if your err_1 hash relates to <template> Commented Oct 30, 2016 at 5:41
  • It relates to template element so how can I get the templateRef object from ViewChild? when I try simply casting the typescript compiler gives me an error Commented Oct 30, 2016 at 5:43
  • Add please code how it looks Commented Oct 30, 2016 at 5:44

1 Answer 1

5

As i mentioned in comments you can try to use @ViewChildren to do it working. But i use additional directive TemplateNameDirective to manipulate template's name.

Here's how it looks:

@Directive({
  'selector': 'template[name]'
})
export class TemplateNameDirective {
  @Input() name: string;
  constructor(public templateRef: TemplateRef<any>) {}
}

@Component({
  selector: 'error-dialog',
  template: `
    <template name="err_1" let-item>
      property1: {{item.p1}}
      property2: {{item.p2}}
    </template>

    <template name="err_2" let-item>
      <p *ngIf="item.p1">{{item.p1}}</p>
      property2: {{item.p2}}
    </template>

    <!-- 
       I use ngTemplateOutlet directive 
       https://angular.io/docs/ts/latest/api/common/index/NgTemplateOutlet-directive.html 
    -->
    <template [ngTemplateOutlet]="templateRef" [ngOutletContext]="{ $implicit: context }">
    </template>
  `
})
export class ErrorDialogComponent {
  @ViewChildren(TemplateNameDirective) children: QueryList<TemplateNameDirective>;

  templateRef: TemplateRef<any>;
  context: any;

  public setContent(errTemplateName, context) {
    this.templateRef = this.children.toArray()
      .find(x => x.name === errTemplateName).templateRef; 
    this.context = context;
  }
}

Parent view:

<error-dialog #dialogRef></error-dialog>
<button (click)="dialogRef.setContent('err_1', { p1: 'test'})">Test err_1</button>
<button (click)="dialogRef.setContent('err_2', { p2: 'test2'})">Test err_2</button>

Plunker Example

Notice: i am passing ngOutletContext like object with $implicit property.

using the key $implicit in the context object will set it's value as default.

It works as follows:

[ngOutletContext]="{ $implicit: row }"  ==> <template let-row>

[ngOutletContext]="{ item: row }"       ==> <template let-row="item">
Sign up to request clarification or add additional context in comments.

7 Comments

Unfortunately it doesnt work, this.children is undefined for some reason, i put "name" attribute in template element but it doesnt work for me.
You have to add TemplateNameDirective to declarations array of '@NgModule`
Can you reproduce it on a plunker?
Your example works as a self independent component, but im using Material 2 dialog component as the guide shows here: github.com/angular/material2/tree/master/src/lib/dialog as u can see I open the dialog and gives the name of the component as an argument when i do so, children property remains undefined for a reason.
Can you improve this plunker plnkr.co/edit/6o8UrUwfLIEHBUa3IaBB?p=preview to reproduce your problem?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.