Skip to content

Commit

Permalink
feat(typeahead): add config service to provide default values
Browse files Browse the repository at this point in the history
Closes #518
Closes #697
  • Loading branch information
jnizet authored and pkozlowski-opensource committed Sep 7, 2016
1 parent 414d20a commit 237b4d5
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 10 deletions.
@@ -0,0 +1,4 @@
<p>This typeahead shows a hint when the input matches because the default values have been customized.</p>

<input type="text" class="form-control" [(ngModel)]="model" [ngbTypeahead]="search" />

36 changes: 36 additions & 0 deletions demo/src/app/components/typeahead/demos/config/typeahead-config.ts
@@ -0,0 +1,36 @@
import {Component} from '@angular/core';
import {Observable} from 'rxjs/Rx';
import {NgbTypeaheadConfig} from '@ng-bootstrap/ng-bootstrap';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

const states = ['Alabama', 'Alaska', 'American Samoa', 'Arizona', 'Arkansas', 'California', 'Colorado',
'Connecticut', 'Delaware', 'District Of Columbia', 'Federated States Of Micronesia', 'Florida', 'Georgia',
'Guam', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine',
'Marshall Islands', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana',
'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
'Northern Mariana Islands', 'Ohio', 'Oklahoma', 'Oregon', 'Palau', 'Pennsylvania', 'Puerto Rico', 'Rhode Island',
'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virgin Islands', 'Virginia',
'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];

@Component({
selector: 'ngbd-typeahead-config',
templateUrl: './typeahead-config.html',
styles: [`.form-control { width: 300px; }`],
providers: [NgbTypeaheadConfig] // add NgbTypeaheadConfig to the component providers
})
export class NgbdTypeaheadConfig {

constructor(config: NgbTypeaheadConfig) {
// customize default values of typeaheads used by this component tree
config.showHint = true;
}

search = (text$: Observable<string>) =>
text$
.debounceTime(200)
.distinctUntilChanged()
.map(term => term.length < 2 ? []
: states.filter(v => v.toLowerCase().startsWith(term.toLocaleLowerCase())).splice(0, 10));
}
20 changes: 15 additions & 5 deletions demo/src/app/components/typeahead/demos/index.ts
Expand Up @@ -2,20 +2,30 @@ import {NgbdTypeaheadFormat} from './format/typeahead-format';
import {NgbdTypeaheadHttp} from './http/typeahead-http';
import {NgbdTypeaheadBasic} from './basic/typeahead-basic';
import {NgbdTypeaheadTemplate} from './template/typeahead-template';
import {NgbdTypeaheadConfig} from './config/typeahead-config';

export const DEMO_DIRECTIVES = [NgbdTypeaheadFormat, NgbdTypeaheadHttp, NgbdTypeaheadBasic, NgbdTypeaheadTemplate];
export const DEMO_DIRECTIVES =
[NgbdTypeaheadFormat, NgbdTypeaheadHttp, NgbdTypeaheadBasic, NgbdTypeaheadTemplate, NgbdTypeaheadConfig];

export const DEMO_SNIPPETS = {
basic: {
code: require('!!prismjs?lang=typescript!./basic/typeahead-basic'),
markup: require('!!prismjs?lang=markup!./basic/typeahead-basic.html')},
markup: require('!!prismjs?lang=markup!./basic/typeahead-basic.html')
},
format: {
code: require('!!prismjs?lang=typescript!./format/typeahead-format'),
markup: require('!!prismjs?lang=markup!./format/typeahead-format.html')},
markup: require('!!prismjs?lang=markup!./format/typeahead-format.html')
},
http: {
code: require('!!prismjs?lang=typescript!./http/typeahead-http'),
markup: require('!!prismjs?lang=markup!./http/typeahead-http.html')},
markup: require('!!prismjs?lang=markup!./http/typeahead-http.html')
},
template: {
code: require('!!prismjs?lang=typescript!./template/typeahead-template'),
markup: require('!!prismjs?lang=markup!./template/typeahead-template.html')}
markup: require('!!prismjs?lang=markup!./template/typeahead-template.html')
},
config: {
code: require('!!prismjs?lang=typescript!./config/typeahead-config'),
markup: require('!!prismjs?lang=markup!./config/typeahead-config.html')
}
};
6 changes: 6 additions & 0 deletions demo/src/app/components/typeahead/typeahead.component.ts
Expand Up @@ -7,6 +7,7 @@ import {DEMO_SNIPPETS} from './demos';
<ngbd-content-wrapper component="Typeahead">
<ngbd-api-docs directive="NgbTypeahead"></ngbd-api-docs>
<ngbd-api-docs-class type="ResultTemplateContext"></ngbd-api-docs-class>
<ngbd-api-docs-config type="NgbTypeaheadConfig"></ngbd-api-docs-config>
<ngbd-example-box demoTitle="Simple Typeahead" [htmlSnippet]="snippets.basic.markup" [tsSnippet]="snippets.basic.code">
<ngbd-typeahead-basic></ngbd-typeahead-basic>
</ngbd-example-box>
Expand All @@ -19,6 +20,11 @@ import {DEMO_SNIPPETS} from './demos';
<ngbd-example-box demoTitle="Template for results" [htmlSnippet]="snippets.template.markup" [tsSnippet]="snippets.template.code">
<ngbd-typeahead-template></ngbd-typeahead-template>
</ngbd-example-box>
<ngbd-example-box demoTitle="Global configuration of typeaheads"
[htmlSnippet]="snippets.config.markup"
[tsSnippet]="snippets.config.code">
<ngbd-typeahead-config></ngbd-typeahead-config>
</ngbd-example-box>
</ngbd-content-wrapper>
`
})
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -31,6 +31,7 @@ export {NgbRatingConfig} from './rating/rating.module';
export {NgbTimepickerConfig} from './timepicker/timepicker.module';
export {NgbTabsetConfig} from './tabset/tabset.module';
export {NgbTooltipConfig} from './tooltip/tooltip.module';
export {NgbTypeaheadConfig} from './typeahead/typeahead.module';

@NgModule({
exports: [
Expand Down
9 changes: 9 additions & 0 deletions src/typeahead/typeahead-config.spec.ts
@@ -0,0 +1,9 @@
import {NgbTypeaheadConfig} from './typeahead-config';

describe('ngb-typehead-config', () => {
it('should have sensible default values', () => {
const config = new NgbTypeaheadConfig();

expect(config.showHint).toBe(false);
});
});
11 changes: 11 additions & 0 deletions src/typeahead/typeahead-config.ts
@@ -0,0 +1,11 @@
import {Injectable} from '@angular/core';

/**
* Configuration service for the NgbTypeahead component.
* You can inject this service, typically in your root component, and customize the values of its properties in
* order to provide default values for all the typeaheads used in the application.
*/
@Injectable()
export class NgbTypeaheadConfig {
showHint = false;
}
6 changes: 5 additions & 1 deletion src/typeahead/typeahead.module.ts
Expand Up @@ -4,12 +4,16 @@ import {CommonModule} from '@angular/common';
import {NgbHighlight} from './highlight';
import {NgbTypeaheadWindow} from './typeahead-window';
import {NgbTypeahead} from './typeahead';
import {NgbTypeaheadConfig} from './typeahead-config';

export {NgbTypeaheadConfig} from './typeahead-config';

@NgModule({
declarations: [NgbTypeahead, NgbHighlight, NgbTypeaheadWindow],
exports: [NgbTypeahead],
imports: [CommonModule],
entryComponents: [NgbTypeaheadWindow]
entryComponents: [NgbTypeaheadWindow],
providers: [NgbTypeaheadConfig]
})
export class NgbTypeaheadModule {
}
57 changes: 55 additions & 2 deletions src/typeahead/typeahead.spec.ts
@@ -1,4 +1,4 @@
import {TestBed, ComponentFixture, async} from '@angular/core/testing';
import {TestBed, ComponentFixture, async, inject} from '@angular/core/testing';
import {createGenericTestComponent, isBrowser} from '../test/common';
import {expectResults, getWindowLinks} from '../test/typeahead/common';

Expand All @@ -10,6 +10,7 @@ import 'rxjs/add/operator/map';

import {NgbTypeahead} from './typeahead';
import {NgbTypeaheadModule} from './typeahead.module';
import {NgbTypeaheadConfig} from './typeahead-config';

const createTestComponent = (html: string) =>
createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
Expand Down Expand Up @@ -470,6 +471,7 @@ describe('ngb-typeahead', () => {
it('should restore hint when results window is dismissed', async(() => {
const fixture = createTestComponent(
`<input type="text" [(ngModel)]="model" [ngbTypeahead]="findAnywhere" [showHint]="true"/>`);
fixture.detectChanges();
const compiled = fixture.nativeElement;
const inputEl = getNativeInput(compiled);

Expand All @@ -490,8 +492,59 @@ describe('ngb-typeahead', () => {
});
}));
});
}

describe('Custom config', () => {
beforeEach(() => {
TestBed.overrideComponent(
TestComponent, {set: {template: '<input type="text" [(ngModel)]="model" [ngbTypeahead]="findAnywhere"/>'}});
});

beforeEach(inject([NgbTypeaheadConfig], (c: NgbTypeaheadConfig) => { c.showHint = true; }));

it('should initialize inputs with provided config', async(() => {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
const inputEl = getNativeInput(compiled);

fixture.whenStable().then(() => {
changeInput(compiled, 'on');
fixture.detectChanges();
expectWindowResults(compiled, ['+one', 'one more']);
expect(inputEl.value).toBe('one');
expect(inputEl.selectionStart).toBe(2);
expect(inputEl.selectionEnd).toBe(3);
});
}));
});

describe('Custom config as provider', () => {
beforeEach(() => {
const config = new NgbTypeaheadConfig();
config.showHint = true;
TestBed.configureTestingModule({providers: [{provide: NgbTypeaheadConfig, useValue: config}]});

TestBed.overrideComponent(
TestComponent, {set: {template: '<input type="text" [(ngModel)]="model" [ngbTypeahead]="findAnywhere"/>'}});
});

it('should initialize inputs with provided config as provider', async(() => {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
const inputEl = getNativeInput(compiled);

fixture.whenStable().then(() => {
changeInput(compiled, 'on');
fixture.detectChanges();
expectWindowResults(compiled, ['+one', 'one more']);
expect(inputEl.value).toBe('one');
expect(inputEl.selectionStart).toBe(2);
expect(inputEl.selectionEnd).toBe(3);
});
}));
});
}
});

@Component({selector: 'test-cmp', template: ''})
Expand Down
6 changes: 4 additions & 2 deletions src/typeahead/typeahead.ts
Expand Up @@ -23,6 +23,7 @@ import {Positioning} from '../util/positioning';
import {NgbTypeaheadWindow, ResultTemplateContext} from './typeahead-window';
import {PopupService} from '../util/popup';
import {toString} from '../util/util';
import {NgbTypeaheadConfig} from './typeahead-config';

enum Key {
Tab = 9,
Expand Down Expand Up @@ -89,7 +90,7 @@ export class NgbTypeahead implements OnInit,
/**
* Show hint when an option in the result list matches.
*/
@Input() showHint = false;
@Input() showHint: boolean;

/**
* An event emitted when a match is selected. Event payload is equal to the selected item.
Expand All @@ -105,7 +106,8 @@ export class NgbTypeahead implements OnInit,

constructor(
private _elementRef: ElementRef, private _viewContainerRef: ViewContainerRef, private _renderer: Renderer,
private _injector: Injector, componentFactoryResolver: ComponentFactoryResolver) {
private _injector: Injector, componentFactoryResolver: ComponentFactoryResolver, config: NgbTypeaheadConfig) {
this.showHint = config.showHint;
this._popupService = new PopupService<NgbTypeaheadWindow>(
NgbTypeaheadWindow, _injector, _viewContainerRef, _renderer, componentFactoryResolver);
this._onChangeNoEmit = (_: any) => {};
Expand Down

0 comments on commit 237b4d5

Please sign in to comment.