Commit c6295556c1744d3f860c3f85465024b20de01165

Authored by Vladyslav Prykhodko
1 parent 4e5f4851

UI: Improvement autocomplete component: debounceTime and catchError

@@ -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;