0
# Template Customization
1
2
Angular ng-select provides a comprehensive template directive system allowing complete customization of every visual aspect of the select component. These directives enable developers to create custom layouts, styling, and behavior for options, labels, headers, footers, and various UI states.
3
4
## Capabilities
5
6
### Item Display Templates
7
8
Template directives for customizing how items and selections are displayed.
9
10
```typescript { .api }
11
/**
12
* Template directive for customizing individual option display in dropdown
13
* Access to: item, item.label, item.value, item.disabled, index, searchTerm
14
*/
15
@Directive({ selector: '[ng-option-tmp]' })
16
export class NgOptionTemplateDirective {
17
constructor(public template: TemplateRef<any>) {}
18
}
19
20
/**
21
* Template directive for customizing selected item label display
22
* Access to: item, clear function, item.label, item.value
23
*/
24
@Directive({ selector: '[ng-label-tmp]' })
25
export class NgLabelTemplateDirective {
26
constructor(public template: TemplateRef<any>) {}
27
}
28
29
/**
30
* Template directive for customizing multi-select label display
31
* Access to: items array, clear function
32
*/
33
@Directive({ selector: '[ng-multi-label-tmp]' })
34
export class NgMultiLabelTemplateDirective {
35
constructor(public template: TemplateRef<any>) {}
36
}
37
38
/**
39
* Directive for displaying and styling item labels with automatic HTML escaping
40
* Alternative to using bindLabel property - provides more control over display
41
*/
42
@Directive({ selector: '[ngItemLabel]' })
43
export class NgItemLabelDirective implements OnChanges {
44
/** The label text to display */
45
@Input() ngItemLabel: string;
46
/** Whether to escape HTML in the label (default: true) */
47
@Input() escape: boolean = true;
48
49
constructor(private element: ElementRef<HTMLElement>) {}
50
51
ngOnChanges(changes: SimpleChanges): void;
52
}
53
54
/**
55
* Template directive for customizing option group display
56
* Access to: item (group), item.label, item.children
57
*/
58
@Directive({ selector: '[ng-optgroup-tmp]' })
59
export class NgOptgroupTemplateDirective {
60
constructor(public template: TemplateRef<any>) {}
61
}
62
63
/**
64
* Template directive for customizing tag display when addTag is enabled
65
* Access to: searchTerm, item
66
*/
67
@Directive({ selector: '[ng-tag-tmp]' })
68
export class NgTagTemplateDirective {
69
constructor(public template: TemplateRef<any>) {}
70
}
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
@Component({
77
template: `
78
<!-- Custom option template with icons and descriptions -->
79
<ng-select [(ngModel)]="selectedUser" [clearable]="false">
80
<ng-option *ngFor="let user of users" [value]="user">
81
<ng-container *ngOptionTemplateDirective="let item=item; let index=index">
82
<div class="user-option">
83
<img [src]="item.avatar" width="24" height="24" class="avatar">
84
<div class="user-info">
85
<div class="name">{{ item.name }}</div>
86
<div class="email">{{ item.email }}</div>
87
</div>
88
<span class="role-badge">{{ item.role }}</span>
89
</div>
90
</ng-container>
91
</ng-option>
92
93
<!-- Custom selected label template -->
94
<ng-label-tmp ng-label-tmp let-item="item" let-clear="clear">
95
<img [src]="item.avatar" width="18" height="18">
96
{{ item.name }}
97
<span class="clear-btn" (click)="clear(item)">×</span>
98
</ng-label-tmp>
99
</ng-select>
100
101
<!-- Multi-select with custom multi-label -->
102
<ng-select [(ngModel)]="selectedUsers" [multiple]="true">
103
<ng-option *ngFor="let user of users" [value]="user">
104
{{ user.name }}
105
</ng-option>
106
107
<ng-multi-label-tmp ng-multi-label-tmp let-items="items" let-clear="clear">
108
<div class="multi-label">
109
<span class="selected-count">{{ items.length }} users selected</span>
110
<span class="clear-all" (click)="clear()">Clear all</span>
111
</div>
112
</ng-multi-label-tmp>
113
</ng-select>
114
`
115
})
116
export class CustomTemplatesComponent {
117
selectedUser: any;
118
selectedUsers: any[] = [];
119
users = [
120
{
121
id: 1,
122
name: 'Alice Johnson',
123
email: 'alice@example.com',
124
avatar: '/avatars/alice.jpg',
125
role: 'Admin'
126
},
127
// ... more users
128
];
129
}
130
```
131
132
### Layout Templates
133
134
Template directives for customizing the overall layout and structure of the dropdown.
135
136
```typescript { .api }
137
/**
138
* Template directive for dropdown header content
139
* Access to: searchTerm, items
140
*/
141
@Directive({ selector: '[ng-header-tmp]' })
142
export class NgHeaderTemplateDirective {
143
constructor(public template: TemplateRef<any>) {}
144
}
145
146
/**
147
* Template directive for dropdown footer content
148
* Access to: searchTerm, items
149
*/
150
@Directive({ selector: '[ng-footer-tmp]' })
151
export class NgFooterTemplateDirective {
152
constructor(public template: TemplateRef<any>) {}
153
}
154
```
155
156
**Usage Examples:**
157
158
```typescript
159
@Component({
160
template: `
161
<ng-select [(ngModel)]="selectedItem" [items]="items" bindLabel="name">
162
<!-- Custom header with search stats -->
163
<ng-header-tmp>
164
<div class="dropdown-header">
165
<h4>Select an Item</h4>
166
<small *ngIf="searchTerm">
167
Showing results for "{{ searchTerm }}"
168
</small>
169
</div>
170
</ng-header-tmp>
171
172
<!-- Custom footer with action buttons -->
173
<ng-footer-tmp>
174
<div class="dropdown-footer">
175
<button type="button" (click)="addNewItem()">Add New Item</button>
176
<button type="button" (click)="selectAll()">Select All</button>
177
</div>
178
</ng-footer-tmp>
179
</ng-select>
180
`
181
})
182
export class HeaderFooterComponent {
183
selectedItem: any;
184
searchTerm: string = '';
185
items = [/* ... */];
186
187
addNewItem() {
188
// Custom logic to add new item
189
}
190
191
selectAll() {
192
// Custom logic to select all items
193
}
194
}
195
```
196
197
### State Templates
198
199
Template directives for customizing various component states and UI elements.
200
201
```typescript { .api }
202
/**
203
* Template directive for placeholder display
204
* Access to: placeholder text
205
*/
206
@Directive({ selector: '[ng-placeholder-tmp]' })
207
export class NgPlaceholderTemplateDirective {
208
constructor(public template: TemplateRef<any>) {}
209
}
210
211
/**
212
* Template directive for "not found" message display
213
* Access to: searchTerm, notFoundText
214
*/
215
@Directive({ selector: '[ng-notfound-tmp]' })
216
export class NgNotFoundTemplateDirective {
217
constructor(public template: TemplateRef<any>) {}
218
}
219
220
/**
221
* Template directive for "type to search" message display
222
* Access to: typeToSearchText
223
*/
224
@Directive({ selector: '[ng-typetosearch-tmp]' })
225
export class NgTypeToSearchTemplateDirective {
226
constructor(public template: TemplateRef<any>) {}
227
}
228
229
/**
230
* Template directive for loading text display
231
* Access to: loadingText
232
*/
233
@Directive({ selector: '[ng-loadingtext-tmp]' })
234
export class NgLoadingTextTemplateDirective {
235
constructor(public template: TemplateRef<any>) {}
236
}
237
238
/**
239
* Template directive for loading spinner display
240
* Access to: loading state
241
*/
242
@Directive({ selector: '[ng-loadingspinner-tmp]' })
243
export class NgLoadingSpinnerTemplateDirective {
244
constructor(public template: TemplateRef<any>) {}
245
}
246
247
/**
248
* Template directive for clear button customization
249
* Access to: clear function
250
*/
251
@Directive({ selector: '[ng-clearbutton-tmp]' })
252
export class NgClearButtonTemplateDirective {
253
constructor(public template: TemplateRef<any>) {}
254
}
255
```
256
257
**Usage Examples:**
258
259
```typescript
260
@Component({
261
template: `
262
<ng-select
263
[(ngModel)]="selectedItem"
264
[items]="items"
265
bindLabel="name"
266
[loading]="isLoading"
267
[clearable]="true">
268
269
<!-- Custom placeholder with icon -->
270
<ng-placeholder-tmp>
271
<div class="custom-placeholder">
272
<i class="search-icon"></i>
273
Choose your favorite option...
274
</div>
275
</ng-placeholder-tmp>
276
277
<!-- Custom not found message -->
278
<ng-notfound-tmp ng-notfound-tmp let-searchTerm="searchTerm">
279
<div class="not-found">
280
<i class="warning-icon"></i>
281
<p>No results found for "{{ searchTerm }}"</p>
282
<button type="button" (click)="suggestAlternatives(searchTerm)">
283
Show suggestions
284
</button>
285
</div>
286
</ng-notfound-tmp>
287
288
<!-- Custom loading spinner -->
289
<ng-loadingspinner-tmp>
290
<div class="custom-loader">
291
<div class="spinner"></div>
292
<span>Loading awesome data...</span>
293
</div>
294
</ng-loadingspinner-tmp>
295
296
<!-- Custom clear button -->
297
<ng-clearbutton-tmp ng-clearbutton-tmp let-clear="clear">
298
<button type="button"
299
class="custom-clear-btn"
300
(click)="clear()"
301
title="Clear selection">
302
<i class="clear-icon"></i>
303
</button>
304
</ng-clearbutton-tmp>
305
</ng-select>
306
`
307
})
308
export class StateTemplatesComponent {
309
selectedItem: any;
310
items: any[] = [];
311
isLoading: boolean = false;
312
313
suggestAlternatives(searchTerm: string) {
314
// Custom logic for showing alternative suggestions
315
}
316
}
317
```
318
319
### Template Context Variables
320
321
Each template directive provides access to specific context variables:
322
323
```typescript { .api }
324
// Context variables available in templates
325
interface NgOptionContext {
326
$implicit: any; // The item
327
item: any; // The item (same as $implicit)
328
index: number; // Item index
329
searchTerm: string; // Current search term
330
}
331
332
interface NgLabelContext {
333
$implicit: any; // The selected item
334
item: any; // The selected item
335
clear: (item?: any) => void; // Function to clear item
336
}
337
338
interface NgMultiLabelContext {
339
items: any[]; // Array of selected items
340
clear: () => void; // Function to clear all items
341
}
342
343
interface NgNotFoundContext {
344
$implicit: string; // The search term
345
searchTerm: string; // The search term
346
notFoundText: string; // The not found text
347
}
348
349
interface NgHeaderFooterContext {
350
searchTerm: string; // Current search term
351
items: any[]; // Current filtered items
352
}
353
```
354
355
## Template Best Practices
356
357
### Performance Considerations
358
359
```typescript
360
// Good: Use OnPush change detection with templates
361
@Component({
362
changeDetection: ChangeDetectionStrategy.OnPush,
363
template: `
364
<ng-select [items]="items" bindLabel="name">
365
<ng-option-tmp ng-option-tmp let-item="item">
366
<div class="option">{{ item.name }}</div>
367
</ng-option-tmp>
368
</ng-select>
369
`
370
})
371
export class OptimizedTemplateComponent {}
372
373
// Good: Use trackBy for better performance with large lists
374
@Component({
375
template: `
376
<ng-select [items]="items" [trackByFn]="trackByFn">
377
<!-- templates -->
378
</ng-select>
379
`
380
})
381
export class TrackByComponent {
382
trackByFn = (index: number, item: any) => item.id;
383
}
384
```
385
386
### Accessibility
387
388
```typescript
389
@Component({
390
template: `
391
<ng-select
392
[items]="items"
393
bindLabel="name"
394
ariaLabel="Select user"
395
labelForId="user-select">
396
397
<ng-option-tmp ng-option-tmp let-item="item" let-index="index">
398
<div
399
role="option"
400
[attr.aria-label]="item.name + ', ' + item.role"
401
[attr.aria-describedby]="'user-desc-' + index">
402
{{ item.name }} - {{ item.role }}
403
</div>
404
</ng-option-tmp>
405
</ng-select>
406
`
407
})
408
export class AccessibleTemplateComponent {}
409
```