From 922c08891f52ee88eb66d9c841fdef38d332b649 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 9 Dec 2022 16:34:37 +0200 Subject: [PATCH 1/5] UI: Refactoring Form group with form array controls --- .../filter-predicate-list.component.html | 4 +- .../filter/filter-predicate-list.component.ts | 57 ++++++++-------- .../filter/key-filter-list.component.html | 4 +- .../filter/key-filter-list.component.ts | 57 +++++++++------- .../alarm/create-alarm-rules.component.ts | 28 ++++---- .../device-profile-alarms.component.html | 4 +- .../alarm/device-profile-alarms.component.ts | 54 ++++++++------- .../lwm2m-attributes-key-list.component.ts | 17 ++--- ...2m-bootstrap-config-servers.component.html | 4 +- ...wm2m-bootstrap-config-servers.component.ts | 66 ++++++++++--------- ...erve-attr-telemetry-instances.component.ts | 10 +-- ...erve-attr-telemetry-resources.component.ts | 20 ++---- .../lwm2m-observe-attr-telemetry.component.ts | 10 +-- ...rofile-communication-config.component.html | 4 +- ...-profile-communication-config.component.ts | 55 ++++++++-------- ...snmp-device-profile-mapping.component.html | 4 +- .../snmp-device-profile-mapping.component.ts | 43 ++++++------ .../queue/tenant-profile-queues.component.ts | 63 +++++++++--------- .../relation/relation-filters.component.html | 8 +-- .../relation/relation-filters.component.ts | 54 ++++++++------- .../app/shared/components/kv-map.component.ts | 29 ++++---- 21 files changed, 304 insertions(+), 291 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 2cea71d502..731ef3d5a3 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -42,7 +42,7 @@
+ *ngFor="let predicateControl of predicatesFormArray.controls; let $index = index">
{{ complexOperationTranslations.get(operation) | translate }}
@@ -68,7 +68,7 @@
- filter.no-filters diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts index fe836f44f1..81e1c73d73 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -27,7 +27,7 @@ import { Validator, Validators } from '@angular/forms'; -import { Observable, of, Subscription } from 'rxjs'; +import { Observable, of, Subject, Subscription } from 'rxjs'; import { ComplexFilterPredicateInfo, ComplexOperation, @@ -37,7 +37,7 @@ import { KeyFilterPredicateInfo } from '@shared/models/query/query.models'; import { MatDialog } from '@angular/material/dialog'; -import { map } from 'rxjs/operators'; +import { map, takeUntil } from 'rxjs/operators'; import { ComponentType } from '@angular/cdk/portal'; import { COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN } from '@home/components/tokens'; import { ComplexFilterPredicateDialogData } from '@home/components/filter/filter-component.models'; @@ -59,7 +59,7 @@ import { ComplexFilterPredicateDialogData } from '@home/components/filter/filter } ] }) -export class FilterPredicateListComponent implements ControlValueAccessor, Validator, OnInit { +export class FilterPredicateListComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { @Input() disabled: boolean; @@ -81,22 +81,29 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid complexOperationTranslations = complexOperationTranslationMap; + private destroy$ = new Subject(); private propagateChange = null; - private valueChangeSubscription: Subscription = null; - constructor(private fb: FormBuilder, @Inject(COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN) private complexFilterPredicateDialogComponent: ComponentType, private dialog: MatDialog) { } ngOnInit(): void { - this.filterListFormGroup = this.fb.group({}); - this.filterListFormGroup.addControl('predicates', - this.fb.array([])); + this.filterListFormGroup = this.fb.group({ + predicates: this.fb.array([]) + }); + this.filterListFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - predicatesFormArray(): FormArray { + get predicatesFormArray(): FormArray { return this.filterListFormGroup.get('predicates') as FormArray; } @@ -123,23 +130,21 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid } writeValue(predicates: Array): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } - const predicateControls: Array = []; - if (predicates) { - for (const predicate of predicates) { - predicateControls.push(this.fb.control(predicate, [Validators.required])); - } - } - this.filterListFormGroup.setControl('predicates', this.fb.array(predicateControls)); - this.valueChangeSubscription = this.filterListFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); - if (this.disabled) { - this.filterListFormGroup.disable({emitEvent: false}); + if (predicates.length === this.predicatesFormArray.length) { + this.predicatesFormArray.patchValue(predicates, {emitEvent: false}); } else { - this.filterListFormGroup.enable({emitEvent: false}); + const predicateControls: Array = []; + if (predicates) { + for (const predicate of predicates) { + predicateControls.push(this.fb.control(predicate, [Validators.required])); + } + } + this.filterListFormGroup.setControl('predicates', this.fb.array(predicateControls), {emitEvent: false}); + if (this.disabled) { + this.filterListFormGroup.disable({emitEvent: false}); + } else { + this.filterListFormGroup.enable({emitEvent: false}); + } } } diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html index 62a6325e4c..d0c943c6fc 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html @@ -36,7 +36,7 @@
+ *ngFor="let keyFilterControl of keyFiltersFormArray.controls; let $index = index">
filter.operation.and
@@ -63,7 +63,7 @@
- filter.no-key-filters diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts index 4ffeb95f2d..9f5fc5d162 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -28,7 +28,7 @@ import { Validator, Validators } from '@angular/forms'; -import { Observable, Subscription } from 'rxjs'; +import { Observable, Subject } from 'rxjs'; import { EntityKeyType, entityKeyTypeTranslationMap, @@ -39,6 +39,7 @@ import { MatDialog } from '@angular/material/dialog'; import { deepClone } from '@core/utils'; import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; import { EntityId } from '@shared/models/id/entity-id'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-key-filter-list', @@ -57,7 +58,7 @@ import { EntityId } from '@shared/models/id/entity-id'; } ] }) -export class KeyFilterListComponent implements ControlValueAccessor, Validator, OnInit { +export class KeyFilterListComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { @Input() disabled: boolean; @@ -75,22 +76,30 @@ export class KeyFilterListComponent implements ControlValueAccessor, Validator, keyFiltersControl: FormControl; + private destroy$ = new Subject(); private propagateChange = null; - private valueChangeSubscription: Subscription = null; - constructor(private fb: FormBuilder, private dialog: MatDialog) { } ngOnInit(): void { - this.keyFilterListFormGroup = this.fb.group({}); - this.keyFilterListFormGroup.addControl('keyFilters', - this.fb.array([])); + this.keyFilterListFormGroup = this.fb.group({ + keyFilters: this.fb.array([]) + }); this.keyFiltersControl = this.fb.control(null); + + this.keyFilterListFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - keyFiltersFormArray(): FormArray { + get keyFiltersFormArray(): FormArray { return this.keyFilterListFormGroup.get('keyFilters') as FormArray; } @@ -119,23 +128,21 @@ export class KeyFilterListComponent implements ControlValueAccessor, Validator, } writeValue(keyFilters: Array): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } - const keyFilterControls: Array = []; - if (keyFilters) { - for (const keyFilter of keyFilters) { - keyFilterControls.push(this.fb.control(keyFilter, [Validators.required])); - } - } - this.keyFilterListFormGroup.setControl('keyFilters', this.fb.array(keyFilterControls)); - this.valueChangeSubscription = this.keyFilterListFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); - if (this.disabled) { - this.keyFilterListFormGroup.disable({emitEvent: false}); + if (keyFilters.length === this.keyFiltersFormArray.length) { + this.keyFiltersFormArray.patchValue(keyFilters, {emitEvent: false}); } else { - this.keyFilterListFormGroup.enable({emitEvent: false}); + const keyFilterControls: Array = []; + if (keyFilters) { + for (const keyFilter of keyFilters) { + keyFilterControls.push(this.fb.control(keyFilter, [Validators.required])); + } + } + this.keyFilterListFormGroup.setControl('keyFilters', this.fb.array(keyFilterControls), {emitEvent: false}); + if (this.disabled) { + this.keyFilterListFormGroup.disable({emitEvent: false}); + } else { + this.keyFilterListFormGroup.enable({emitEvent: false}); + } } const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters); this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts index 00490b6f6e..3bae4dec89 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -23,15 +23,16 @@ import { FormControl, FormGroup, NG_VALIDATORS, - NG_VALUE_ACCESSOR, ValidationErrors, + NG_VALUE_ACCESSOR, Validator, Validators } from '@angular/forms'; import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; import { MatDialog } from '@angular/material/dialog'; -import { Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-create-alarm-rules', @@ -50,7 +51,7 @@ import { EntityId } from '@shared/models/id/entity-id'; } ] }) -export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, Validator { +export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { alarmSeverities = Object.keys(AlarmSeverity); alarmSeverityEnum = AlarmSeverity; @@ -67,8 +68,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, private usedSeverities: AlarmSeverity[] = []; - private valueChangeSubscription: Subscription = null; - + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private dialog: MatDialog, @@ -86,6 +86,14 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, this.createAlarmRulesFormGroup = this.fb.group({ createAlarmRules: this.fb.array([]) }); + this.createAlarmRulesFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } createAlarmRulesFormArray(): FormArray { @@ -102,9 +110,6 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, } writeValue(createAlarmRules: {[severity: string]: AlarmRule}): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } const createAlarmRulesControls: Array = []; if (createAlarmRules) { Object.keys(createAlarmRules).forEach((severity) => { @@ -118,15 +123,12 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, })); }); } - this.createAlarmRulesFormGroup.setControl('createAlarmRules', this.fb.array(createAlarmRulesControls)); + this.createAlarmRulesFormGroup.setControl('createAlarmRules', this.fb.array(createAlarmRulesControls), {emitEvent: false}); if (this.disabled) { this.createAlarmRulesFormGroup.disable({emitEvent: false}); } else { this.createAlarmRulesFormGroup.enable({emitEvent: false}); } - this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); this.updateUsedSeverities(); if (!this.disabled && !this.createAlarmRulesFormGroup.valid) { this.updateModel(); diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html index dde3df31c1..8ceb7ccc2b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html @@ -17,7 +17,7 @@ -->
-
-
+
device-profile.no-alarm-rules
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts index 8dc1e1430d..ca775bd48c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -32,9 +32,9 @@ import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; import { guid } from '@core/utils'; -import { Subscription } from 'rxjs'; -import { MatDialog } from '@angular/material/dialog'; +import { Subject } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-device-profile-alarms', @@ -53,7 +53,7 @@ import { EntityId } from '@shared/models/id/entity-id'; } ] }) -export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnInit, Validator { +export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { deviceProfileAlarmsFormGroup: FormGroup; @@ -72,13 +72,11 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni @Input() deviceProfileId: EntityId; - private valueChangeSubscription: Subscription = null; - + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: FormBuilder, - private dialog: MatDialog) { + private fb: FormBuilder) { } registerOnChange(fn: any): void { @@ -92,9 +90,17 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni this.deviceProfileAlarmsFormGroup = this.fb.group({ alarms: this.fb.array([]) }); + this.deviceProfileAlarmsFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - alarmsFormArray(): FormArray { + get alarmsFormArray(): FormArray { return this.deviceProfileAlarmsFormGroup.get('alarms') as FormArray; } @@ -108,24 +114,22 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni } writeValue(alarms: Array | null): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } - const alarmsControls: Array = []; - if (alarms) { - alarms.forEach((alarm) => { - alarmsControls.push(this.fb.control(alarm, [Validators.required])); - }); - } - this.deviceProfileAlarmsFormGroup.setControl('alarms', this.fb.array(alarmsControls)); - if (this.disabled) { - this.deviceProfileAlarmsFormGroup.disable({emitEvent: false}); + if (alarms?.length === this.alarmsFormArray.length) { + this.alarmsFormArray.patchValue(alarms, {emitEvent: false}); } else { - this.deviceProfileAlarmsFormGroup.enable({emitEvent: false}); + const alarmsControls: Array = []; + if (alarms) { + alarms.forEach((alarm) => { + alarmsControls.push(this.fb.control(alarm, [Validators.required])); + }); + } + this.deviceProfileAlarmsFormGroup.setControl('alarms', this.fb.array(alarmsControls), {emitEvent: false}); + if (this.disabled) { + this.deviceProfileAlarmsFormGroup.disable({emitEvent: false}); + } else { + this.deviceProfileAlarmsFormGroup.enable({emitEvent: false}); + } } - this.valueChangeSubscription = this.deviceProfileAlarmsFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); } public trackByAlarm(index: number, alarmControl: AbstractControl): string { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts index 072be36de9..cabf080779 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts @@ -26,7 +26,7 @@ import { Validator, Validators } from '@angular/forms'; -import { Subject, Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { AttributeName, AttributeNameTranslationMap, @@ -70,7 +70,6 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co attributesValueFormGroup: FormGroup; private propagateChange = null; - private valueChange$: Subscription = null; private destroy$ = new Subject(); private usedAttributesName: AttributeName[] = []; @@ -80,6 +79,9 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co this.attributesValueFormGroup = this.fb.group({ attributesValue: this.fb.array([]) }); + this.attributesValueFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); } ngOnInit() { @@ -92,9 +94,6 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co } ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } this.destroy$.next(); this.destroy$.complete(); } @@ -116,24 +115,18 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co } writeValue(keyValMap: AttributesNameValueMap): void { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } const attributesValueControls: Array = []; if (keyValMap) { (Object.keys(keyValMap) as AttributeName[]).forEach(name => { attributesValueControls.push(this.createdFormGroup({name, value: keyValMap[name]})); }); } - this.attributesValueFormGroup.setControl('attributesValue', this.fb.array(attributesValueControls)); + this.attributesValueFormGroup.setControl('attributesValue', this.fb.array(attributesValueControls), {emitEvent: false}); if (this.disabled) { this.attributesValueFormGroup.disable({emitEvent: false}); } else { this.attributesValueFormGroup.enable({emitEvent: false}); } - this.valueChange$ = this.attributesValueFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); this.updateUsedAttributesName(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html index e6d299f8fd..aae45e7b22 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html @@ -17,7 +17,7 @@ -->
-
+
-
+
device-profile.lwm2m.no-config-servers
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts index feeed358d9..d4c070e87a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts @@ -14,23 +14,24 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { AbstractControl, ControlValueAccessor, FormArray, - FormBuilder, FormControl, + FormBuilder, + FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { of, Subscription } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '@core/services/dialog.service'; import { MatDialog } from '@angular/material/dialog'; import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; -import { mergeMap } from 'rxjs/operators'; +import { mergeMap, takeUntil } from 'rxjs/operators'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; @@ -50,7 +51,7 @@ import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; } ] }) -export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValueAccessor { +export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValueAccessor, OnDestroy { bootstrapConfigServersFormGroup: FormGroup; @@ -72,8 +73,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu } } - private valueChangeSubscription: Subscription = null; - + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(public translate: TranslateService, @@ -94,9 +94,17 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu this.bootstrapConfigServersFormGroup = this.fb.group({ serverConfigs: this.fb.array([]) }); + this.bootstrapConfigServersFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - serverConfigsFromArray(): FormArray { + get serverConfigsFromArray(): FormArray { return this.bootstrapConfigServersFormGroup.get('serverConfigs') as FormArray; } @@ -110,24 +118,22 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu } writeValue(serverConfigs: Array | null): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } - const serverConfigsControls: Array = []; - if (serverConfigs) { - serverConfigs.forEach((serverConfig) => { - serverConfigsControls.push(this.fb.control(serverConfig)); - }); - } - this.bootstrapConfigServersFormGroup.setControl('serverConfigs', this.fb.array(serverConfigsControls)); - if (this.disabled) { - this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); + if (serverConfigs?.length === this.serverConfigsFromArray.length) { + this.serverConfigsFromArray.patchValue(serverConfigs, {emitEvent: false}); } else { - this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); + const serverConfigsControls: Array = []; + if (serverConfigs) { + serverConfigs.forEach((serverConfig) => { + serverConfigsControls.push(this.fb.control(serverConfig)); + }); + } + this.bootstrapConfigServersFormGroup.setControl('serverConfigs', this.fb.array(serverConfigsControls), {emitEvent: false}); + if (this.disabled) { + this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); + } else { + this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); + } } - this.valueChangeSubscription = this.bootstrapConfigServersFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); } trackByParams(index: number): number { @@ -147,7 +153,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu true ).subscribe((result) => { if (result) { - this.serverConfigsFromArray().removeAt(index); + this.serverConfigsFromArray.removeAt(index); } }); } @@ -169,7 +175,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu addServerConfigObs.subscribe((serverConfig) => { if (serverConfig) { serverConfig.securityMode = Lwm2mSecurityType.NO_SEC; - this.serverConfigsFromArray().push(this.fb.control(serverConfig)); + this.serverConfigsFromArray.push(this.fb.control(serverConfig)); this.updateModel(); } else { this.isTransportWasRunWithBootstrap = false; @@ -196,7 +202,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu } private isBootstrapAdded(): boolean { - const serverConfigsArray = this.serverConfigsFromArray().getRawValue(); + const serverConfigsArray = this.serverConfigsFromArray.getRawValue(); for (let i = 0; i < serverConfigsArray.length; i++) { if (serverConfigsArray[i].bootstrapServerIs) { return true; @@ -207,15 +213,15 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu private removeBootstrapServerConfig(): void { if (this.bootstrapConfigServersFormGroup) { - const bootstrapServerIndex = this.serverConfigsFromArray().getRawValue().findIndex(server => server.bootstrapServerIs === true); + const bootstrapServerIndex = this.serverConfigsFromArray.getRawValue().findIndex(server => server.bootstrapServerIs === true); if (bootstrapServerIndex !== -1) { - this.serverConfigsFromArray().removeAt(bootstrapServerIndex); + this.serverConfigsFromArray.removeAt(bootstrapServerIndex); } } } private updateModel() { - const serverConfigs: Array = this.serverConfigsFromArray().value; + const serverConfigs: Array = this.serverConfigsFromArray.value; this.propagateChange(serverConfigs); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts index 4aa89a4f6a..47d03726b6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts @@ -80,6 +80,8 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue this.instancesFormGroup = this.fb.group({ instances: this.fb.array([]) }); + + this.valueChange$ = this.instancesFormGroup.valueChanges.subscribe(value => this.updateModel(value.instances)); } ngOnDestroy() { @@ -122,22 +124,16 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue if (instances.length === this.instancesFormArray.length) { this.instancesFormArray.patchValue(instances, {emitEvent: false}); } else { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } const instancesControl: Array = []; if (instances) { instances.forEach((instance) => { instancesControl.push(this.createInstanceFormGroup(instance)); }); } - this.instancesFormGroup.setControl('instances', this.fb.array(instancesControl)); + this.instancesFormGroup.setControl('instances', this.fb.array(instancesControl), {emitEvent: false}); if (this.disabled) { this.instancesFormGroup.disable({emitEvent: false}); } - this.valueChange$ = this.instancesFormGroup.valueChanges.subscribe(value => { - this.updateModel(value.instances); - }); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts index 1d2353de04..844d8f4ba5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts @@ -29,7 +29,7 @@ import { } from '@angular/forms'; import { ResourceLwM2M } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { combineLatest, Subject, Subscription } from 'rxjs'; +import { combineLatest, Subject } from 'rxjs'; import { startWith, takeUntil } from 'rxjs/operators'; @Component({ @@ -71,19 +71,19 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue } private destroy$ = new Subject(); - private valueChange$: Subscription = null; private propagateChange = (v: any) => { }; constructor(private fb: FormBuilder) { this.resourcesFormGroup = this.fb.group({ resources: this.fb.array([]) }); + + this.resourcesFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel(this.resourcesFormGroup.getRawValue().resources)); } ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } this.destroy$.next(); this.destroy$.complete(); } @@ -131,24 +131,18 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue private updatedResources(resources: ResourceLwM2M[]): void { if (resources.length === this.resourcesFormArray.length) { - this.resourcesFormArray.patchValue(resources, {onlySelf: true}); + this.resourcesFormArray.patchValue(resources, {onlySelf: true, emitEvent: false}); } else { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } const resourcesControl: Array = []; if (resources) { resources.forEach((resource) => { resourcesControl.push(this.createdResourceFormGroup(resource)); }); } - this.resourcesFormGroup.setControl('resources', this.fb.array(resourcesControl)); + this.resourcesFormGroup.setControl('resources', this.fb.array(resourcesControl), {emitEvent: false}); if (this.disabled) { this.resourcesFormGroup.disable({emitEvent: false}); } - this.valueChange$ = this.resourcesFormGroup.valueChanges.subscribe(() => { - this.updateModel(this.resourcesFormGroup.getRawValue().resources); - }); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index f7a99b9ac9..51ffe37be5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -86,6 +86,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, this.modelsFormGroup = this.fb.group({ models: this.fb.array([]) }); + + this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => this.updateModel(value.models)); } ngOnDestroy() { @@ -130,20 +132,14 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, if (models.length === this.modelsFormArray.length) { this.modelsFormArray.patchValue(models, {emitEvent: false}); } else { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } const modelControls: Array = []; models.forEach(model => { modelControls.push(this.createModelFormGroup(model)); }); - this.modelsFormGroup.setControl('models', this.fb.array(modelControls)); + this.modelsFormGroup.setControl('models', this.fb.array(modelControls), {emitEvent: false}); if (this.disabled) { this.modelsFormGroup.disable({emitEvent: false}); } - this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => { - this.updateModel(value.models); - }); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html index 24907a5265..b20a8e2fc9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html @@ -16,7 +16,7 @@ -->
-
@@ -58,7 +58,7 @@ remove_circle_outline
-
+
device-profile.snmp.please-add-communication-config
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts index 6fe20f5e2f..d99d41724c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts @@ -27,7 +27,7 @@ import { Validators } from '@angular/forms'; import { SnmpCommunicationConfig, SnmpSpecType, SnmpSpecTypeTranslationMap } from '@shared/models/device.models'; -import { Subject, Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { isUndefinedOrNull } from '@core/utils'; import { takeUntil } from 'rxjs/operators'; @@ -58,7 +58,6 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On disabled: boolean; private usedSpecType: SnmpSpecType[] = []; - private valueChange$: Subscription = null; private destroy$ = new Subject(); private propagateChange = (v: any) => { }; @@ -68,17 +67,17 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On this.deviceProfileCommunicationConfig = this.fb.group({ communicationConfig: this.fb.array([]) }); + this.deviceProfileCommunicationConfig.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); } ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } this.destroy$.next(); this.destroy$.complete(); } - communicationConfigFormArray(): FormArray { + get communicationConfigFormArray(): FormArray { return this.deviceProfileCommunicationConfig.get('communicationConfig') as FormArray; } @@ -99,27 +98,27 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On } writeValue(communicationConfig: SnmpCommunicationConfig[]) { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } - const communicationConfigControl: Array = []; - if (communicationConfig) { - communicationConfig.forEach((config) => { - communicationConfigControl.push(this.createdFormGroup(config)); - }); - } - this.deviceProfileCommunicationConfig.setControl('communicationConfig', this.fb.array(communicationConfigControl)); - if (!communicationConfig || !communicationConfig.length) { - this.addCommunicationConfig(); - } - if (this.disabled) { - this.deviceProfileCommunicationConfig.disable({emitEvent: false}); + if (communicationConfig?.length === this.communicationConfigFormArray.length) { + this.communicationConfigFormArray.patchValue(communicationConfig, {emitEvent: false}); } else { - this.deviceProfileCommunicationConfig.enable({emitEvent: false}); + const communicationConfigControl: Array = []; + if (communicationConfig) { + communicationConfig.forEach((config) => { + communicationConfigControl.push(this.createdFormGroup(config)); + }); + } + this.deviceProfileCommunicationConfig.setControl( + 'communicationConfig', this.fb.array(communicationConfigControl), {emitEvent: false} + ); + if (!communicationConfig || !communicationConfig.length) { + this.addCommunicationConfig(); + } + if (this.disabled) { + this.deviceProfileCommunicationConfig.disable({emitEvent: false}); + } else { + this.deviceProfileCommunicationConfig.enable({emitEvent: false}); + } } - this.valueChange$ = this.deviceProfileCommunicationConfig.valueChanges.subscribe(() => { - this.updateModel(); - }); this.updateUsedSpecType(); if (!this.disabled && !this.deviceProfileCommunicationConfig.valid) { this.updateModel(); @@ -133,16 +132,16 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On } public removeCommunicationConfig(index: number) { - this.communicationConfigFormArray().removeAt(index); + this.communicationConfigFormArray.removeAt(index); } get isAddEnabled(): boolean { - return this.communicationConfigFormArray().length !== Object.keys(SnmpSpecType).length; + return this.communicationConfigFormArray.length !== Object.keys(SnmpSpecType).length; } public addCommunicationConfig() { - this.communicationConfigFormArray().push(this.createdFormGroup()); + this.communicationConfigFormArray.push(this.createdFormGroup()); this.deviceProfileCommunicationConfig.updateValueAndValidity(); if (!this.deviceProfileCommunicationConfig.valid) { this.updateModel(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html index e202370d21..0d576b91de 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html @@ -25,7 +25,7 @@
-
@@ -67,7 +67,7 @@
-
+
device-profile.snmp.please-add-mapping-config
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts index 2b8192908b..8f74a82f55 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts @@ -69,6 +69,7 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con this.mappingsConfigForm = this.fb.group({ mappings: this.fb.array([]) }); + this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => this.updateModel()); } ngOnDestroy() { @@ -100,38 +101,36 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con } writeValue(mappings: SnmpMapping[]) { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } - const mappingsControl: Array = []; - if (mappings) { - mappings.forEach((config) => { - mappingsControl.push(this.createdFormGroup(config)); - }); - } - this.mappingsConfigForm.setControl('mappings', this.fb.array(mappingsControl)); - if (!mappings || !mappings.length) { - this.addMappingConfig(); - } - if (this.disabled) { - this.mappingsConfigForm.disable({emitEvent: false}); + if (mappings?.length === this.mappingsConfigFormArray.length) { + this.mappingsConfigFormArray.patchValue(mappings, {emitEvent: false}); } else { - this.mappingsConfigForm.enable({emitEvent: false}); + const mappingsControl: Array = []; + if (mappings) { + mappings.forEach((config) => { + mappingsControl.push(this.createdFormGroup(config)); + }); + } + this.mappingsConfigForm.setControl('mappings', this.fb.array(mappingsControl), {emitEvent: false}); + if (!mappings || !mappings.length) { + this.addMappingConfig(); + } + if (this.disabled) { + this.mappingsConfigForm.disable({emitEvent: false}); + } else { + this.mappingsConfigForm.enable({emitEvent: false}); + } } - this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => { - this.updateModel(); - }); if (!this.disabled && !this.mappingsConfigForm.valid) { this.updateModel(); } } - mappingsConfigFormArray(): FormArray { + get mappingsConfigFormArray(): FormArray { return this.mappingsConfigForm.get('mappings') as FormArray; } public addMappingConfig() { - this.mappingsConfigFormArray().push(this.createdFormGroup()); + this.mappingsConfigFormArray.push(this.createdFormGroup()); this.mappingsConfigForm.updateValueAndValidity(); if (!this.mappingsConfigForm.valid) { this.updateModel(); @@ -139,7 +138,7 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con } public removeMappingConfig(index: number) { - this.mappingsConfigFormArray().removeAt(index); + this.mappingsConfigFormArray.removeAt(index); } private createdFormGroup(value?: SnmpMapping): FormGroup { diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts index aa5325e974..1c5b110059 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts @@ -30,10 +30,11 @@ import { import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { QueueInfo } from '@shared/models/queue.models'; import { UtilsService } from '@core/services/utils.service'; import { guid } from '@core/utils'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-tenant-profile-queues', @@ -70,8 +71,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid @Input() disabled: boolean; - private valueChangeSubscription$: Subscription = null; - + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private store: Store, @@ -83,12 +83,6 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid this.propagateChange = fn; } - ngOnDestroy() { - if (this.valueChangeSubscription$) { - this.valueChangeSubscription$.unsubscribe(); - } - } - registerOnTouched(fn: any): void { } @@ -96,6 +90,15 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid this.tenantProfileQueuesFormGroup = this.fb.group({ queues: this.fb.array([]) }); + + this.tenantProfileQueuesFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } get queuesFormArray(): FormArray { @@ -112,30 +115,28 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid } writeValue(queues: Array | null): void { - if (this.valueChangeSubscription$) { - this.valueChangeSubscription$.unsubscribe(); - } - const queuesControls: Array = []; - if (queues) { - queues.forEach((queue, index) => { - if (!queue.id) { - if (!this.idMap[index]) { - this.idMap.push(guid()); - } - queue.id = this.idMap[index]; - } - queuesControls.push(this.fb.control(queue, [Validators.required])); - }); - } - this.tenantProfileQueuesFormGroup.setControl('queues', this.fb.array(queuesControls)); - if (this.disabled) { - this.tenantProfileQueuesFormGroup.disable({emitEvent: false}); + if (queues.length === this.queuesFormArray.length) { + this.queuesFormArray.patchValue(queues, {emitEvent: false}); } else { - this.tenantProfileQueuesFormGroup.enable({emitEvent: false}); + const queuesControls: Array = []; + if (queues) { + queues.forEach((queue, index) => { + if (!queue.id) { + if (!this.idMap[index]) { + this.idMap.push(guid()); + } + queue.id = this.idMap[index]; + } + queuesControls.push(this.fb.control(queue, [Validators.required])); + }); + } + this.tenantProfileQueuesFormGroup.setControl('queues', this.fb.array(queuesControls), {emitEvent: false}); + if (this.disabled) { + this.tenantProfileQueuesFormGroup.disable({emitEvent: false}); + } else { + this.tenantProfileQueuesFormGroup.enable({emitEvent: false}); + } } - this.valueChangeSubscription$ = this.tenantProfileQueuesFormGroup.valueChanges.subscribe(() => - this.updateModel() - ); } public trackByQueue(index: number, queueControl: AbstractControl) { diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html index 1f5d6273ec..af1f663c1a 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html @@ -17,17 +17,17 @@ -->
-
+
relation.type entity.entity-types  
-
+
+ *ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
-
+
relation.any-relation
diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts index 911aa4fe30..a19d16c689 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -28,7 +28,8 @@ import { RelationEntityTypeFilter } from '@shared/models/relation.models'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-relation-filters', @@ -42,7 +43,7 @@ import { Subscription } from 'rxjs'; } ] }) -export class RelationFiltersComponent extends PageComponent implements ControlValueAccessor, OnInit { +export class RelationFiltersComponent extends PageComponent implements ControlValueAccessor, OnInit, OnDestroy { @Input() disabled: boolean; @@ -50,22 +51,32 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa relationFiltersFormGroup: FormGroup; + private destroy$ = new Subject(); private propagateChange = null; - private valueChangeSubscription: Subscription = null; - constructor(protected store: Store, private fb: FormBuilder) { super(store); } ngOnInit(): void { - this.relationFiltersFormGroup = this.fb.group({}); - this.relationFiltersFormGroup.addControl('relationFilters', - this.fb.array([])); + this.relationFiltersFormGroup = this.fb.group({ + relationFilters: this.fb.array([]) + }); + + this.relationFiltersFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { + this.updateModel(); + }); } - relationFiltersFormArray(): FormArray { + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + get relationFiltersFormArray(): FormArray { return this.relationFiltersFormGroup.get('relationFilters') as FormArray; } @@ -81,19 +92,17 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa } writeValue(filters: Array): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } - const relationFiltersControls: Array = []; - if (filters && filters.length) { - filters.forEach((filter) => { - relationFiltersControls.push(this.createRelationFilterFormGroup(filter)); - }); + if (filters?.length === this.relationFiltersFormArray.length) { + this.relationFiltersFormArray.patchValue(filters, {emitEvent: false}); + } else { + const relationFiltersControls: Array = []; + if (filters && filters.length) { + filters.forEach((filter) => { + relationFiltersControls.push(this.createRelationFilterFormGroup(filter)); + }); + } + this.relationFiltersFormGroup.setControl('relationFilters', this.fb.array(relationFiltersControls), {emitEvent: false}); } - this.relationFiltersFormGroup.setControl('relationFilters', this.fb.array(relationFiltersControls)); - this.valueChangeSubscription = this.relationFiltersFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); } public removeFilter(index: number) { @@ -101,12 +110,11 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa } public addFilter() { - const relationFiltersFormArray = this.relationFiltersFormGroup.get('relationFilters') as FormArray; const filter: RelationEntityTypeFilter = { relationType: null, entityTypes: [] }; - relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter)); + this.relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter)); } private createRelationFilterFormGroup(filter: RelationEntityTypeFilter): AbstractControl { diff --git a/ui-ngx/src/app/shared/components/kv-map.component.ts b/ui-ngx/src/app/shared/components/kv-map.component.ts index b32c8c2f4a..21d17a840d 100644 --- a/ui-ngx/src/app/shared/components/kv-map.component.ts +++ b/ui-ngx/src/app/shared/components/kv-map.component.ts @@ -30,7 +30,8 @@ import { import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-key-val-map', @@ -63,19 +64,27 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc kvListFormGroup: FormGroup; + private destroy$ = new Subject(); private propagateChange = null; - private valueChangeSubscription: Subscription = null; - constructor(protected store: Store, private fb: FormBuilder) { super(store); } ngOnInit(): void { - this.kvListFormGroup = this.fb.group({}); - this.kvListFormGroup.addControl('keyVals', - this.fb.array([])); + this.kvListFormGroup = this.fb.group({ + keyVals: this.fb.array([]) + }); + + this.kvListFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } keyValsFormArray(): FormArray { @@ -99,9 +108,6 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc } writeValue(keyValMap: {[key: string]: string}): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); - } const keyValsControls: Array = []; if (keyValMap) { for (const property of Object.keys(keyValMap)) { @@ -113,10 +119,7 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc } } } - this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls)); - this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); + this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls), {emitEvent: false}); if (this.disabled) { this.kvListFormGroup.disable({emitEvent: false}); } else { From 36e040d0a7735dad31b196e70e06a314e763a7bb Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 12 Dec 2022 10:31:58 +0200 Subject: [PATCH 2/5] UI: optimaze imports --- .../home/components/relation/relation-filters.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts index a19d16c689..009c2fc5f8 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts @@ -28,7 +28,7 @@ import { RelationEntityTypeFilter } from '@shared/models/relation.models'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Subject, Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({ From f2088ad53bcb93dc1f888d0f0b24994d9ef1264c Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 8 Feb 2023 11:02:29 +0200 Subject: [PATCH 3/5] UI: Refactoring --- .../components/filter/filter-predicate-list.component.ts | 8 +++----- .../home/components/filter/key-filter-list.component.ts | 2 +- .../profile/alarm/create-alarm-rules.component.ts | 2 +- .../profile/alarm/device-profile-alarms.component.ts | 5 ++--- .../lwm2m/lwm2m-bootstrap-config-servers.component.ts | 2 +- .../profile/queue/tenant-profile-queues.component.ts | 2 +- .../components/relation/relation-filters.component.ts | 2 +- ui-ngx/src/app/shared/components/kv-map.component.ts | 2 +- 8 files changed, 11 insertions(+), 14 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts index dbc95aa141..78e7252a72 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts @@ -27,7 +27,7 @@ import { Validator, Validators } from '@angular/forms'; -import { Observable, of, Subject, Subscription } from 'rxjs'; +import { Observable, of, Subject } from 'rxjs'; import { ComplexFilterPredicateInfo, ComplexOperation, @@ -81,11 +81,9 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid complexOperationTranslations = complexOperationTranslationMap; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = null; - private valueChangeSubscription: Subscription = null; - constructor(private fb: UntypedFormBuilder, @Inject(COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN) private complexFilterPredicateDialogComponent: ComponentType, private dialog: MatDialog) { @@ -151,7 +149,7 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid } public removePredicate(index: number) { - (this.filterListFormGroup.get('predicates') as UntypedFormArray).removeAt(index); + this.predicatesFormArray.removeAt(index); } public addPredicate(complex: boolean) { diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts index 029eba0990..d427c275ce 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts @@ -76,7 +76,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, Validator, keyFiltersControl: UntypedFormControl; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = null; constructor(private fb: UntypedFormBuilder, diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts index 0cbdb722d7..1948ca51d2 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts @@ -68,7 +68,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, private usedSeverities: AlarmSeverity[] = []; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private dialog: MatDialog, diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts index dd496fbedd..a828040e9c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts @@ -72,12 +72,11 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni @Input() deviceProfileId: EntityId; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private store: Store, - private fb: UntypedFormBuilder, - private dialog: MatDialog) { + private fb: UntypedFormBuilder) { } registerOnChange(fn: any): void { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts index 3220a670af..3b70918a1e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts @@ -72,7 +72,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu } } - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(public translate: TranslateService, diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts index 7e98a4b3fd..c3ff3ccc4c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts @@ -71,7 +71,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid @Input() disabled: boolean; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private store: Store, diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts index 255a878977..aa027c4846 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts @@ -51,7 +51,7 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa relationFiltersFormGroup: UntypedFormGroup; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = null; constructor(protected store: Store, diff --git a/ui-ngx/src/app/shared/components/kv-map.component.ts b/ui-ngx/src/app/shared/components/kv-map.component.ts index c86aa84ef0..6772214390 100644 --- a/ui-ngx/src/app/shared/components/kv-map.component.ts +++ b/ui-ngx/src/app/shared/components/kv-map.component.ts @@ -64,7 +64,7 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc kvListFormGroup: UntypedFormGroup; - private destroy$ = new Subject(); + private destroy$ = new Subject(); private propagateChange = null; constructor(protected store: Store, From 8a02f3643cc5ae10e1a1ed52fd9ccc668a026a67 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 8 Feb 2023 15:50:57 +0200 Subject: [PATCH 4/5] UI: Refactoring value change to destroy --- .../snmp-device-profile-mapping.component.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts index 2f7ba0ff74..428eb6b86e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts @@ -18,19 +18,20 @@ import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms'; import { SnmpMapping } from '@shared/models/device.models'; -import { Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { DataType, DataTypeTranslationMap } from '@shared/models/constants'; import { isUndefinedOrNull } from '@core/utils'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-snmp-device-profile-mapping', @@ -60,7 +61,7 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con private readonly oidPattern: RegExp = /^\.?([0-2])((\.0)|(\.[1-9][0-9]*))*$/; - private valueChange$: Subscription = null; + private destroy$ = new Subject(); private propagateChange = (v: any) => { }; constructor(private fb: UntypedFormBuilder) { } @@ -69,13 +70,14 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con this.mappingsConfigForm = this.fb.group({ mappings: this.fb.array([]) }); - this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => this.updateModel()); + this.mappingsConfigForm.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); } ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); - } + this.destroy$.next(); + this.destroy$.complete(); } registerOnChange(fn: any) { From 2098e1f3ff07c617e27abca886a5a5418c0dfc37 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 27 Mar 2023 15:29:31 +0300 Subject: [PATCH 5/5] UI: Refactoring --- ui-ngx/src/app/shared/components/kv-map.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/kv-map.component.ts b/ui-ngx/src/app/shared/components/kv-map.component.ts index 6540ae0af0..abbb52e60c 100644 --- a/ui-ngx/src/app/shared/components/kv-map.component.ts +++ b/ui-ngx/src/app/shared/components/kv-map.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -51,7 +51,7 @@ import { takeUntil } from 'rxjs/operators'; } ] }) -export class KeyValMapComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { +export class KeyValMapComponent extends PageComponent implements ControlValueAccessor, OnInit, OnDestroy, Validator { @Input() disabled: boolean;