Browse Source

UI: Refactoring Form group with form array controls

pull/7780/head
Artem Dzhereleiko 4 years ago
parent
commit
922c08891f
  1. 4
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html
  2. 57
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts
  3. 4
      ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html
  4. 57
      ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts
  5. 28
      ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts
  6. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html
  7. 54
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts
  8. 17
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts
  9. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html
  10. 66
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts
  11. 10
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts
  12. 20
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts
  13. 10
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts
  14. 4
      ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html
  15. 55
      ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts
  16. 4
      ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html
  17. 43
      ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.ts
  18. 63
      ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts
  19. 8
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html
  20. 54
      ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts
  21. 29
      ui-ngx/src/app/shared/components/kv-map.component.ts

4
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html

@ -42,7 +42,7 @@
<div class="predicate-list"> <div class="predicate-list">
<div fxLayout="row" fxLayoutAlign="start center" style="height: 45px;" <div fxLayout="row" fxLayoutAlign="start center" style="height: 45px;"
formArrayName="predicates" formArrayName="predicates"
*ngFor="let predicateControl of predicatesFormArray().controls; let $index = index"> *ngFor="let predicateControl of predicatesFormArray.controls; let $index = index">
<div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation"> <div fxFlex="8" fxLayout="row" fxLayoutAlign="center" class="filters-operation">
<span *ngIf="$index > 0">{{ complexOperationTranslations.get(operation) | translate }}</span> <span *ngIf="$index > 0">{{ complexOperationTranslations.get(operation) | translate }}</span>
</div> </div>
@ -68,7 +68,7 @@
</div> </div>
</div> </div>
</div> </div>
<span [fxShow]="!predicatesFormArray().length" <span [fxShow]="!predicatesFormArray.length"
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}" fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
class="no-data-found" translate>filter.no-filters</span> class="no-data-found" translate>filter.no-filters</span>
</div> </div>

57
ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Inject, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Inject, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -27,7 +27,7 @@ import {
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Observable, of, Subscription } from 'rxjs'; import { Observable, of, Subject, Subscription } from 'rxjs';
import { import {
ComplexFilterPredicateInfo, ComplexFilterPredicateInfo,
ComplexOperation, ComplexOperation,
@ -37,7 +37,7 @@ import {
KeyFilterPredicateInfo KeyFilterPredicateInfo
} from '@shared/models/query/query.models'; } from '@shared/models/query/query.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { map } from 'rxjs/operators'; import { map, takeUntil } from 'rxjs/operators';
import { ComponentType } from '@angular/cdk/portal'; import { ComponentType } from '@angular/cdk/portal';
import { COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN } from '@home/components/tokens'; import { COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN } from '@home/components/tokens';
import { ComplexFilterPredicateDialogData } from '@home/components/filter/filter-component.models'; 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; @Input() disabled: boolean;
@ -81,22 +81,29 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid
complexOperationTranslations = complexOperationTranslationMap; complexOperationTranslations = complexOperationTranslationMap;
private destroy$ = new Subject();
private propagateChange = null; private propagateChange = null;
private valueChangeSubscription: Subscription = null;
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
@Inject(COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN) private complexFilterPredicateDialogComponent: ComponentType<any>, @Inject(COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN) private complexFilterPredicateDialogComponent: ComponentType<any>,
private dialog: MatDialog) { private dialog: MatDialog) {
} }
ngOnInit(): void { ngOnInit(): void {
this.filterListFormGroup = this.fb.group({}); this.filterListFormGroup = this.fb.group({
this.filterListFormGroup.addControl('predicates', predicates: this.fb.array([])
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; return this.filterListFormGroup.get('predicates') as FormArray;
} }
@ -123,23 +130,21 @@ export class FilterPredicateListComponent implements ControlValueAccessor, Valid
} }
writeValue(predicates: Array<KeyFilterPredicateInfo>): void { writeValue(predicates: Array<KeyFilterPredicateInfo>): void {
if (this.valueChangeSubscription) { if (predicates.length === this.predicatesFormArray.length) {
this.valueChangeSubscription.unsubscribe(); this.predicatesFormArray.patchValue(predicates, {emitEvent: false});
}
const predicateControls: Array<AbstractControl> = [];
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});
} else { } else {
this.filterListFormGroup.enable({emitEvent: false}); const predicateControls: Array<AbstractControl> = [];
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});
}
} }
} }

4
ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html

@ -36,7 +36,7 @@
<div class="key-filter-list"> <div class="key-filter-list">
<div fxLayout="row" fxLayoutAlign="start center" style="max-height: 40px;" <div fxLayout="row" fxLayoutAlign="start center" style="max-height: 40px;"
formArrayName="keyFilters" formArrayName="keyFilters"
*ngFor="let keyFilterControl of keyFiltersFormArray().controls; let $index = index"> *ngFor="let keyFilterControl of keyFiltersFormArray.controls; let $index = index">
<div fxFlex="8" class="filters-operation"> <div fxFlex="8" class="filters-operation">
<span *ngIf="$index > 0" translate>filter.operation.and</span> <span *ngIf="$index > 0" translate>filter.operation.and</span>
</div> </div>
@ -63,7 +63,7 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
</div> </div>
</div> </div>
<span [fxShow]="!keyFiltersFormArray().length" <span [fxShow]="!keyFiltersFormArray.length"
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}" fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
class="no-data-found" translate>filter.no-key-filters</span> class="no-data-found" translate>filter.no-key-filters</span>
</div> </div>

57
ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -28,7 +28,7 @@ import {
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { import {
EntityKeyType, EntityKeyType,
entityKeyTypeTranslationMap, entityKeyTypeTranslationMap,
@ -39,6 +39,7 @@ import { MatDialog } from '@angular/material/dialog';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-key-filter-list', 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; @Input() disabled: boolean;
@ -75,22 +76,30 @@ export class KeyFilterListComponent implements ControlValueAccessor, Validator,
keyFiltersControl: FormControl; keyFiltersControl: FormControl;
private destroy$ = new Subject();
private propagateChange = null; private propagateChange = null;
private valueChangeSubscription: Subscription = null;
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
private dialog: MatDialog) { private dialog: MatDialog) {
} }
ngOnInit(): void { ngOnInit(): void {
this.keyFilterListFormGroup = this.fb.group({}); this.keyFilterListFormGroup = this.fb.group({
this.keyFilterListFormGroup.addControl('keyFilters', keyFilters: this.fb.array([])
this.fb.array([])); });
this.keyFiltersControl = this.fb.control(null); 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; return this.keyFilterListFormGroup.get('keyFilters') as FormArray;
} }
@ -119,23 +128,21 @@ export class KeyFilterListComponent implements ControlValueAccessor, Validator,
} }
writeValue(keyFilters: Array<KeyFilterInfo>): void { writeValue(keyFilters: Array<KeyFilterInfo>): void {
if (this.valueChangeSubscription) { if (keyFilters.length === this.keyFiltersFormArray.length) {
this.valueChangeSubscription.unsubscribe(); this.keyFiltersFormArray.patchValue(keyFilters, {emitEvent: false});
}
const keyFilterControls: Array<AbstractControl> = [];
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});
} else { } else {
this.keyFilterListFormGroup.enable({emitEvent: false}); const keyFilterControls: Array<AbstractControl> = [];
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); const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters);
this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false}); this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false});

28
ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -23,15 +23,16 @@ import {
FormControl, FormControl,
FormGroup, FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, ValidationErrors, NG_VALUE_ACCESSOR,
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-create-alarm-rules', 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); alarmSeverities = Object.keys(AlarmSeverity);
alarmSeverityEnum = AlarmSeverity; alarmSeverityEnum = AlarmSeverity;
@ -67,8 +68,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
private usedSeverities: AlarmSeverity[] = []; private usedSeverities: AlarmSeverity[] = [];
private valueChangeSubscription: Subscription = null; private destroy$ = new Subject();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private dialog: MatDialog, constructor(private dialog: MatDialog,
@ -86,6 +86,14 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
this.createAlarmRulesFormGroup = this.fb.group({ this.createAlarmRulesFormGroup = this.fb.group({
createAlarmRules: this.fb.array([]) createAlarmRules: this.fb.array([])
}); });
this.createAlarmRulesFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
} }
createAlarmRulesFormArray(): FormArray { createAlarmRulesFormArray(): FormArray {
@ -102,9 +110,6 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
} }
writeValue(createAlarmRules: {[severity: string]: AlarmRule}): void { writeValue(createAlarmRules: {[severity: string]: AlarmRule}): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
}
const createAlarmRulesControls: Array<AbstractControl> = []; const createAlarmRulesControls: Array<AbstractControl> = [];
if (createAlarmRules) { if (createAlarmRules) {
Object.keys(createAlarmRules).forEach((severity) => { 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) { if (this.disabled) {
this.createAlarmRulesFormGroup.disable({emitEvent: false}); this.createAlarmRulesFormGroup.disable({emitEvent: false});
} else { } else {
this.createAlarmRulesFormGroup.enable({emitEvent: false}); this.createAlarmRulesFormGroup.enable({emitEvent: false});
} }
this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
this.updateUsedSeverities(); this.updateUsedSeverities();
if (!this.disabled && !this.createAlarmRulesFormGroup.valid) { if (!this.disabled && !this.createAlarmRulesFormGroup.valid) {
this.updateModel(); this.updateModel();

4
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html

@ -17,7 +17,7 @@
--> -->
<div fxLayout="column"> <div fxLayout="column">
<div class="tb-device-profile-alarms"> <div class="tb-device-profile-alarms">
<div *ngFor="let alarmControl of alarmsFormArray().controls; trackBy: trackByAlarm; <div *ngFor="let alarmControl of alarmsFormArray.controls; trackBy: trackByAlarm;
let $index = index; last as isLast;" let $index = index; last as isLast;"
fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
<tb-device-profile-alarm [formControl]="alarmControl" <tb-device-profile-alarm [formControl]="alarmControl"
@ -27,7 +27,7 @@
</tb-device-profile-alarm> </tb-device-profile-alarm>
</div> </div>
</div> </div>
<div *ngIf="!alarmsFormArray().controls.length"> <div *ngIf="!alarmsFormArray.controls.length">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"
class="tb-prompt">device-profile.no-alarm-rules</span> class="tb-prompt">device-profile.no-alarm-rules</span>
</div> </div>

54
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -32,9 +32,9 @@ import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models'; import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models';
import { guid } from '@core/utils'; import { guid } from '@core/utils';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { MatDialog } from '@angular/material/dialog';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-device-profile-alarms', 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; deviceProfileAlarmsFormGroup: FormGroup;
@ -72,13 +72,11 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
@Input() @Input()
deviceProfileId: EntityId; deviceProfileId: EntityId;
private valueChangeSubscription: Subscription = null; private destroy$ = new Subject();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private fb: FormBuilder, private fb: FormBuilder) {
private dialog: MatDialog) {
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -92,9 +90,17 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
this.deviceProfileAlarmsFormGroup = this.fb.group({ this.deviceProfileAlarmsFormGroup = this.fb.group({
alarms: this.fb.array([]) 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; return this.deviceProfileAlarmsFormGroup.get('alarms') as FormArray;
} }
@ -108,24 +114,22 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
} }
writeValue(alarms: Array<DeviceProfileAlarm> | null): void { writeValue(alarms: Array<DeviceProfileAlarm> | null): void {
if (this.valueChangeSubscription) { if (alarms?.length === this.alarmsFormArray.length) {
this.valueChangeSubscription.unsubscribe(); this.alarmsFormArray.patchValue(alarms, {emitEvent: false});
}
const alarmsControls: Array<AbstractControl> = [];
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});
} else { } else {
this.deviceProfileAlarmsFormGroup.enable({emitEvent: false}); const alarmsControls: Array<AbstractControl> = [];
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 { public trackByAlarm(index: number, alarmControl: AbstractControl): string {

17
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts

@ -26,7 +26,7 @@ import {
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Subject, Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { import {
AttributeName, AttributeName,
AttributeNameTranslationMap, AttributeNameTranslationMap,
@ -70,7 +70,6 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
attributesValueFormGroup: FormGroup; attributesValueFormGroup: FormGroup;
private propagateChange = null; private propagateChange = null;
private valueChange$: Subscription = null;
private destroy$ = new Subject(); private destroy$ = new Subject();
private usedAttributesName: AttributeName[] = []; private usedAttributesName: AttributeName[] = [];
@ -80,6 +79,9 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
this.attributesValueFormGroup = this.fb.group({ this.attributesValueFormGroup = this.fb.group({
attributesValue: this.fb.array([]) attributesValue: this.fb.array([])
}); });
this.attributesValueFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel());
} }
ngOnInit() { ngOnInit() {
@ -92,9 +94,6 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
} }
ngOnDestroy() { ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
@ -116,24 +115,18 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
} }
writeValue(keyValMap: AttributesNameValueMap): void { writeValue(keyValMap: AttributesNameValueMap): void {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
const attributesValueControls: Array<AbstractControl> = []; const attributesValueControls: Array<AbstractControl> = [];
if (keyValMap) { if (keyValMap) {
(Object.keys(keyValMap) as AttributeName[]).forEach(name => { (Object.keys(keyValMap) as AttributeName[]).forEach(name => {
attributesValueControls.push(this.createdFormGroup({name, value: keyValMap[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) { if (this.disabled) {
this.attributesValueFormGroup.disable({emitEvent: false}); this.attributesValueFormGroup.disable({emitEvent: false});
} else { } else {
this.attributesValueFormGroup.enable({emitEvent: false}); this.attributesValueFormGroup.enable({emitEvent: false});
} }
this.valueChange$ = this.attributesValueFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
this.updateUsedAttributesName(); this.updateUsedAttributesName();
} }

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.html

@ -17,7 +17,7 @@
--> -->
<div fxLayout="column"> <div fxLayout="column">
<mat-accordion multi="true"> <mat-accordion multi="true">
<div *ngFor="let serverConfig of serverConfigsFromArray().controls; trackBy: trackByParams; let $index = index;"> <div *ngFor="let serverConfig of serverConfigsFromArray.controls; trackBy: trackByParams; let $index = index;">
<tb-profile-lwm2m-device-config-server <tb-profile-lwm2m-device-config-server
[formControl]="serverConfig" [formControl]="serverConfig"
(removeServer)="removeServerConfig($event, $index)" (removeServer)="removeServerConfig($event, $index)"
@ -25,7 +25,7 @@
</tb-profile-lwm2m-device-config-server> </tb-profile-lwm2m-device-config-server>
</div> </div>
</mat-accordion> </mat-accordion>
<div *ngIf="!serverConfigsFromArray().controls.length" style="margin:32px 0"> <div *ngIf="!serverConfigsFromArray.controls.length" style="margin:32px 0">
<span translate fxLayoutAlign="center center" <span translate fxLayoutAlign="center center"
class="tb-prompt">device-profile.lwm2m.no-config-servers</span> class="tb-prompt">device-profile.lwm2m.no-config-servers</span>
</div> </div>

66
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-bootstrap-config-servers.component.ts

@ -14,23 +14,24 @@
/// limitations under the License. /// 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 { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
FormArray, FormArray,
FormBuilder, FormControl, FormBuilder,
FormControl,
FormGroup, FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR NG_VALUE_ACCESSOR
} from '@angular/forms'; } 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 { ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; 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 { DeviceProfileService } from '@core/http/device-profile.service';
import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; 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; bootstrapConfigServersFormGroup: FormGroup;
@ -72,8 +73,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
} }
} }
private valueChangeSubscription: Subscription = null; private destroy$ = new Subject();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(public translate: TranslateService, constructor(public translate: TranslateService,
@ -94,9 +94,17 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
this.bootstrapConfigServersFormGroup = this.fb.group({ this.bootstrapConfigServersFormGroup = this.fb.group({
serverConfigs: this.fb.array([]) 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; return this.bootstrapConfigServersFormGroup.get('serverConfigs') as FormArray;
} }
@ -110,24 +118,22 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
} }
writeValue(serverConfigs: Array<ServerSecurityConfig> | null): void { writeValue(serverConfigs: Array<ServerSecurityConfig> | null): void {
if (this.valueChangeSubscription) { if (serverConfigs?.length === this.serverConfigsFromArray.length) {
this.valueChangeSubscription.unsubscribe(); this.serverConfigsFromArray.patchValue(serverConfigs, {emitEvent: false});
}
const serverConfigsControls: Array<AbstractControl> = [];
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});
} else { } else {
this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); const serverConfigsControls: Array<AbstractControl> = [];
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 { trackByParams(index: number): number {
@ -147,7 +153,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
true true
).subscribe((result) => { ).subscribe((result) => {
if (result) { if (result) {
this.serverConfigsFromArray().removeAt(index); this.serverConfigsFromArray.removeAt(index);
} }
}); });
} }
@ -169,7 +175,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
addServerConfigObs.subscribe((serverConfig) => { addServerConfigObs.subscribe((serverConfig) => {
if (serverConfig) { if (serverConfig) {
serverConfig.securityMode = Lwm2mSecurityType.NO_SEC; serverConfig.securityMode = Lwm2mSecurityType.NO_SEC;
this.serverConfigsFromArray().push(this.fb.control(serverConfig)); this.serverConfigsFromArray.push(this.fb.control(serverConfig));
this.updateModel(); this.updateModel();
} else { } else {
this.isTransportWasRunWithBootstrap = false; this.isTransportWasRunWithBootstrap = false;
@ -196,7 +202,7 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
} }
private isBootstrapAdded(): boolean { private isBootstrapAdded(): boolean {
const serverConfigsArray = this.serverConfigsFromArray().getRawValue(); const serverConfigsArray = this.serverConfigsFromArray.getRawValue();
for (let i = 0; i < serverConfigsArray.length; i++) { for (let i = 0; i < serverConfigsArray.length; i++) {
if (serverConfigsArray[i].bootstrapServerIs) { if (serverConfigsArray[i].bootstrapServerIs) {
return true; return true;
@ -207,15 +213,15 @@ export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValu
private removeBootstrapServerConfig(): void { private removeBootstrapServerConfig(): void {
if (this.bootstrapConfigServersFormGroup) { 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) { if (bootstrapServerIndex !== -1) {
this.serverConfigsFromArray().removeAt(bootstrapServerIndex); this.serverConfigsFromArray.removeAt(bootstrapServerIndex);
} }
} }
} }
private updateModel() { private updateModel() {
const serverConfigs: Array<ServerSecurityConfig> = this.serverConfigsFromArray().value; const serverConfigs: Array<ServerSecurityConfig> = this.serverConfigsFromArray.value;
this.propagateChange(serverConfigs); this.propagateChange(serverConfigs);
} }
} }

10
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({ this.instancesFormGroup = this.fb.group({
instances: this.fb.array([]) instances: this.fb.array([])
}); });
this.valueChange$ = this.instancesFormGroup.valueChanges.subscribe(value => this.updateModel(value.instances));
} }
ngOnDestroy() { ngOnDestroy() {
@ -122,22 +124,16 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue
if (instances.length === this.instancesFormArray.length) { if (instances.length === this.instancesFormArray.length) {
this.instancesFormArray.patchValue(instances, {emitEvent: false}); this.instancesFormArray.patchValue(instances, {emitEvent: false});
} else { } else {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
const instancesControl: Array<AbstractControl> = []; const instancesControl: Array<AbstractControl> = [];
if (instances) { if (instances) {
instances.forEach((instance) => { instances.forEach((instance) => {
instancesControl.push(this.createInstanceFormGroup(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) { if (this.disabled) {
this.instancesFormGroup.disable({emitEvent: false}); this.instancesFormGroup.disable({emitEvent: false});
} }
this.valueChange$ = this.instancesFormGroup.valueChanges.subscribe(value => {
this.updateModel(value.instances);
});
} }
} }

20
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'; } from '@angular/forms';
import { ResourceLwM2M } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { ResourceLwM2M } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { combineLatest, Subject, Subscription } from 'rxjs'; import { combineLatest, Subject } from 'rxjs';
import { startWith, takeUntil } from 'rxjs/operators'; import { startWith, takeUntil } from 'rxjs/operators';
@Component({ @Component({
@ -71,19 +71,19 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue
} }
private destroy$ = new Subject(); private destroy$ = new Subject();
private valueChange$: Subscription = null;
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private fb: FormBuilder) { constructor(private fb: FormBuilder) {
this.resourcesFormGroup = this.fb.group({ this.resourcesFormGroup = this.fb.group({
resources: this.fb.array([]) resources: this.fb.array([])
}); });
this.resourcesFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel(this.resourcesFormGroup.getRawValue().resources));
} }
ngOnDestroy() { ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
@ -131,24 +131,18 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue
private updatedResources(resources: ResourceLwM2M[]): void { private updatedResources(resources: ResourceLwM2M[]): void {
if (resources.length === this.resourcesFormArray.length) { if (resources.length === this.resourcesFormArray.length) {
this.resourcesFormArray.patchValue(resources, {onlySelf: true}); this.resourcesFormArray.patchValue(resources, {onlySelf: true, emitEvent: false});
} else { } else {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
const resourcesControl: Array<AbstractControl> = []; const resourcesControl: Array<AbstractControl> = [];
if (resources) { if (resources) {
resources.forEach((resource) => { resources.forEach((resource) => {
resourcesControl.push(this.createdResourceFormGroup(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) { if (this.disabled) {
this.resourcesFormGroup.disable({emitEvent: false}); this.resourcesFormGroup.disable({emitEvent: false});
} }
this.valueChange$ = this.resourcesFormGroup.valueChanges.subscribe(() => {
this.updateModel(this.resourcesFormGroup.getRawValue().resources);
});
} }
} }

10
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({ this.modelsFormGroup = this.fb.group({
models: this.fb.array([]) models: this.fb.array([])
}); });
this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => this.updateModel(value.models));
} }
ngOnDestroy() { ngOnDestroy() {
@ -130,20 +132,14 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor,
if (models.length === this.modelsFormArray.length) { if (models.length === this.modelsFormArray.length) {
this.modelsFormArray.patchValue(models, {emitEvent: false}); this.modelsFormArray.patchValue(models, {emitEvent: false});
} else { } else {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
const modelControls: Array<AbstractControl> = []; const modelControls: Array<AbstractControl> = [];
models.forEach(model => { models.forEach(model => {
modelControls.push(this.createModelFormGroup(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) { if (this.disabled) {
this.modelsFormGroup.disable({emitEvent: false}); this.modelsFormGroup.disable({emitEvent: false});
} }
this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => {
this.updateModel(value.models);
});
} }
} }

4
ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html

@ -16,7 +16,7 @@
--> -->
<div fxLayout="column"> <div fxLayout="column">
<div *ngFor="let deviceProfileCommunication of communicationConfigFormArray().controls; let $index = index; <div *ngFor="let deviceProfileCommunication of communicationConfigFormArray.controls; let $index = index;
last as isLast;" fxLayout="row" fxLayoutAlign="start center" last as isLast;" fxLayout="row" fxLayoutAlign="start center"
fxLayoutGap="8px" class="scope-row" [formGroup]="deviceProfileCommunication"> fxLayoutGap="8px" class="scope-row" [formGroup]="deviceProfileCommunication">
<div class="communication-config" fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> <div class="communication-config" fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start">
@ -58,7 +58,7 @@
<mat-icon>remove_circle_outline</mat-icon> <mat-icon>remove_circle_outline</mat-icon>
</button> </button>
</div> </div>
<div *ngIf="!communicationConfigFormArray().controls.length && !disabled"> <div *ngIf="!communicationConfigFormArray.controls.length && !disabled">
<span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-communication-config</span> <span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-communication-config</span>
</div> </div>
<div *ngIf="!disabled && isAddEnabled"> <div *ngIf="!disabled && isAddEnabled">

55
ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.ts

@ -27,7 +27,7 @@ import {
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { SnmpCommunicationConfig, SnmpSpecType, SnmpSpecTypeTranslationMap } from '@shared/models/device.models'; import { SnmpCommunicationConfig, SnmpSpecType, SnmpSpecTypeTranslationMap } from '@shared/models/device.models';
import { Subject, Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { isUndefinedOrNull } from '@core/utils'; import { isUndefinedOrNull } from '@core/utils';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
@ -58,7 +58,6 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On
disabled: boolean; disabled: boolean;
private usedSpecType: SnmpSpecType[] = []; private usedSpecType: SnmpSpecType[] = [];
private valueChange$: Subscription = null;
private destroy$ = new Subject(); private destroy$ = new Subject();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
@ -68,17 +67,17 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On
this.deviceProfileCommunicationConfig = this.fb.group({ this.deviceProfileCommunicationConfig = this.fb.group({
communicationConfig: this.fb.array([]) communicationConfig: this.fb.array([])
}); });
this.deviceProfileCommunicationConfig.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel());
} }
ngOnDestroy() { ngOnDestroy() {
if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
communicationConfigFormArray(): FormArray { get communicationConfigFormArray(): FormArray {
return this.deviceProfileCommunicationConfig.get('communicationConfig') as FormArray; return this.deviceProfileCommunicationConfig.get('communicationConfig') as FormArray;
} }
@ -99,27 +98,27 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On
} }
writeValue(communicationConfig: SnmpCommunicationConfig[]) { writeValue(communicationConfig: SnmpCommunicationConfig[]) {
if (this.valueChange$) { if (communicationConfig?.length === this.communicationConfigFormArray.length) {
this.valueChange$.unsubscribe(); this.communicationConfigFormArray.patchValue(communicationConfig, {emitEvent: false});
}
const communicationConfigControl: Array<AbstractControl> = [];
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});
} else { } else {
this.deviceProfileCommunicationConfig.enable({emitEvent: false}); const communicationConfigControl: Array<AbstractControl> = [];
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(); this.updateUsedSpecType();
if (!this.disabled && !this.deviceProfileCommunicationConfig.valid) { if (!this.disabled && !this.deviceProfileCommunicationConfig.valid) {
this.updateModel(); this.updateModel();
@ -133,16 +132,16 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On
} }
public removeCommunicationConfig(index: number) { public removeCommunicationConfig(index: number) {
this.communicationConfigFormArray().removeAt(index); this.communicationConfigFormArray.removeAt(index);
} }
get isAddEnabled(): boolean { get isAddEnabled(): boolean {
return this.communicationConfigFormArray().length !== Object.keys(SnmpSpecType).length; return this.communicationConfigFormArray.length !== Object.keys(SnmpSpecType).length;
} }
public addCommunicationConfig() { public addCommunicationConfig() {
this.communicationConfigFormArray().push(this.createdFormGroup()); this.communicationConfigFormArray.push(this.createdFormGroup());
this.deviceProfileCommunicationConfig.updateValueAndValidity(); this.deviceProfileCommunicationConfig.updateValueAndValidity();
if (!this.deviceProfileCommunicationConfig.valid) { if (!this.deviceProfileCommunicationConfig.valid) {
this.updateModel(); this.updateModel();

4
ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html

@ -25,7 +25,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngFor="let mappingConfig of mappingsConfigFormArray().controls; let $index = index; <div *ngFor="let mappingConfig of mappingsConfigFormArray.controls; let $index = index;
last as isLast;" fxLayout="row" fxLayoutAlign="start center" last as isLast;" fxLayout="row" fxLayoutAlign="start center"
fxLayoutGap="8px" [formGroup]="mappingConfig" class="mapping-list"> fxLayoutGap="8px" [formGroup]="mappingConfig" class="mapping-list">
<div fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> <div fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start">
@ -67,7 +67,7 @@
</button> </button>
</div> </div>
</div> </div>
<div *ngIf="!mappingsConfigFormArray().controls.length && !disabled"> <div *ngIf="!mappingsConfigFormArray.controls.length && !disabled">
<span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-mapping-config</span> <span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-mapping-config</span>
</div> </div>
<div *ngIf="!disabled"> <div *ngIf="!disabled">

43
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({ this.mappingsConfigForm = this.fb.group({
mappings: this.fb.array([]) mappings: this.fb.array([])
}); });
this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => this.updateModel());
} }
ngOnDestroy() { ngOnDestroy() {
@ -100,38 +101,36 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con
} }
writeValue(mappings: SnmpMapping[]) { writeValue(mappings: SnmpMapping[]) {
if (this.valueChange$) { if (mappings?.length === this.mappingsConfigFormArray.length) {
this.valueChange$.unsubscribe(); this.mappingsConfigFormArray.patchValue(mappings, {emitEvent: false});
}
const mappingsControl: Array<AbstractControl> = [];
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});
} else { } else {
this.mappingsConfigForm.enable({emitEvent: false}); const mappingsControl: Array<AbstractControl> = [];
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) { if (!this.disabled && !this.mappingsConfigForm.valid) {
this.updateModel(); this.updateModel();
} }
} }
mappingsConfigFormArray(): FormArray { get mappingsConfigFormArray(): FormArray {
return this.mappingsConfigForm.get('mappings') as FormArray; return this.mappingsConfigForm.get('mappings') as FormArray;
} }
public addMappingConfig() { public addMappingConfig() {
this.mappingsConfigFormArray().push(this.createdFormGroup()); this.mappingsConfigFormArray.push(this.createdFormGroup());
this.mappingsConfigForm.updateValueAndValidity(); this.mappingsConfigForm.updateValueAndValidity();
if (!this.mappingsConfigForm.valid) { if (!this.mappingsConfigForm.valid) {
this.updateModel(); this.updateModel();
@ -139,7 +138,7 @@ export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, Con
} }
public removeMappingConfig(index: number) { public removeMappingConfig(index: number) {
this.mappingsConfigFormArray().removeAt(index); this.mappingsConfigFormArray.removeAt(index);
} }
private createdFormGroup(value?: SnmpMapping): FormGroup { private createdFormGroup(value?: SnmpMapping): FormGroup {

63
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 { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subscription } from 'rxjs'; import { Subject } from 'rxjs';
import { QueueInfo } from '@shared/models/queue.models'; import { QueueInfo } from '@shared/models/queue.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { guid } from '@core/utils'; import { guid } from '@core/utils';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-tenant-profile-queues', selector: 'tb-tenant-profile-queues',
@ -70,8 +71,7 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
@Input() @Input()
disabled: boolean; disabled: boolean;
private valueChangeSubscription$: Subscription = null; private destroy$ = new Subject();
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
@ -83,12 +83,6 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
this.propagateChange = fn; this.propagateChange = fn;
} }
ngOnDestroy() {
if (this.valueChangeSubscription$) {
this.valueChangeSubscription$.unsubscribe();
}
}
registerOnTouched(fn: any): void { registerOnTouched(fn: any): void {
} }
@ -96,6 +90,15 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
this.tenantProfileQueuesFormGroup = this.fb.group({ this.tenantProfileQueuesFormGroup = this.fb.group({
queues: this.fb.array([]) queues: this.fb.array([])
}); });
this.tenantProfileQueuesFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
} }
get queuesFormArray(): FormArray { get queuesFormArray(): FormArray {
@ -112,30 +115,28 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid
} }
writeValue(queues: Array<QueueInfo> | null): void { writeValue(queues: Array<QueueInfo> | null): void {
if (this.valueChangeSubscription$) { if (queues.length === this.queuesFormArray.length) {
this.valueChangeSubscription$.unsubscribe(); this.queuesFormArray.patchValue(queues, {emitEvent: false});
}
const queuesControls: Array<AbstractControl> = [];
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});
} else { } else {
this.tenantProfileQueuesFormGroup.enable({emitEvent: false}); const queuesControls: Array<AbstractControl> = [];
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) { public trackByQueue(index: number, queueControl: AbstractControl) {

8
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html

@ -17,17 +17,17 @@
--> -->
<div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup"> <div class="tb-relation-filters" [formGroup]="relationFiltersFormGroup">
<div class="container"> <div class="container">
<div class="header" [fxShow]="relationFiltersFormArray().length"> <div class="header" [fxShow]="relationFiltersFormArray.length">
<div fxLayout="row" fxLayoutAlign="start center"> <div fxLayout="row" fxLayoutAlign="start center">
<span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span> <span class="cell" style="width: 200px; min-width: 200px;" translate>relation.type</span>
<span class="cell" fxFlex translate>entity.entity-types</span> <span class="cell" fxFlex translate>entity.entity-types</span>
<span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span> <span class="cell" style="width: 40px; min-width: 40px;">&nbsp;</span>
</div> </div>
</div> </div>
<div class="body" [fxShow]="relationFiltersFormArray().length"> <div class="body" [fxShow]="relationFiltersFormArray.length">
<div class="row" fxFlex fxLayout="row" <div class="row" fxFlex fxLayout="row"
fxLayoutAlign="start center" formArrayName="relationFilters" fxLayoutAlign="start center" formArrayName="relationFilters"
*ngFor="let relationFilterControl of relationFiltersFormArray().controls; let $index = index"> *ngFor="let relationFilterControl of relationFiltersFormArray.controls; let $index = index">
<div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center"> <div class="mat-elevation-z1" fxFlex fxLayout="row" fxLayoutAlign="start center">
<tb-relation-type-autocomplete <tb-relation-type-autocomplete
class="cell" style="width: 200px; min-width: 200px;" class="cell" style="width: 200px; min-width: 200px;"
@ -50,7 +50,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="any-filter" [fxShow]="!relationFiltersFormArray().length"> <div class="any-filter" [fxShow]="!relationFiltersFormArray.length">
<span fxLayoutAlign="center center" <span fxLayoutAlign="center center"
class="tb-prompt" translate>relation.any-relation</span> class="tb-prompt" translate>relation.any-relation</span>
</div> </div>

54
ui-ngx/src/app/modules/home/components/relation/relation-filters.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -28,7 +28,8 @@ import { RelationEntityTypeFilter } from '@shared/models/relation.models';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-relation-filters', 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; @Input() disabled: boolean;
@ -50,22 +51,32 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
relationFiltersFormGroup: FormGroup; relationFiltersFormGroup: FormGroup;
private destroy$ = new Subject();
private propagateChange = null; private propagateChange = null;
private valueChangeSubscription: Subscription = null;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
} }
ngOnInit(): void { ngOnInit(): void {
this.relationFiltersFormGroup = this.fb.group({}); this.relationFiltersFormGroup = this.fb.group({
this.relationFiltersFormGroup.addControl('relationFilters', relationFilters: this.fb.array([])
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; return this.relationFiltersFormGroup.get('relationFilters') as FormArray;
} }
@ -81,19 +92,17 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
} }
writeValue(filters: Array<RelationEntityTypeFilter>): void { writeValue(filters: Array<RelationEntityTypeFilter>): void {
if (this.valueChangeSubscription) { if (filters?.length === this.relationFiltersFormArray.length) {
this.valueChangeSubscription.unsubscribe(); this.relationFiltersFormArray.patchValue(filters, {emitEvent: false});
} } else {
const relationFiltersControls: Array<AbstractControl> = []; const relationFiltersControls: Array<AbstractControl> = [];
if (filters && filters.length) { if (filters && filters.length) {
filters.forEach((filter) => { filters.forEach((filter) => {
relationFiltersControls.push(this.createRelationFilterFormGroup(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) { public removeFilter(index: number) {
@ -101,12 +110,11 @@ export class RelationFiltersComponent extends PageComponent implements ControlVa
} }
public addFilter() { public addFilter() {
const relationFiltersFormArray = this.relationFiltersFormGroup.get('relationFilters') as FormArray;
const filter: RelationEntityTypeFilter = { const filter: RelationEntityTypeFilter = {
relationType: null, relationType: null,
entityTypes: [] entityTypes: []
}; };
relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter)); this.relationFiltersFormArray.push(this.createRelationFilterFormGroup(filter));
} }
private createRelationFilterFormGroup(filter: RelationEntityTypeFilter): AbstractControl { private createRelationFilterFormGroup(filter: RelationEntityTypeFilter): AbstractControl {

29
ui-ngx/src/app/shared/components/kv-map.component.ts

@ -30,7 +30,8 @@ import {
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-key-val-map', selector: 'tb-key-val-map',
@ -63,19 +64,27 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc
kvListFormGroup: FormGroup; kvListFormGroup: FormGroup;
private destroy$ = new Subject();
private propagateChange = null; private propagateChange = null;
private valueChangeSubscription: Subscription = null;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
} }
ngOnInit(): void { ngOnInit(): void {
this.kvListFormGroup = this.fb.group({}); this.kvListFormGroup = this.fb.group({
this.kvListFormGroup.addControl('keyVals', keyVals: this.fb.array([])
this.fb.array([])); });
this.kvListFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.updateModel());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
} }
keyValsFormArray(): FormArray { keyValsFormArray(): FormArray {
@ -99,9 +108,6 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc
} }
writeValue(keyValMap: {[key: string]: string}): void { writeValue(keyValMap: {[key: string]: string}): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
}
const keyValsControls: Array<AbstractControl> = []; const keyValsControls: Array<AbstractControl> = [];
if (keyValMap) { if (keyValMap) {
for (const property of Object.keys(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.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls), {emitEvent: false});
this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
if (this.disabled) { if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false}); this.kvListFormGroup.disable({emitEvent: false});
} else { } else {

Loading…
Cancel
Save