diff --git a/demo/src/app/components/+typeahead/demos/async/async.html b/demo/src/app/components/+typeahead/demos/async/async.html index bf21cad553..b06ac96294 100644 --- a/demo/src/app/components/+typeahead/demos/async/async.html +++ b/demo/src/app/components/+typeahead/demos/async/async.html @@ -1,12 +1,8 @@ -
Model: {{asyncSelected | json}}
+
Model: {{asyncSelected}}
-
Loading
diff --git a/demo/src/app/components/+typeahead/demos/async/async.ts b/demo/src/app/components/+typeahead/demos/async/async.ts index b06009a664..81f47581ec 100644 --- a/demo/src/app/components/+typeahead/demos/async/async.ts +++ b/demo/src/app/components/+typeahead/demos/async/async.ts @@ -1,8 +1,12 @@ import { Component } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { TypeaheadMatch } from 'ngx-bootstrap/typeahead'; -import { mergeMap } from 'rxjs/operators'; + +interface DataSourceType { + id: number; + name: string; + region: string; +} @Component({ selector: 'demo-typeahead-async', @@ -10,10 +14,8 @@ import { mergeMap } from 'rxjs/operators'; }) export class DemoTypeaheadAsyncComponent { asyncSelected: string; - typeaheadLoading: boolean; - typeaheadNoResults: boolean; - dataSource: Observable; - statesComplex: any[] = [ + dataSource: Observable; + statesComplex: DataSourceType[] = [ { id: 1, name: 'Alabama', region: 'South' }, { id: 2, name: 'Alaska', region: 'West' }, { id: 3, name: 'Arizona', region: 'West' }, @@ -67,30 +69,6 @@ export class DemoTypeaheadAsyncComponent { ]; constructor() { - this.dataSource = Observable.create((observer: any) => { - // Runs on every search - observer.next(this.asyncSelected); - }) - .pipe( - mergeMap((token: string) => this.getStatesAsObservable(token)) - ); - } - - getStatesAsObservable(token: string): Observable { - const query = new RegExp(token, 'i'); - - return of( - this.statesComplex.filter((state: any) => { - return query.test(state.name); - }) - ); - } - - changeTypeaheadLoading(e: boolean): void { - this.typeaheadLoading = e; - } - - typeaheadOnSelect(e: TypeaheadMatch): void { - console.log('Selected value: ', e.value); + this.dataSource = of(this.statesComplex); } } diff --git a/src/spec/typeahead.directive.spec.ts b/src/spec/typeahead.directive.spec.ts index 36ebcb2437..6f627278db 100644 --- a/src/spec/typeahead.directive.spec.ts +++ b/src/spec/typeahead.directive.spec.ts @@ -5,8 +5,9 @@ import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { dispatchMouseEvent, dispatchTouchEvent, dispatchKeyboardEvent } from '@netbasal/spectator'; import { of } from 'rxjs'; +import { dispatchMouseEvent, dispatchTouchEvent, dispatchKeyboardEvent } from '@netbasal/spectator'; + import { TypeaheadMatch, TypeaheadDirective, TypeaheadModule } from '../typeahead'; interface State { @@ -102,9 +103,8 @@ describe('Directive: Typeahead', () => { expect(directive.typeaheadSelectFirstItem).toBeTruthy(); }); - it('should typeaheadAsync to false, if typeahead is an observable', () => { + it('should typeaheadAsync to true, if typeahead is an observable', () => { directive.typeahead = of(component.states); - directive.ngOnInit(); expect(directive.typeaheadAsync).toBeTruthy(); @@ -345,6 +345,19 @@ describe('Directive: Typeahead', () => { }) ); + it('should result in 2 item matches, when "Ala" is entered in async mode', fakeAsync(() => { + directive.typeahead = of(component.states); + directive.ngOnInit(); + inputElement.value = 'Ala'; + // fireEvent(inputElement, 'input'); + dispatchTouchEvent(inputElement, 'input'); + fixture.detectChanges(); + tick(100); + + expect(directive.matches.length).toBe(2); + }) + ); + it('should result in 0 matches, when input does not match', fakeAsync(() => { inputElement.value = 'foo'; dispatchTouchEvent(inputElement, 'input'); diff --git a/src/typeahead/typeahead.directive.ts b/src/typeahead/typeahead.directive.ts index 4fd7e2034b..44a0c28584 100644 --- a/src/typeahead/typeahead.directive.ts +++ b/src/typeahead/typeahead.directive.ts @@ -22,7 +22,7 @@ import { TypeaheadContainerComponent } from './typeahead-container.component'; import { TypeaheadMatch } from './typeahead-match.class'; import { TypeaheadConfig } from './typeahead.config'; import { getValueFromObject, latinize, tokenize } from './typeahead-utils'; -import { debounceTime, filter, mergeMap, switchMap, toArray } from 'rxjs/operators'; +import { debounceTime, filter, map, mergeMap, switchMap, toArray } from 'rxjs/operators'; @Directive({selector: '[typeahead]', exportAs: 'bs-typeahead'}) export class TypeaheadDirective implements OnInit, OnDestroy { @@ -53,7 +53,7 @@ export class TypeaheadDirective implements OnInit, OnDestroy { * contains the group value, matches are grouped by this field when set. */ @Input() typeaheadGroupField: string; - /** should be used only in case of typeahead attribute is array. + /** should be used only in case of typeahead attribute is Observable of array. * If true - loading of options will be async, otherwise - sync. * true make sense if options array is large. */ @@ -389,7 +389,18 @@ export class TypeaheadDirective implements OnInit, OnDestroy { this.keyUpEventEmitter .pipe( debounceTime(this.typeaheadWaitMs), - switchMap(() => this.typeahead) + switchMap((value: string) => { + return this.typeahead + .pipe( + // tslint:disable-next-line:no-any + map((typeahead: any[]) => { + const normalizedQuery = this.normalizeQuery(value); + + return typeahead.filter((option: any) => + option && this.testMatch(this.normalizeOption(option), normalizedQuery)); + }) + ); + }) ) .subscribe((matches: TypeaheadMatch[]) => { this.finalizeAsyncCall(matches); @@ -408,11 +419,7 @@ export class TypeaheadDirective implements OnInit, OnDestroy { return from(this.typeahead) .pipe( filter((option: TypeaheadMatch) => { - - return ( - option && - this.testMatch(this.normalizeOption(option), normalizedQuery) - ); + return option && this.testMatch(this.normalizeOption(option), normalizedQuery); }), toArray() );