1

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>
          `
     })
mruanova
  • 6,351
  • 6
  • 37
  • 55
Shay
  • 407
  • 2
  • 8
  • 15

2 Answers2

1

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>
andzep
  • 1,877
  • 24
  • 35
0

@andzep Hmm I have tried it and seems like it doesnt work =/

in my main html:

<my-own-input type="dropdown">

    <ng-template #selectedItem>
        <div>
            {{ someService.selectedCloth.controls.cloth.value?.clothName }}
        </div>
    </ng-template>
    <ng-template #item let-cloth>
        <div>{{ cloth.clothName }}</div>
     </ng-template>

</my-own-input>

in my-own-input html

<p-dropdown styleClass="w-100"
            [inputId]="..."
            [name]="..."
            [options]="options"
            [optionLabel]="optionLabel"
            [(ngModel)]="dropdownValue"
            [placeholder]="placeholder"
            [showClear]="showClear"
            *ngIf="type === 'dropdown'">
    <ng-template 
        let-outerContext 
        *ngIf="selectedItem" 
        pTemplate="selectedItem">
        <ng-container
            *ngTemplateOutlet="selectedItem; context: {$implicit: outerContext}">
        </ng-container>
    </ng-template>
    <ng-template let-outerContext *ngIf="item" pTemplate="item">
        <ng-container
            *ngTemplateOutlet="item; context: {$implicit: outerContext}">
        </ng-container>
    </ng-template>
</p-dropdown>

in my-own-input ts

@ContentChild('selectedItem') selectedItem: TemplateRef<ElementRef> | undefined;
@ContentChild('item') item: TemplateRef<ElementRef> | undefined;
Emiliorth
  • 23
  • 3