Commit c6295556c1744d3f860c3f85465024b20de01165

Authored by Vladyslav Prykhodko
1 parent 4e5f4851

UI: Improvement autocomplete component: debounceTime and catchError

... ... @@ -16,9 +16,9 @@
16 16
17 17 import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable } from 'rxjs';
20   -import { map, mergeMap, startWith, tap } from 'rxjs/operators';
21   -import { PageData } from '@shared/models/page/page-data';
  19 +import { Observable, of } from 'rxjs';
  20 +import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators';
  21 +import { emptyPageData, PageData } from '@shared/models/page/page-data';
22 22 import { Store } from '@ngrx/store';
23 23 import { AppState } from '@app/core/core.state';
24 24 import { TranslateService } from '@ngx-translate/core';
... ... @@ -88,6 +88,7 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
88 88 ngOnInit() {
89 89 this.filteredEntityInfos = this.selectEntityInfoFormGroup.get('entityInfo').valueChanges
90 90 .pipe(
  91 + debounceTime(150),
91 92 tap(value => {
92 93 let modelValue;
93 94 if (typeof value === 'string' || !value) {
... ... @@ -99,7 +100,8 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
99 100 }),
100 101 startWith<string | EntityInfo>(''),
101 102 map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
102   - mergeMap(name => this.fetchEntityInfos(name) )
  103 + distinctUntilChanged(),
  104 + switchMap(name => this.fetchEntityInfos(name))
103 105 );
104 106 }
105 107
... ... @@ -142,7 +144,9 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor,
142 144 }
143 145
144 146 getEntityInfos(searchText: string): Observable<PageData<EntityInfo>> {
145   - return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true});
  147 + return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true}).pipe(
  148 + catchError(() => of(emptyPageData<EntityInfo>()))
  149 + );
146 150 }
147 151
148 152 clear() {
... ...
... ... @@ -20,16 +20,18 @@ import {
20 20 EventEmitter,
21 21 forwardRef,
22 22 Input,
23   - NgZone, OnChanges,
  23 + NgZone,
  24 + OnChanges,
24 25 OnInit,
25   - Output, SimpleChanges,
  26 + Output,
  27 + SimpleChanges,
26 28 ViewChild
27 29 } from '@angular/core';
28 30 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
29   -import { Observable } from 'rxjs';
  31 +import { Observable, of } from 'rxjs';
30 32 import { PageLink } from '@shared/models/page/page-link';
31 33 import { Direction } from '@shared/models/page/sort-order';
32   -import { map, mergeMap, share, tap } from 'rxjs/operators';
  34 +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
33 35 import { Store } from '@ngrx/store';
34 36 import { AppState } from '@app/core/core.state';
35 37 import { TranslateService } from '@ngx-translate/core';
... ... @@ -44,6 +46,7 @@ import { DeviceProfileService } from '@core/http/device-profile.service';
44 46 import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component';
45 47 import { MatAutocomplete } from '@angular/material/autocomplete';
46 48 import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from './add-device-profile-dialog.component';
  49 +import { emptyPageData } from "@shared/models/page/page-data";
47 50
48 51 @Component({
49 52 selector: 'tb-device-profile-autocomplete',
... ... @@ -143,6 +146,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
143 146 ngOnInit() {
144 147 this.filteredDeviceProfiles = this.selectDeviceProfileFormGroup.get('deviceProfile').valueChanges
145 148 .pipe(
  149 + debounceTime(150),
146 150 tap((value: DeviceProfileInfo | string) => {
147 151 let modelValue: DeviceProfileInfo | null;
148 152 if (typeof value === 'string' || !value) {
... ... @@ -169,7 +173,8 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
169 173 return '';
170 174 }
171 175 }),
172   - mergeMap(name => this.fetchDeviceProfiles(name) ),
  176 + distinctUntilChanged(),
  177 + switchMap(name => this.fetchDeviceProfiles(name)),
173 178 share()
174 179 );
175 180 }
... ... @@ -289,6 +294,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
289 294 direction: Direction.ASC
290 295 });
291 296 return this.deviceProfileService.getDeviceProfileInfos(pageLink, this.transportType, {ignoreLoading: true}).pipe(
  297 + catchError(() => of(emptyPageData<DeviceProfileInfo>())),
292 298 map(pageData => {
293 299 let data = pageData.data;
294 300 if (this.displayAllOnEmpty) {
... ...
... ... @@ -16,10 +16,10 @@
16 16
17 17 import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable } from 'rxjs';
  19 +import { Observable, of } from 'rxjs';
20 20 import { PageLink } from '@shared/models/page/page-link';
21 21 import { Direction } from '@shared/models/page/sort-order';
22   -import { map, mergeMap, share, startWith, tap } from 'rxjs/operators';
  22 +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
23 23 import { Store } from '@ngrx/store';
24 24 import { AppState } from '@app/core/core.state';
25 25 import { TranslateService } from '@ngx-translate/core';
... ... @@ -33,6 +33,7 @@ import { ENTER } from '@angular/cdk/keycodes';
33 33 import { TenantProfile } from '@shared/models/tenant.model';
34 34 import { MatDialog } from '@angular/material/dialog';
35 35 import { TenantProfileDialogComponent, TenantProfileDialogData } from './tenant-profile-dialog.component';
  36 +import { emptyPageData } from "@shared/models/page/page-data";
36 37
37 38 @Component({
38 39 selector: 'tb-tenant-profile-autocomplete',
... ... @@ -99,6 +100,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
99 100 ngOnInit() {
100 101 this.filteredTenantProfiles = this.selectTenantProfileFormGroup.get('tenantProfile').valueChanges
101 102 .pipe(
  103 + debounceTime(150),
102 104 tap((value: EntityInfoData | string) => {
103 105 let modelValue: TenantProfileId | null;
104 106 if (typeof value === 'string' || !value) {
... ... @@ -109,7 +111,8 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
109 111 this.updateView(modelValue);
110 112 }),
111 113 map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
112   - mergeMap(name => this.fetchTenantProfiles(name) ),
  114 + distinctUntilChanged(),
  115 + switchMap(name => this.fetchTenantProfiles(name)),
113 116 share()
114 117 );
115 118 }
... ... @@ -174,6 +177,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor,
174 177 direction: Direction.ASC
175 178 });
176 179 return this.tenantProfileService.getTenantProfileInfos(pageLink, {ignoreLoading: true}).pipe(
  180 + catchError(() => of(emptyPageData<EntityInfoData>())),
177 181 map(pageData => {
178 182 return pageData.data;
179 183 })
... ...
... ... @@ -16,8 +16,8 @@
16 16
17 17 import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable } from 'rxjs';
20   -import { map, mergeMap, share, tap } from 'rxjs/operators';
  19 +import { Observable, of } from 'rxjs';
  20 +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
21 21 import { Store } from '@ngrx/store';
22 22 import { AppState } from '@core/core.state';
23 23 import { TranslateService } from '@ngx-translate/core';
... ... @@ -99,6 +99,7 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
99 99 ngOnInit() {
100 100 this.filteredRuleChains = this.selectRuleChainFormGroup.get('ruleChainId').valueChanges
101 101 .pipe(
  102 + debounceTime(150),
102 103 tap(value => {
103 104 let modelValue;
104 105 if (typeof value === 'string' || !value) {
... ... @@ -110,8 +111,10 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
110 111 if (value === null) {
111 112 this.clear();
112 113 }
113   - }), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
114   - mergeMap(name => this.fetchRuleChain(name) ),
  114 + }),
  115 + map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
  116 + distinctUntilChanged(),
  117 + switchMap(name => this.fetchRuleChain(name) ),
115 118 share()
116 119 );
117 120 }
... ... @@ -190,7 +193,9 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
190 193 this.searchText = searchText;
191 194 // voba: at the moment device profiles are not supported by edge, so 'core' hardcoded
192 195 return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText,
193   - 50, RuleChainType.CORE, {ignoreLoading: true});
  196 + 50, RuleChainType.CORE, {ignoreLoading: true}).pipe(
  197 + catchError(() => of([]))
  198 + );
194 199 }
195 200
196 201 clear() {
... ...
... ... @@ -19,7 +19,7 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from
19 19 import { Observable, of } from 'rxjs';
20 20 import { PageLink } from '@shared/models/page/page-link';
21 21 import { Direction } from '@shared/models/page/sort-order';
22   -import { map, mergeMap, startWith, tap } from 'rxjs/operators';
  22 +import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators';
23 23 import { emptyPageData, PageData } from '@shared/models/page/page-data';
24 24 import { DashboardInfo } from '@app/shared/models/dashboard.models';
25 25 import { DashboardService } from '@core/http/dashboard.service';
... ... @@ -107,6 +107,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
107 107 ngOnInit() {
108 108 this.filteredDashboards = this.selectDashboardFormGroup.get('dashboard').valueChanges
109 109 .pipe(
  110 + debounceTime(150),
110 111 tap(value => {
111 112 let modelValue;
112 113 if (typeof value === 'string' || !value) {
... ... @@ -118,7 +119,8 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
118 119 }),
119 120 startWith<string | DashboardInfo>(''),
120 121 map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
121   - mergeMap(name => this.fetchDashboards(name) )
  122 + distinctUntilChanged(),
  123 + switchMap(name => this.fetchDashboards(name) )
122 124 );
123 125 }
124 126
... ... @@ -189,6 +191,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
189 191 direction: Direction.ASC
190 192 });
191 193 return this.getDashboards(pageLink).pipe(
  194 + catchError(() => of(emptyPageData<DashboardInfo>())),
192 195 map(pageData => {
193 196 return pageData.data;
194 197 })
... ...
... ... @@ -26,8 +26,8 @@ import {
26 26 ViewChild
27 27 } from '@angular/core';
28 28 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
29   -import { Observable } from 'rxjs';
30   -import { map, mergeMap, share, tap } from 'rxjs/operators';
  29 +import { Observable, of } from 'rxjs';
  30 +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
31 31 import { Store } from '@ngrx/store';
32 32 import { AppState } from '@app/core/core.state';
33 33 import { TranslateService } from '@ngx-translate/core';
... ... @@ -142,6 +142,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
142 142 ngOnInit() {
143 143 this.filteredEntities = this.selectEntityFormGroup.get('entity').valueChanges
144 144 .pipe(
  145 + debounceTime(150),
145 146 tap(value => {
146 147 let modelValue;
147 148 if (typeof value === 'string' || !value) {
... ... @@ -156,7 +157,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
156 157 }),
157 158 // startWith<string | BaseData<EntityId>>(''),
158 159 map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
159   - mergeMap(name => this.fetchEntities(name) ),
  160 + distinctUntilChanged(),
  161 + switchMap(name => this.fetchEntities(name)),
160 162 share()
161 163 );
162 164 }
... ... @@ -326,6 +328,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
326 328 const targetEntityType = this.checkEntityType(this.entityTypeValue);
327 329 return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText,
328 330 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe(
  331 + catchError(() => of(null)),
329 332 map((data) => {
330 333 if (data) {
331 334 if (this.excludeEntityIds && this.excludeEntityIds.length) {
... ...
... ... @@ -16,8 +16,17 @@
16 16
17 17 import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable, Subscription, throwError } from 'rxjs';
20   -import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
  19 +import { Observable, of, Subscription, throwError } from 'rxjs';
  20 +import {
  21 + catchError,
  22 + debounceTime,
  23 + distinctUntilChanged,
  24 + map,
  25 + publishReplay,
  26 + refCount,
  27 + switchMap,
  28 + tap
  29 +} from 'rxjs/operators';
21 30 import { Store } from '@ngrx/store';
22 31 import { AppState } from '@app/core/core.state';
23 32 import { TranslateService } from '@ngx-translate/core';
... ... @@ -137,12 +146,14 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
137 146
138 147 this.filteredSubTypes = this.subTypeFormGroup.get('subType').valueChanges
139 148 .pipe(
  149 + debounceTime(150),
  150 + distinctUntilChanged(),
140 151 tap(value => {
141 152 this.updateView(value);
142 153 }),
143 154 // startWith<string | EntitySubtype>(''),
144 155 map(value => value ? value : ''),
145   - mergeMap(type => this.fetchSubTypes(type) )
  156 + switchMap(type => this.fetchSubTypes(type))
146 157 );
147 158 }
148 159
... ... @@ -221,6 +232,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
221 232 }
222 233 if (subTypesObservable) {
223 234 this.subTypes = subTypesObservable.pipe(
  235 + catchError(() => of([] as Array<EntitySubtype>)),
224 236 map(subTypes => subTypes.map(subType => subType.type)),
225 237 publishReplay(1),
226 238 refCount()
... ...
... ... @@ -16,8 +16,8 @@
16 16
17 17 import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable } from 'rxjs';
20   -import { map, mergeMap, share, tap } from 'rxjs/operators';
  19 +import { Observable, of } from 'rxjs';
  20 +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators';
21 21 import { Store } from '@ngrx/store';
22 22 import { AppState } from '@core/core.state';
23 23 import { TranslateService } from '@ngx-translate/core';
... ... @@ -27,11 +27,11 @@ import { EntityType } from '@shared/models/entity-type.models';
27 27 import { BaseData } from '@shared/models/base-data';
28 28 import { EntityService } from '@core/http/entity.service';
29 29 import { TruncatePipe } from '@shared/pipe/truncate.pipe';
30   -import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
31 30 import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models';
32 31 import { OtaPackageService } from '@core/http/ota-package.service';
33 32 import { PageLink } from '@shared/models/page/page-link';
34 33 import { Direction } from '@shared/models/page/sort-order';
  34 +import { emptyPageData } from "@shared/models/page/page-data";
35 35
36 36 @Component({
37 37 selector: 'tb-ota-package-autocomplete',
... ... @@ -109,6 +109,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
109 109 ngOnInit() {
110 110 this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges
111 111 .pipe(
  112 + debounceTime(150),
112 113 tap(value => {
113 114 let modelValue;
114 115 if (typeof value === 'string' || !value) {
... ... @@ -122,7 +123,8 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
122 123 }
123 124 }),
124 125 map(value => value ? (typeof value === 'string' ? value : value.title) : ''),
125   - mergeMap(name => this.fetchPackages(name)),
  126 + distinctUntilChanged(),
  127 + switchMap(name => this.fetchPackages(name)),
126 128 share()
127 129 );
128 130 }
... ... @@ -217,6 +219,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On
217 219 });
218 220 return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type,
219 221 {ignoreLoading: true}).pipe(
  222 + catchError(() => of(emptyPageData<OtaPackageInfo>())),
220 223 map((data) => data && data.data.length ? data.data : null)
221 224 );
222 225 }
... ...
... ... @@ -16,8 +16,17 @@
16 16
17 17 import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
18 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
19   -import { Observable } from 'rxjs';
20   -import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
  19 +import { Observable, of } from 'rxjs';
  20 +import {
  21 + catchError,
  22 + debounceTime,
  23 + distinctUntilChanged,
  24 + map,
  25 + publishReplay,
  26 + refCount,
  27 + switchMap,
  28 + tap
  29 +} from 'rxjs/operators';
21 30 import { Store } from '@ngrx/store';
22 31 import { AppState } from '@app/core/core.state';
23 32 import { TranslateService } from '@ngx-translate/core';
... ... @@ -87,11 +96,13 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft
87 96 ngOnInit() {
88 97 this.filteredQueues = this.queueFormGroup.get('queue').valueChanges
89 98 .pipe(
  99 + debounceTime(150),
  100 + distinctUntilChanged(),
90 101 tap(value => {
91 102 this.updateView(value);
92 103 }),
93 104 map(value => value ? value : ''),
94   - mergeMap(queue => this.fetchQueues(queue) )
  105 + switchMap(queue => this.fetchQueues(queue) )
95 106 );
96 107 }
97 108
... ... @@ -138,6 +149,7 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft
138 149 fetchQueues(searchText?: string): Observable<Array<string>> {
139 150 this.searchText = searchText;
140 151 return this.getQueues().pipe(
  152 + catchError(() => of([])),
141 153 map(queues => {
142 154 const result = queues.filter( queue => {
143 155 return searchText ? queue.toUpperCase().startsWith(searchText.toUpperCase()) : true;
... ...