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