I want to create a component that contains an ng-select
component to which I am passing an array of complex objects and definitions of what fields to display in the dropdown and for the selected value. With ng-select
you specify the field to be displayed (bindLabel
) and define a template to display the selected value (you could display one or more fields from the object, or other HTML markup). I am able to pass the value for bindLabel
, but can't figure out how to interpolate the template.
For instance this is how I'd normally use ng-select
. In this case I am displaying two of the object's fields and some HTML (bolding the abbreviation field), and listing the abbreviation fields in the dropdown:
child component
items = [
{ name: 'United States', abbreviation: 'US' },
{ name: 'United Kingdom', abbreviation: 'UK' },
{ name: 'Canada', abbreviation: 'CA' }
];
displayField = 'abbreviation';
child template
<ng-select [items]="items"
bindLabel="displayField"
[(ngModel)]="model"
name="ngselect"
(change)=emitModelChanged()>
<ng-template ng-label-tmp let-item="item">
<b>{{item.abbreviation}}</b> - {{item.name}}
</ng-template>
</ng-select>
To configure it dynamically from a parent component I pass items
, displayField
and template
as Inputs:
parent component
selectedTemplate = '<b>{{item.name}}</b> - {{item.abbreviation}}';
parent template
<child-component [model]=model
[items]=items
[displayField]="'abbreviation'"
[template]=selectedTemplate
(update)=updateModel($event)></child-component>
child component
@Input() items;
@Input() displayField; //what field shows in dropdown options
@Input() template; // what shows for selected value in combobox
@Input() model;
@Output() update: EventEmitter<any> = new EventEmitter(); //emit back to parent
child-component template
<ng-select [items]="items"
bindLabel="{{displayField}}"
[(ngModel)]="model"
name="ngselect"
(change)=modelChanged()>
<ng-template let-item="item">
<label [innerHTML]="template"></label>
</ng-template>
</ng-select>
While the bold tag of "template" is interpreted, the data fields are not interpolated, the value displays literally as
{{item.name}} - {{item.abbreviation}}
Is it losing scope and thus not interpolating {{item.name}} to the appropriate value? The same happens when instead of the label with innerHTML I just use {{template}}. How can I prevent this from being rendered as a string?
I likewise have the same interpolation fail with a standard <select>, it renders the options as literal strings:
selectField = 'item.'+this.displayField; // (equivalent to item.abbreviation)
<select #standardSelect [(ngModel)]="model" (change)=modelChanged() >
<!-- Also getting interpolating error here. Below renders as a string "item.abbreviation" -->
<option *ngFor="let item of items" [ngValue]="item">{{selectField}}</option>
<!-- This also renders as a string -->
<!-- <option *ngFor="let item of items" [ngValue]="item" [innerHTML]="selectField"></option> -->
<!-- Hardcoded value below works -->
<!-- <option *ngFor="let item of items" [ngValue]="item">{{item.abbreviation}}</option> -->
</select>
you can path template into your component and render it with ng-container
, but you need use ContentChild
to handle template reference. for several templates use #
naming to match them with ContentChild
references. you can try as follow example
use TemplateRef to reference outer template
import { TemplateRef } from '@angular/core';
@Component({
selector: 'ng-select-accessible[displayField]',
templateUrl: './ng-select-accessible.component.html',
styleUrls: [ './ng-select-accessible.component.scss' ]
})
export class NgSelectAccessibleComponent {
@ContentChild('labelTemplate') labelTemplate: TemplateRef<any>;;
@ContentChild('optionTemplate') optionTemplate: TemplateRef<any>;;
}
use ng-container to place outer template inside of item template
<div class='styled-select' aria-hidden=true>
<ng-select [items]="items"
[placeholder]=placeholder
[(ngModel)]="model"
name="ngselect"
(change)=modelChanged()
attr.aria-label={{ariaLabel}}>
<ng-template ng-label-tmp let-item="item">
<ng-container
*ngTemplateOutlet="labelTemplate; context: { $implicit: item }">
</ng-container>
</ng-template>
<ng-template ng-option-tmp let-item="item">
<ng-container
*ngTemplateOutlet="optionTemplate; context: { $implicit: item }">
</ng-container>
</ng-template>
</ng-select>
</div>
define item view in ng-template as follow
<ng-select-accessible
[model]=model
[items]=items
[displayField]="'abbreviation'"
[placeholder]="'custom placeholder'"
(update)=updateModel($event)
[ariaLabel]="'Select a number'">
<ng-template #labelTemplate let-item>
<label>
<b>{{item.name}}</b> - {{item.abbreviation}}
</label>
</ng-template>
<ng-template #optionTemplate let-item>
<label>
<b>{{item.name}}</b>
</label>
</ng-template>
</ng-select-accessible>
i think this one is a good article about ng-template