diff --git a/ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts index 0c903f8f3f..a56c19c281 100644 --- a/ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/alias/aliases-entity-autocomplete.component.ts @@ -16,9 +16,9 @@ import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { map, mergeMap, startWith, tap } from 'rxjs/operators'; -import { PageData } from '@shared/models/page/page-data'; +import { Observable, of } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -88,6 +88,7 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor, ngOnInit() { this.filteredEntityInfos = this.selectEntityInfoFormGroup.get('entityInfo').valueChanges .pipe( + debounceTime(150), tap(value => { let modelValue; if (typeof value === 'string' || !value) { @@ -99,7 +100,8 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor, }), startWith(''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchEntityInfos(name) ) + distinctUntilChanged(), + switchMap(name => this.fetchEntityInfos(name)) ); } @@ -142,7 +144,9 @@ export class AliasesEntityAutocompleteComponent implements ControlValueAccessor, } getEntityInfos(searchText: string): Observable> { - return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true}); + return this.entityService.findEntityInfosByFilterAndName(this.entityFilter, searchText, {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())) + ); } clear() { diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts index a2fa8d1ed3..c59a2cb9b3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts @@ -20,16 +20,18 @@ import { EventEmitter, forwardRef, Input, - NgZone, OnChanges, + NgZone, + OnChanges, OnInit, - Output, SimpleChanges, + Output, + SimpleChanges, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; -import { map, mergeMap, share, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -44,6 +46,7 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; import { DeviceProfileDialogComponent, DeviceProfileDialogData } from './device-profile-dialog.component'; import { MatAutocomplete } from '@angular/material/autocomplete'; import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from './add-device-profile-dialog.component'; +import { emptyPageData } from "@shared/models/page/page-data"; @Component({ selector: 'tb-device-profile-autocomplete', @@ -143,6 +146,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, ngOnInit() { this.filteredDeviceProfiles = this.selectDeviceProfileFormGroup.get('deviceProfile').valueChanges .pipe( + debounceTime(150), tap((value: DeviceProfileInfo | string) => { let modelValue: DeviceProfileInfo | null; if (typeof value === 'string' || !value) { @@ -169,7 +173,8 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, return ''; } }), - mergeMap(name => this.fetchDeviceProfiles(name) ), + distinctUntilChanged(), + switchMap(name => this.fetchDeviceProfiles(name)), share() ); } @@ -289,6 +294,7 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, direction: Direction.ASC }); return this.deviceProfileService.getDeviceProfileInfos(pageLink, this.transportType, {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())), map(pageData => { let data = pageData.data; if (this.displayAllOnEmpty) { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts index 278c694840..97a1edcd1b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.ts @@ -16,10 +16,10 @@ import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; -import { map, mergeMap, share, startWith, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -33,6 +33,7 @@ import { ENTER } from '@angular/cdk/keycodes'; import { TenantProfile } from '@shared/models/tenant.model'; import { MatDialog } from '@angular/material/dialog'; import { TenantProfileDialogComponent, TenantProfileDialogData } from './tenant-profile-dialog.component'; +import { emptyPageData } from "@shared/models/page/page-data"; @Component({ selector: 'tb-tenant-profile-autocomplete', @@ -99,6 +100,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, ngOnInit() { this.filteredTenantProfiles = this.selectTenantProfileFormGroup.get('tenantProfile').valueChanges .pipe( + debounceTime(150), tap((value: EntityInfoData | string) => { let modelValue: TenantProfileId | null; if (typeof value === 'string' || !value) { @@ -109,7 +111,8 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, this.updateView(modelValue); }), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchTenantProfiles(name) ), + distinctUntilChanged(), + switchMap(name => this.fetchTenantProfiles(name)), share() ); } @@ -174,6 +177,7 @@ export class TenantProfileAutocompleteComponent implements ControlValueAccessor, direction: Direction.ASC }); return this.tenantProfileService.getTenantProfileInfos(pageLink, {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())), map(pageData => { return pageData.data; }) diff --git a/ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts index b190b0d293..626da4edb3 100644 --- a/ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts @@ -16,8 +16,8 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { map, mergeMap, share, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -99,6 +99,7 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI ngOnInit() { this.filteredRuleChains = this.selectRuleChainFormGroup.get('ruleChainId').valueChanges .pipe( + debounceTime(150), tap(value => { let modelValue; if (typeof value === 'string' || !value) { @@ -110,8 +111,10 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI if (value === null) { this.clear(); } - }), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchRuleChain(name) ), + }), + map(value => value ? (typeof value === 'string' ? value : value.name) : ''), + distinctUntilChanged(), + switchMap(name => this.fetchRuleChain(name) ), share() ); } @@ -190,7 +193,9 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI this.searchText = searchText; // voba: at the moment device profiles are not supported by edge, so 'core' hardcoded return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText, - 50, RuleChainType.CORE, {ignoreLoading: true}); + 50, RuleChainType.CORE, {ignoreLoading: true}).pipe( + catchError(() => of([])) + ); } clear() { diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts index e4e97c95b2..f3c3b3b07f 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts @@ -19,7 +19,7 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from import { Observable, of } from 'rxjs'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; -import { map, mergeMap, startWith, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, startWith, switchMap, tap } from 'rxjs/operators'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { DashboardInfo } from '@app/shared/models/dashboard.models'; import { DashboardService } from '@core/http/dashboard.service'; @@ -107,6 +107,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI ngOnInit() { this.filteredDashboards = this.selectDashboardFormGroup.get('dashboard').valueChanges .pipe( + debounceTime(150), tap(value => { let modelValue; if (typeof value === 'string' || !value) { @@ -118,7 +119,8 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI }), startWith(''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchDashboards(name) ) + distinctUntilChanged(), + switchMap(name => this.fetchDashboards(name) ) ); } @@ -189,6 +191,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI direction: Direction.ASC }); return this.getDashboards(pageLink).pipe( + catchError(() => of(emptyPageData())), map(pageData => { return pageData.data; }) diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts index 631a1ad236..28b2b0aad0 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts @@ -26,8 +26,8 @@ import { ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { map, mergeMap, share, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -142,6 +142,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit ngOnInit() { this.filteredEntities = this.selectEntityFormGroup.get('entity').valueChanges .pipe( + debounceTime(150), tap(value => { let modelValue; if (typeof value === 'string' || !value) { @@ -156,7 +157,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit }), // startWith>(''), map(value => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchEntities(name) ), + distinctUntilChanged(), + switchMap(name => this.fetchEntities(name)), share() ); } @@ -326,6 +328,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit const targetEntityType = this.checkEntityType(this.entityTypeValue); return this.entityService.getEntitiesByNameFilter(targetEntityType, searchText, 50, this.entitySubtypeValue, {ignoreLoading: true}).pipe( + catchError(() => of(null)), map((data) => { if (data) { if (this.excludeEntityIds && this.excludeEntityIds.length) { diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts index 14d2fcb228..fa0bcd3810 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts @@ -16,8 +16,17 @@ import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable, Subscription, throwError } from 'rxjs'; -import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; +import { Observable, of, Subscription, throwError } from 'rxjs'; +import { + catchError, + debounceTime, + distinctUntilChanged, + map, + publishReplay, + refCount, + switchMap, + tap +} from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -137,12 +146,14 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, this.filteredSubTypes = this.subTypeFormGroup.get('subType').valueChanges .pipe( + debounceTime(150), + distinctUntilChanged(), tap(value => { this.updateView(value); }), // startWith(''), map(value => value ? value : ''), - mergeMap(type => this.fetchSubTypes(type) ) + switchMap(type => this.fetchSubTypes(type)) ); } @@ -221,6 +232,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, } if (subTypesObservable) { this.subTypes = subTypesObservable.pipe( + catchError(() => of([] as Array)), map(subTypes => subTypes.map(subType => subType.type)), publishReplay(1), refCount() diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index a8f7aa65a5..6609d4ceb7 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -16,8 +16,8 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { map, mergeMap, share, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -27,11 +27,11 @@ import { EntityType } from '@shared/models/entity-type.models'; import { BaseData } from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service'; import { TruncatePipe } from '@shared/pipe/truncate.pipe'; -import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models'; import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; +import { emptyPageData } from "@shared/models/page/page-data"; @Component({ selector: 'tb-ota-package-autocomplete', @@ -109,6 +109,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On ngOnInit() { this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges .pipe( + debounceTime(150), tap(value => { let modelValue; if (typeof value === 'string' || !value) { @@ -122,7 +123,8 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On } }), map(value => value ? (typeof value === 'string' ? value : value.title) : ''), - mergeMap(name => this.fetchPackages(name)), + distinctUntilChanged(), + switchMap(name => this.fetchPackages(name)), share() ); } @@ -217,6 +219,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On }); return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())), map((data) => data && data.data.length ? data.data : null) ); } diff --git a/ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts b/ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts index befa95ceb2..eb850aefda 100644 --- a/ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts +++ b/ui-ngx/src/app/shared/components/queue/queue-type-list.component.ts @@ -16,8 +16,17 @@ import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable } from 'rxjs'; -import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { + catchError, + debounceTime, + distinctUntilChanged, + map, + publishReplay, + refCount, + switchMap, + tap +} from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -87,11 +96,13 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft ngOnInit() { this.filteredQueues = this.queueFormGroup.get('queue').valueChanges .pipe( + debounceTime(150), + distinctUntilChanged(), tap(value => { this.updateView(value); }), map(value => value ? value : ''), - mergeMap(queue => this.fetchQueues(queue) ) + switchMap(queue => this.fetchQueues(queue) ) ); } @@ -138,6 +149,7 @@ export class QueueTypeListComponent implements ControlValueAccessor, OnInit, Aft fetchQueues(searchText?: string): Observable> { this.searchText = searchText; return this.getQueues().pipe( + catchError(() => of([])), map(queues => { const result = queues.filter( queue => { return searchText ? queue.toUpperCase().startsWith(searchText.toUpperCase()) : true;