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 Feb 8, 2018
1 parent 465ed0d commit 1552274
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 71 deletions.
21 changes: 9 additions & 12 deletions demo/src/app/components/+typeahead/demos/async/async.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
<input [(ngModel)]="asyncSelected"
[typeahead]="dataSource"
(typeaheadLoading)="changeTypeaheadLoading($event)"
(typeaheadNoResults)="changeTypeaheadNoResults($event)"
(typeaheadOnSelect)="typeaheadOnSelect($event)"
[typeaheadOptionsLimit]="7"
typeaheadOptionField="name"
placeholder="Locations loaded with timeout"
class="form-control">
<div *ngIf="typeaheadLoading===true">Loading</div>
<div *ngIf="typeaheadNoResults===true">&#10060; No Results Found</div>
<pre class="card card-block card-header mb-3">Model: {{asyncSelected}}</pre>

<input [(ngModel)]="asyncSelected"
[typeahead]="dataSource"
[typeaheadAsync]="true"
[typeaheadWaitMs]="1000"
typeaheadOptionField="name"
placeholder="Locations loaded with delay"
class="form-control">
39 changes: 4 additions & 35 deletions demo/src/app/components/+typeahead/demos/async/async.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { Observable } from 'rxjs/Observable';
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';

@Component({
Expand All @@ -10,17 +10,11 @@ import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
})
export class DemoTypeaheadAsyncComponent {
asyncSelected: string;
typeaheadLoading: boolean;
typeaheadNoResults: boolean;
dataSource: Observable<any>;
dataSource: Observable<any[]>;
statesComplex: any[] = [
{ id: 1, name: 'Alabama', region: 'South' },
{ id: 2, name: 'Alaska', region: 'West' },
{
id: 3,
name: 'Arizona',
region: 'West'
},
{ id: 3, name: 'Arizona', region: 'West' },
{ id: 4, name: 'Arkansas', region: 'South' },
{ id: 5, name: 'California', region: 'West' },
{ id: 6, name: 'Colorado', region: 'West' },
Expand Down Expand Up @@ -71,31 +65,6 @@ export class DemoTypeaheadAsyncComponent {
];

constructor() {
this.dataSource = Observable.create((observer: any) => {
// Runs on every search
observer.next(this.asyncSelected);
}).mergeMap((token: string) => this.getStatesAsObservable(token));
}

getStatesAsObservable(token: string): Observable<any> {
let query = new RegExp(token, 'ig');

return Observable.of(
this.statesComplex.filter((state: any) => {
return query.test(state.name);
})
);
}

changeTypeaheadLoading(e: boolean): void {
this.typeaheadLoading = e;
}

changeTypeaheadNoResults(e: boolean): void {
this.typeaheadNoResults = e;
}

typeaheadOnSelect(e: TypeaheadMatch): void {
console.log('Selected value: ', e.value);
this.dataSource = Observable.of(this.statesComplex);
}
}
46 changes: 28 additions & 18 deletions src/spec/typeahead.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { Component, DebugElement } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';

import 'rxjs/add/observable/of';
import { Observable } from 'rxjs/Observable';

import { fireEvent } from '../../scripts/helpers';
import { TypeaheadMatch } from '../typeahead/typeahead-match.class';
import { TypeaheadDirective } from '../typeahead/typeahead.directive';
import { TypeaheadModule } from '../typeahead/typeahead.module';
import 'rxjs/add/observable/of';

interface State {
id: number;
Expand All @@ -16,7 +18,6 @@ interface State {
}

@Component({
// (typeaheadOnSelect)="typeaheadOnSelect($event)"
template: `
<input [(ngModel)]="selectedState"
[typeahead]="states"
Expand All @@ -31,7 +32,9 @@ class TestTypeaheadComponent {
{id: 2, name: 'Alaska', region: 'West'}
];

onBlurEvent(activeItem) {}
onBlurEvent(activeItem: any): void {
return;
}
}

describe('Directive: Typeahead', () => {
Expand Down Expand Up @@ -83,7 +86,7 @@ describe('Directive: Typeahead', () => {
expect(directive.typeaheadAsync).toBeFalsy();
});

it('should typeaheadAsync to false, if typeahead is an observable', () => {
it('should typeaheadAsync to true, if typeahead is an observable', () => {
directive.typeahead = Observable.of(component.states);
directive.ngOnInit();

Expand Down Expand Up @@ -125,8 +128,7 @@ describe('Directive: Typeahead', () => {
expect(directive._container).toBeTruthy();
});

it(
'should result in a total of 2 matches, when "Ala" is entered',
it('should result in a total of 2 matches, when "Ala" is entered',
fakeAsync(() => {
expect(directive.matches.length).toBe(2);
})
Expand All @@ -150,8 +152,7 @@ describe('Directive: Typeahead', () => {
})
);

it(
'should result in 0 matches, when input does not match',
it('should result in 0 matches, when input does not match',
fakeAsync(() => {
inputElement.value = 'foo';
fireEvent(inputElement, 'input');
Expand All @@ -163,8 +164,7 @@ describe('Directive: Typeahead', () => {
})
);

it(
'should not display null item',
it('should not display null item',
fakeAsync(() => {
component.states.push({id: 3, name: null, region: 'West'});
inputElement.value = 'Ala';
Expand All @@ -175,6 +175,19 @@ describe('Directive: Typeahead', () => {
expect(directive.matches.length).toBe(2);
})
);

it('should result in 2 item matches, when "Ala" is entered in async mode',
fakeAsync(() => {
directive.typeahead = Observable.of(component.states);
directive.ngOnInit();
inputElement.value = 'Ala';
fireEvent(inputElement, 'input');
fixture.detectChanges();
tick(100);

expect(directive.matches.length).toBe(2);
})
);
});

describe('onChange grouped', () => {
Expand All @@ -189,15 +202,13 @@ describe('Directive: Typeahead', () => {
})
);

it(
'should result in a total of 4 matches, when "Ala" is entered',
it('should result in a total of 4 matches, when "Ala" is entered',
fakeAsync(() => {
expect(directive.matches.length).toBe(4);
})
);

it(
'should result in 2 header matches, when "Ala" is entered',
it('should result in 2 header matches, when "Ala" is entered',
fakeAsync(() => {
expect(directive.matches).toContain(
new TypeaheadMatch('South', 'South', true)
Expand All @@ -208,8 +219,7 @@ describe('Directive: Typeahead', () => {
})
);

it(
'should result in 2 item matches, when "Ala" is entered',
it('should result in 2 item matches, when "Ala" is entered',
fakeAsync(() => {
expect(directive.matches).toContain(
new TypeaheadMatch(
Expand Down
17 changes: 11 additions & 6 deletions src/typeahead/typeahead.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,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 @@ -344,7 +344,15 @@ export class TypeaheadDirective implements OnInit, OnDestroy {
this._subscriptions.push(
this.keyUpEventEmitter
.debounceTime(this.typeaheadWaitMs)
.mergeMap(() => this.typeahead)
.mergeMap((value: string) => {
const normalizedQuery = this.normalizeQuery(value);

return this.typeahead.map((data: any[]) => {
return data.filter((option: any) => {
return option && this.testMatch(this.normalizeOption(option), normalizedQuery);
});
});
})
.subscribe((matches: any[]) => {
this.finalizeAsyncCall(matches);
})
Expand All @@ -360,10 +368,7 @@ export class TypeaheadDirective implements OnInit, OnDestroy {

return Observable.from(this.typeahead)
.filter((option: any) => {
return (
option &&
this.testMatch(this.normalizeOption(option), normalizedQuery)
);
return option && this.testMatch(this.normalizeOption(option), normalizedQuery);
})
.toArray();
})
Expand Down

0 comments on commit 1552274

Please sign in to comment.