Skip to content

Commit

Permalink
feat(typeahead): adding custom item template (#776)
Browse files Browse the repository at this point in the history
fixes #503, fixes #652
  • Loading branch information
Adrian Faciu authored and valorkin committed Jul 24, 2016
1 parent b24dabf commit 1356ff7
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 12 deletions.
2 changes: 2 additions & 0 deletions components/typeahead/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class TypeaheadDirective implements OnInit {
@Input() public typeaheadSingleWords:boolean = true;
@Input() public typeaheadWordDelimiters:string = ' ';
@Input() public typeaheadPhraseDelimiters:string = '\'"';
@Input() public typeaheadItemTemplate:TemplateRef<any>;

// not yet implemented
@Input() private typeaheadAppendToBody:boolean;
Expand All @@ -51,6 +52,7 @@ export class TypeaheadDirective implements OnInit {
- `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").
- `typeaheadWordDelimiters` (`?string=" "`) - should be used only in case `typeaheadSingleWords` attribute is `true`. Sets the word delimiter to break words. Defaults to space.
- `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.
- `typeaheadItemTemplate` (`?TemplateRef`) - used to specify a custom item template. Template variables exposed are called `item` and `index`;
- `typeaheadAppendToBody` (*not implemented*) (`?boolean=false`) - if `true` the typeahead popup will be appended to $body instead of the parent element
- `typeaheadEditable` (*not implemented*) (`?boolean=true`) - if `false` restrict model values to the ones selected from the popup only will be provided
- `typeaheadFocusFirst` (*not implemented*) (`?boolean=true`) - if `false` the first match automatically will not be focused as you type
Expand Down
52 changes: 41 additions & 11 deletions components/typeahead/typeahead-container.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, ElementRef, ViewEncapsulation} from '@angular/core';
import {Component, ElementRef, ViewEncapsulation, TemplateRef} from '@angular/core';
import {CORE_DIRECTIVES} from '@angular/common';
import {TypeaheadUtils} from './typeahead-utils';
import {TypeaheadDirective} from './typeahead.directive';
Expand All @@ -9,26 +9,52 @@ const TEMPLATE:any = {
[Ng2BootstrapTheme.BS4]: `
<div class="dropdown-menu"
style="display: block"
[ngStyle]="{top: top, left: left, display: display}"
(mouseleave)="focusLost()">
<a href="#"
*ngFor="let match of matches"
[ngStyle]="{top: top, left: left, display: display}"
(mouseleave)="focusLost()">
<div *ngIf="!itemTemplate">
<a href="#"
*ngFor="let match of matches"
class="dropdown-item"
(click)="selectMatch(match, $event)"
(mouseenter)="selectActive(match)"
[class.active]="isActive(match)"
[innerHtml]="hightlight(match, query)"></a>
</div>
<div *ngIf="itemTemplate">
<a href="#"
*ngFor="let match of matches; let i = index"
class="dropdown-item"
(click)="selectMatch(match, $event)"
(mouseenter)="selectActive(match)"
[class.active]="isActive(match)"
[innerHtml]="hightlight(match, query)"></a>
[class.active]="isActive(match)">
<template [ngTemplateOutlet]="itemTemplate"
[ngOutletContext]="{item: match, index: i}">
</template>
</a>
</div>
</div>
`,
[Ng2BootstrapTheme.BS3]: `
<ul class="dropdown-menu"
style="display: block"
[ngStyle]="{top: top, left: left, display: display}"
(mouseleave)="focusLost()">
<li *ngFor="let match of matches"
<li *ngFor="let match of matches; let i = index"
[class.active]="isActive(match)"
(mouseenter)="selectActive(match)">
<a href="#" (click)="selectMatch(match, $event)" tabindex="-1" [innerHtml]="hightlight(match, query)"></a>
<a href="#"
*ngIf="!itemTemplate"
(click)="selectMatch(match, $event)"
tabindex="-1"
[innerHtml]="hightlight(match, query)"></a>
<a href="#"
*ngIf="itemTemplate"
(click)="selectMatch(match, $event)"
tabindex="-1">
<template [ngTemplateOutlet]="itemTemplate"
[ngOutletContext]="{item: match, index: i}">
</template>
</a>
</li>
</ul>
`
Expand Down Expand Up @@ -57,11 +83,15 @@ export class TypeaheadContainerComponent {
Object.assign(this, options);
}

public get matches():Array<string> {
public get matches():Array<any> {
return this._matches;
}

public set matches(value:Array<string>) {
public get itemTemplate():TemplateRef<any> {
return this.parent ? this.parent.typeaheadItemTemplate : undefined;
}

public set matches(value:Array<any>) {
this._matches = value;
if (this._matches.length > 0) {
this._active = this._matches[0];
Expand Down
3 changes: 2 additions & 1 deletion components/typeahead/typeahead.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Directive, Input, Output, HostListener, EventEmitter, OnInit, ElementRef,
Directive, Input, Output, HostListener, EventEmitter, OnInit, ElementRef, TemplateRef,
Renderer, DynamicComponentLoader, ComponentRef, ReflectiveInjector, provide, ViewContainerRef
} from '@angular/core';
import {NgControl, FormControl} from '@angular/forms';
Expand Down Expand Up @@ -41,6 +41,7 @@ export class TypeaheadDirective implements OnInit {
@Input() public typeaheadSingleWords:boolean = true;
@Input() public typeaheadWordDelimiters:string = ' ';
@Input() public typeaheadPhraseDelimiters:string = '\'"';
@Input() public typeaheadItemTemplate:TemplateRef<any>;

// not yet implemented
// @Input() private typeaheadAppendToBody:boolean;
Expand Down
13 changes: 13 additions & 0 deletions demo/components/typeahead/typeahead-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ <h4>Static arrays</h4>
(typeaheadOnSelect)="typeaheadOnSelect($event)"
class="form-control">

<!-- Custom item template -->
<template #customItemTemplate let-model="item" let-index="index">
<h5>This is: {{model | json}} Index: {{ index }}</h5>
</template>
<h4>Custom item template</h4>
<pre class="card card-block card-header">Model: {{customSelected | json}}</pre>
<input [(ngModel)]="customSelected"
[typeahead]="statesComplex"
[typeaheadItemTemplate]="customItemTemplate"
[typeaheadOptionField]="'name'"
(typeaheadOnSelect)="typeaheadOnSelect($event)"
class="form-control">

<!-- Asynchronous results -->
<h4>Asynchronous results</h4>
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
Expand Down
1 change: 1 addition & 0 deletions demo/components/typeahead/typeahead-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class TypeaheadDemoComponent {
state: this.stateCtrl
});

public customSelected:string = '';
public selected:string = '';
public dataSource:Observable<any>;
public asyncSelected:string = '';
Expand Down

0 comments on commit 1356ff7

Please sign in to comment.