Dan
Dan

Reputation: 204

Passing through ng-content to ng-template element in child component doesn't bind to properties

I have a component called select.component (selector: 'app-select') which searches for users using typeahead (I left this out for simplicity). I then want to show the name and employeeId by binding to item in ng-template.

<ng-select>
  <ng-template ng-option-tmp let-item="item">
    {{item.name}}</br>
    {{item.employeeId}}
  </ng-template
</ng-select>

As we use many of these ng-select elements in the app, I have made select.component a reusable component which other parent components use. I therefore want to make the ng-template part dynamic and have it passed in from the parent component. I have changed select.component to look like this instead:

<ng-select>
  <ng-template ng-option-tmp let-item="item">
    <ng-content></ng-content>
  </ng-template
</ng-select>

I then have user.component which uses content projection to populate ng-template in the select.component like so:

<app-select>
  {{item.name}}</br>
  {{item.employeeId}}
</app-select>

The problem here is that by the time content projection has passed the inner html of app-select to ng-template, {{item.name}} and {{item.employeeId}} are not evaluated within the ng-template. In the dropdown of items, I see {{item.name}}, rather than the actual name value.

How could I best achieve this? I really need parent components to be able to specify how the dropdown items should appear to make select.component fully reusable.

See my stackblitz

Upvotes: 4

Views: 4159

Answers (3)

Scott Ayers
Scott Ayers

Reputation: 143

I agree it is helpful to encapsulate common setup in a component and allow for the rest of the setup to be provided by the consumer of the component.

NgSelect uses @ContentChild() to get templates provided within the component tag when the parent is providing them. I suspect <ng-content> is not yet available because the ngAfterContentInit lifecycle hook is called before ngAfterViewInit.

Another approach would be to accept a TemplateRef as an input to your <app-select> and set NgSelect's optionTemplate manually, after the view has initialized:

export class SelectComponent implements AfterViewInit {
  @Input() items: any[];
  @Input() optionTemplate: TemplateRef<any>; // The options template to provide to ng-select

  @ViewChild(NgSelectComponent) ngSelect: NgSelectComponent; // A reference to the ng-select component

  // Once @ViewChild is defined after view init
  ngAfterViewInit() {
    if (this.optionTemplate) {
      // Manually assign ng-select's optionTemplate from the input
      this.ngSelect.optionTemplate = this.optionTemplate;
    }
  }
}

Then you can pass in a template you can reference via a reference variable:

<app-select [items]="items" [optionTemplate]="appSelectOption">
</app-select>

<ng-template #appSelectOption let-item="item">
  {{item.name}}<br/>
  {{item.employeeId}}
</ng-template>

This way you can keep the template outlet context (let-item="item") in the same area where you are using the variable.

See a fork of your StackBlitz

Upvotes: 1

Chellappan வ
Chellappan வ

Reputation: 27293

You need to pass the code in component and catch in select component

<app-select>
  <ng-select [items]="items" bindLabel="name" placeholder="Works">
    <ng-template ng-option-tmp let-item="item">
        {{item.name}}<br/>
        {{item.employeeId}}
  </ng-template>
</ng-select>
</app-select>

Then you need to use

<ng-content></ng-content>

in your select component

This way you will be able to achive what you want.

Upvotes: 1

baj9032
baj9032

Reputation: 2582

ng-template is never ever work without any stucture directive or template ref variable. you can use ng-container instead of ng-template.sorry any thing is missing coz i have answered this question from my cell phone

Upvotes: 0

Related Questions