Skip to content

Commit

Permalink
fix(typeahead): incoming data are not filtered after typeahead kicks-in
Browse files Browse the repository at this point in the history
#3725

Fix filtering incoming data after typeahead kicks-in

Close #3725
  • Loading branch information
IraErshova committed Jan 20, 2020
1 parent 74f752f commit 757f29b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 49 deletions.
10 changes: 3 additions & 7 deletions demo/src/app/components/+typeahead/demos/async/async.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
<pre class="card card-block card-header mb-3">Model: {{asyncSelected}}</pre>

<input [(ngModel)]="asyncSelected"
[typeaheadAsync]="true"
[typeahead]="dataSource"
(typeaheadLoading)="changeTypeaheadLoading($event)"
(typeaheadOnSelect)="typeaheadOnSelect($event)"
[typeaheadOptionsLimit]="7"
[typeaheadAsync]="true"
typeaheadOptionField="name"
placeholder="Locations loaded with timeout"
placeholder="Locations loaded via observable"
class="form-control">
<div *ngIf="typeaheadLoading">Loading</div>
40 changes: 9 additions & 31 deletions demo/src/app/components/+typeahead/demos/async/async.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
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',
templateUrl: './async.html'
})
export class DemoTypeaheadAsyncComponent {
asyncSelected: string;
typeaheadLoading: boolean;
typeaheadNoResults: boolean;
dataSource: Observable<any>;
statesComplex: any[] = [
dataSource: Observable<DataSourceType[]>;
statesComplex: DataSourceType[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{ id: 3, name: 'Arizona', region: 'West' },
Expand Down Expand Up @@ -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<any> {
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);
}
}
19 changes: 16 additions & 3 deletions src/spec/typeahead.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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');
Expand Down
23 changes: 15 additions & 8 deletions src/typeahead/typeahead.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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);
Expand All @@ -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()
);
Expand Down

0 comments on commit 757f29b

Please sign in to comment.