Skip to content

Commit 1356ff7

Browse files
Adrian Faciuvalorkin
authored andcommitted
feat(typeahead): adding custom item template (#776)
fixes #503, fixes #652
1 parent b24dabf commit 1356ff7

File tree

5 files changed

+59
-12
lines changed

5 files changed

+59
-12
lines changed

components/typeahead/readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class TypeaheadDirective implements OnInit {
2626
@Input() public typeaheadSingleWords:boolean = true;
2727
@Input() public typeaheadWordDelimiters:string = ' ';
2828
@Input() public typeaheadPhraseDelimiters:string = '\'"';
29+
@Input() public typeaheadItemTemplate:TemplateRef<any>;
2930

3031
// not yet implemented
3132
@Input() private typeaheadAppendToBody:boolean;
@@ -51,6 +52,7 @@ export class TypeaheadDirective implements OnInit {
5152
- `typeaheadSingleWords` (`?boolean=true`) - break words with spaces. If `true` the text `"exact phrase" here match` would match with `match exact phrase here` but not with `phrase here exact match` (kind of "google style").
5253
- `typeaheadWordDelimiters` (`?string=" "`) - should be used only in case `typeaheadSingleWords` attribute is `true`. Sets the word delimiter to break words. Defaults to space.
5354
- `typeaheadPhraseDelimiters` (`?string="'\""`) - should be used only in case `typeaheadSingleWords` attribute is `true`. Sets the word delimiter to match exact phrase. Defaults to simple and double quotes.
55+
- `typeaheadItemTemplate` (`?TemplateRef`) - used to specify a custom item template. Template variables exposed are called `item` and `index`;
5456
- `typeaheadAppendToBody` (*not implemented*) (`?boolean=false`) - if `true` the typeahead popup will be appended to $body instead of the parent element
5557
- `typeaheadEditable` (*not implemented*) (`?boolean=true`) - if `false` restrict model values to the ones selected from the popup only will be provided
5658
- `typeaheadFocusFirst` (*not implemented*) (`?boolean=true`) - if `false` the first match automatically will not be focused as you type

components/typeahead/typeahead-container.component.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, ElementRef, ViewEncapsulation} from '@angular/core';
1+
import {Component, ElementRef, ViewEncapsulation, TemplateRef} from '@angular/core';
22
import {CORE_DIRECTIVES} from '@angular/common';
33
import {TypeaheadUtils} from './typeahead-utils';
44
import {TypeaheadDirective} from './typeahead.directive';
@@ -9,26 +9,52 @@ const TEMPLATE:any = {
99
[Ng2BootstrapTheme.BS4]: `
1010
<div class="dropdown-menu"
1111
style="display: block"
12-
[ngStyle]="{top: top, left: left, display: display}"
13-
(mouseleave)="focusLost()">
14-
<a href="#"
15-
*ngFor="let match of matches"
12+
[ngStyle]="{top: top, left: left, display: display}"
13+
(mouseleave)="focusLost()">
14+
<div *ngIf="!itemTemplate">
15+
<a href="#"
16+
*ngFor="let match of matches"
17+
class="dropdown-item"
18+
(click)="selectMatch(match, $event)"
19+
(mouseenter)="selectActive(match)"
20+
[class.active]="isActive(match)"
21+
[innerHtml]="hightlight(match, query)"></a>
22+
</div>
23+
<div *ngIf="itemTemplate">
24+
<a href="#"
25+
*ngFor="let match of matches; let i = index"
1626
class="dropdown-item"
1727
(click)="selectMatch(match, $event)"
1828
(mouseenter)="selectActive(match)"
19-
[class.active]="isActive(match)"
20-
[innerHtml]="hightlight(match, query)"></a>
29+
[class.active]="isActive(match)">
30+
<template [ngTemplateOutlet]="itemTemplate"
31+
[ngOutletContext]="{item: match, index: i}">
32+
</template>
33+
</a>
34+
</div>
2135
</div>
2236
`,
2337
[Ng2BootstrapTheme.BS3]: `
2438
<ul class="dropdown-menu"
2539
style="display: block"
2640
[ngStyle]="{top: top, left: left, display: display}"
2741
(mouseleave)="focusLost()">
28-
<li *ngFor="let match of matches"
42+
<li *ngFor="let match of matches; let i = index"
2943
[class.active]="isActive(match)"
3044
(mouseenter)="selectActive(match)">
31-
<a href="#" (click)="selectMatch(match, $event)" tabindex="-1" [innerHtml]="hightlight(match, query)"></a>
45+
<a href="#"
46+
*ngIf="!itemTemplate"
47+
(click)="selectMatch(match, $event)"
48+
tabindex="-1"
49+
[innerHtml]="hightlight(match, query)"></a>
50+
<a href="#"
51+
*ngIf="itemTemplate"
52+
(click)="selectMatch(match, $event)"
53+
tabindex="-1">
54+
<template [ngTemplateOutlet]="itemTemplate"
55+
[ngOutletContext]="{item: match, index: i}">
56+
</template>
57+
</a>
3258
</li>
3359
</ul>
3460
`
@@ -57,11 +83,15 @@ export class TypeaheadContainerComponent {
5783
Object.assign(this, options);
5884
}
5985

60-
public get matches():Array<string> {
86+
public get matches():Array<any> {
6187
return this._matches;
6288
}
6389

64-
public set matches(value:Array<string>) {
90+
public get itemTemplate():TemplateRef<any> {
91+
return this.parent ? this.parent.typeaheadItemTemplate : undefined;
92+
}
93+
94+
public set matches(value:Array<any>) {
6595
this._matches = value;
6696
if (this._matches.length > 0) {
6797
this._active = this._matches[0];

components/typeahead/typeahead.directive.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Directive, Input, Output, HostListener, EventEmitter, OnInit, ElementRef,
2+
Directive, Input, Output, HostListener, EventEmitter, OnInit, ElementRef, TemplateRef,
33
Renderer, DynamicComponentLoader, ComponentRef, ReflectiveInjector, provide, ViewContainerRef
44
} from '@angular/core';
55
import {NgControl, FormControl} from '@angular/forms';
@@ -41,6 +41,7 @@ export class TypeaheadDirective implements OnInit {
4141
@Input() public typeaheadSingleWords:boolean = true;
4242
@Input() public typeaheadWordDelimiters:string = ' ';
4343
@Input() public typeaheadPhraseDelimiters:string = '\'"';
44+
@Input() public typeaheadItemTemplate:TemplateRef<any>;
4445

4546
// not yet implemented
4647
// @Input() private typeaheadAppendToBody:boolean;

demo/components/typeahead/typeahead-demo.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ <h4>Static arrays</h4>
77
(typeaheadOnSelect)="typeaheadOnSelect($event)"
88
class="form-control">
99

10+
<!-- Custom item template -->
11+
<template #customItemTemplate let-model="item" let-index="index">
12+
<h5>This is: {{model | json}} Index: {{ index }}</h5>
13+
</template>
14+
<h4>Custom item template</h4>
15+
<pre class="card card-block card-header">Model: {{customSelected | json}}</pre>
16+
<input [(ngModel)]="customSelected"
17+
[typeahead]="statesComplex"
18+
[typeaheadItemTemplate]="customItemTemplate"
19+
[typeaheadOptionField]="'name'"
20+
(typeaheadOnSelect)="typeaheadOnSelect($event)"
21+
class="form-control">
22+
1023
<!-- Asynchronous results -->
1124
<h4>Asynchronous results</h4>
1225
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>

demo/components/typeahead/typeahead-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export class TypeaheadDemoComponent {
2121
state: this.stateCtrl
2222
});
2323

24+
public customSelected:string = '';
2425
public selected:string = '';
2526
public dataSource:Observable<any>;
2627
public asyncSelected:string = '';

0 commit comments

Comments
 (0)