Skip to content

Commit 8378105

Browse files
IraErshovadmitry-zhemchugov
authored andcommitted
feat(typeahead): incoming data are not filtered after typeahead kicks-in #3725 (#3728)
Fix filtering incoming data after typeahead kicks-in Close #3725 Co-authored-by: dmitry-zhemchugov <44227371+dmitry-zhemchugov@users.noreply.github.com>
1 parent 43d0da5 commit 8378105

File tree

4 files changed

+42
-49
lines changed

4 files changed

+42
-49
lines changed
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
1+
<pre class="card card-block card-header">Model: {{ asyncSelected | json }}</pre>
22

33
<input [(ngModel)]="asyncSelected"
4-
[typeaheadAsync]="true"
54
[typeahead]="dataSource"
6-
(typeaheadLoading)="changeTypeaheadLoading($event)"
7-
(typeaheadOnSelect)="typeaheadOnSelect($event)"
8-
[typeaheadOptionsLimit]="7"
5+
[typeaheadAsync]="true"
96
typeaheadOptionField="name"
10-
placeholder="Locations loaded with timeout"
7+
placeholder="Locations loaded via observable"
118
class="form-control">
12-
<div *ngIf="typeaheadLoading">Loading</div>

demo/src/app/components/+typeahead/demos/async/async.ts

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { Component } from '@angular/core';
22

33
import { Observable, of } from 'rxjs';
4-
import { TypeaheadMatch } from 'ngx-bootstrap/typeahead';
5-
import { mergeMap } from 'rxjs/operators';
4+
5+
interface DataSourceType {
6+
id: number;
7+
name: string;
8+
region: string;
9+
}
610

711
@Component({
812
selector: 'demo-typeahead-async',
913
templateUrl: './async.html'
1014
})
1115
export class DemoTypeaheadAsyncComponent {
1216
asyncSelected: string;
13-
typeaheadLoading: boolean;
14-
typeaheadNoResults: boolean;
15-
dataSource: Observable<any>;
16-
statesComplex: any[] = [
17+
dataSource: Observable<DataSourceType[]>;
18+
statesComplex: DataSourceType[] = [
1719
{ id: 1, name: 'Alabama', region: 'South' },
1820
{ id: 2, name: 'Alaska', region: 'West' },
1921
{ id: 3, name: 'Arizona', region: 'West' },
@@ -67,30 +69,6 @@ export class DemoTypeaheadAsyncComponent {
6769
];
6870

6971
constructor() {
70-
this.dataSource = Observable.create((observer: any) => {
71-
// Runs on every search
72-
observer.next(this.asyncSelected);
73-
})
74-
.pipe(
75-
mergeMap((token: string) => this.getStatesAsObservable(token))
76-
);
77-
}
78-
79-
getStatesAsObservable(token: string): Observable<any> {
80-
const query = new RegExp(token, 'i');
81-
82-
return of(
83-
this.statesComplex.filter((state: any) => {
84-
return query.test(state.name);
85-
})
86-
);
87-
}
88-
89-
changeTypeaheadLoading(e: boolean): void {
90-
this.typeaheadLoading = e;
91-
}
92-
93-
typeaheadOnSelect(e: TypeaheadMatch): void {
94-
console.log('Selected value: ', e.value);
72+
this.dataSource = of(this.statesComplex);
9573
}
9674
}

src/spec/typeahead.directive.spec.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { Component, DebugElement } from '@angular/core';
55
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
66
import { FormsModule } from '@angular/forms';
77

8-
import { dispatchMouseEvent, dispatchTouchEvent, dispatchKeyboardEvent } from '@netbasal/spectator';
98
import { of } from 'rxjs';
9+
import { dispatchMouseEvent, dispatchTouchEvent, dispatchKeyboardEvent } from '@netbasal/spectator';
10+
1011
import { TypeaheadMatch, TypeaheadDirective, TypeaheadModule } from '../typeahead';
1112

1213
interface State {
@@ -102,9 +103,8 @@ describe('Directive: Typeahead', () => {
102103
expect(directive.typeaheadSelectFirstItem).toBeTruthy();
103104
});
104105

105-
it('should typeaheadAsync to false, if typeahead is an observable', () => {
106+
it('should typeaheadAsync to true, if typeahead is an observable', () => {
106107
directive.typeahead = of(component.states);
107-
108108
directive.ngOnInit();
109109

110110
expect(directive.typeaheadAsync).toBeTruthy();
@@ -345,6 +345,18 @@ describe('Directive: Typeahead', () => {
345345
})
346346
);
347347

348+
it('should result in 2 item matches, when "Ala" is entered in async mode', fakeAsync(() => {
349+
directive.typeahead = of(component.states);
350+
directive.ngOnInit();
351+
inputElement.value = 'Ala';
352+
dispatchTouchEvent(inputElement, 'input');
353+
fixture.detectChanges();
354+
tick(100);
355+
356+
expect(directive.matches.length).toBe(2);
357+
})
358+
);
359+
348360
it('should result in 0 matches, when input does not match', fakeAsync(() => {
349361
inputElement.value = 'foo';
350362
dispatchTouchEvent(inputElement, 'input');

src/typeahead/typeahead.directive.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { TypeaheadContainerComponent } from './typeahead-container.component';
2222
import { TypeaheadMatch } from './typeahead-match.class';
2323
import { TypeaheadConfig } from './typeahead.config';
2424
import { getValueFromObject, latinize, tokenize } from './typeahead-utils';
25-
import { debounceTime, filter, mergeMap, switchMap, toArray } from 'rxjs/operators';
25+
import { debounceTime, filter, map, mergeMap, switchMap, toArray } from 'rxjs/operators';
2626

2727
@Directive({selector: '[typeahead]', exportAs: 'bs-typeahead'})
2828
export class TypeaheadDirective implements OnInit, OnDestroy {
@@ -53,7 +53,7 @@ export class TypeaheadDirective implements OnInit, OnDestroy {
5353
* contains the group value, matches are grouped by this field when set.
5454
*/
5555
@Input() typeaheadGroupField: string;
56-
/** should be used only in case of typeahead attribute is array.
56+
/** should be used only in case of typeahead attribute is Observable of array.
5757
* If true - loading of options will be async, otherwise - sync.
5858
* true make sense if options array is large.
5959
*/
@@ -389,7 +389,18 @@ export class TypeaheadDirective implements OnInit, OnDestroy {
389389
this.keyUpEventEmitter
390390
.pipe(
391391
debounceTime(this.typeaheadWaitMs),
392-
switchMap(() => this.typeahead)
392+
switchMap((value: string) => {
393+
return this.typeahead
394+
.pipe(
395+
// tslint:disable-next-line:no-any
396+
map((typeahead: any[]) => {
397+
const normalizedQuery = this.normalizeQuery(value);
398+
399+
return typeahead.filter((option: any) =>
400+
option && this.testMatch(this.normalizeOption(option), normalizedQuery));
401+
})
402+
);
403+
})
393404
)
394405
.subscribe((matches: TypeaheadMatch[]) => {
395406
this.finalizeAsyncCall(matches);
@@ -408,11 +419,7 @@ export class TypeaheadDirective implements OnInit, OnDestroy {
408419
return from(this.typeahead)
409420
.pipe(
410421
filter((option: TypeaheadMatch) => {
411-
412-
return (
413-
option &&
414-
this.testMatch(this.normalizeOption(option), normalizedQuery)
415-
);
422+
return option && this.testMatch(this.normalizeOption(option), normalizedQuery);
416423
}),
417424
toArray()
418425
);

0 commit comments

Comments
 (0)