1
votes

I can't find how to use nested ng-template in angular 5.2.

I have a component in my app using PrimeNG dropdown:

@Component({
  selector: 'app-dropdown',
  template: `
    <p-dropdown [options]="options" [(ngModel)]="selected">
         <ng-template let-item pTemplate="selectedItem">
            <span>{{item.label | translate}}</span>
         </ng-template>
         <ng-template let-item pTemplate="item">
            <span>{{item.label | translate}}</span>
         </ng-template>
    </p-dropdown>
  `
})

I need to wrap it in another component. Something like this:

@Component({
      selector: 'app-dropdown-wrapper',
      template: `
        <label>my label</label>
        <app-dropdown [options]='options' [selectedItem]='selectedItem'></app-dropdown>
      `
    })

The problem is I don't know how to pass in the 'selectedItem' template when using the wrapper component:

 @Component({
     selector: 'app-main',
     template: `
        <app-dropdown-wrapper [options]='options'>
           <ng-template let-item pTemplate="selectedItem">
               <span>{{item.label | translate}}</span>
           </ng-template>
        </app-dropdown-wrapper>
          `
     })
1

1 Answers

1
votes

I also struggled with the same problem until I finally found a way to do it.

The solution is to pass the template reference as a @ContentChild to your wrapper-component, and then output the reference to a container inside the autocomplete's template.

Here is an example:

On your wrapper component (my-wrapper.component.ts) declare a @ContentChild

/**
 * Pass a template for the autocomplete in this component
 * 
 * @usage
 * Can be passed inside the body of this component as:
 *  <app-my-wrapper ...>
 *      <ng-template ... #item let-obj>...</ng-template>
 *  </app-my-wrapper>
 */
@ContentChild('item') item: TemplateRef<ElementRef>;

Notice, that item (when calling @ContentChild('item') is the name of the TemplateRef you're passing. So on the outside, it must be #item ... or whatever you like, just make sure the names you use match.

The next challenge I had, was to figure out how to pass the context from the autocomplete's wrapping template to my inner template in the outlet. So, on the example below, I'm using a template variable on the outer template named outerContext that I want to pass to the inner template through the *ngTemplateOutlet's context.

The trick was to pass the context as $implicit which automatically will be set into any variable declared on the outer template, in this example it's obj.

Also notice, that I'm conditioning to use ng-template only if item is not undefined (which means a template referece was passed to my wrapper).

So the component's template my-wrapper.component.html should look like this:

<p-autoComplete ...>
    <ng-template let-outerContext *ngIf="item" pTemplate="item">
        <ng-container
            *ngTemplateOutlet="item; context: {$implicit: outerContext}">
        </ng-container>
    </ng-template>
</p-autoComplete>

And that's it! Now you can just use it like:

<app-my-wrapper ... >
    <ng-template #item let-obj>
        <span>{{obj.label | translate}}</span>
        (<em>{{obj.name}}</em>)
    </ng-template>
</app-my-wrapper>