Commit 6cd7e3df4bb9a20a57bf351e702aaf131ec0ed91

Authored by Igor Kulikov
Committed by GitHub
2 parents 167e833d ad68aa6d

Merge pull request #4651 from vvlladd28/improvement/device-profile/lwm2m

UI: Refactoring device profile transport
Showing 16 changed files with 307 additions and 285 deletions
1 -/**  
2 - * Copyright © 2016-2021 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -:host ::ng-deep {  
17 - textarea.mat-input-element.cdk-textarea-autosize {  
18 - box-sizing: content-box;  
19 - }  
20 -}  
@@ -41,7 +41,7 @@ import { Subject } from 'rxjs'; @@ -41,7 +41,7 @@ import { Subject } from 'rxjs';
41 @Component({ 41 @Component({
42 selector: 'tb-security-config-lwm2m-server', 42 selector: 'tb-security-config-lwm2m-server',
43 templateUrl: './security-config-lwm2m-server.component.html', 43 templateUrl: './security-config-lwm2m-server.component.html',
44 - styleUrls: ['./security-config-lwm2m-server.component.scss'], 44 + styleUrls: [],
45 providers: [ 45 providers: [
46 { 46 {
47 provide: NG_VALUE_ACCESSOR, 47 provide: NG_VALUE_ACCESSOR,
@@ -27,8 +27,4 @@ @@ -27,8 +27,4 @@
27 .mat-tab-body { 27 .mat-tab-body {
28 padding: 16px 0; 28 padding: 16px 0;
29 } 29 }
30 -  
31 - textarea.mat-input-element.cdk-textarea-autosize {  
32 - box-sizing: content-box;  
33 - }  
34 } 30 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, forwardRef, Input, OnInit } from '@angular/core'; 17 +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 import { Store } from '@ngrx/store'; 19 import { Store } from '@ngrx/store';
20 import { AppState } from '@app/core/core.state'; 20 import { AppState } from '@app/core/core.state';
@@ -33,6 +33,8 @@ import { @@ -33,6 +33,8 @@ import {
33 transportPayloadTypeTranslationMap, 33 transportPayloadTypeTranslationMap,
34 } from '@shared/models/device.models'; 34 } from '@shared/models/device.models';
35 import { isDefinedAndNotNull } from '@core/utils'; 35 import { isDefinedAndNotNull } from '@core/utils';
  36 +import { Subject } from 'rxjs';
  37 +import { takeUntil } from 'rxjs/operators';
36 38
37 @Component({ 39 @Component({
38 selector: 'tb-coap-device-profile-transport-configuration', 40 selector: 'tb-coap-device-profile-transport-configuration',
@@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils'; @@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils';
44 multi: true 46 multi: true
45 }] 47 }]
46 }) 48 })
47 -export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { 49 +export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
48 50
49 coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); 51 coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType);
50 52
@@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control @@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
56 58
57 coapDeviceProfileTransportConfigurationFormGroup: FormGroup; 59 coapDeviceProfileTransportConfigurationFormGroup: FormGroup;
58 60
  61 + private destroy$ = new Subject();
59 private requiredValue: boolean; 62 private requiredValue: boolean;
60 63
61 private transportPayloadTypeConfiguration = this.fb.group({ 64 private transportPayloadTypeConfiguration = this.fb.group({
@@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control @@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
99 }) 102 })
100 } 103 }
101 ); 104 );
102 - this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType')  
103 - .valueChanges.subscribe(coapDeviceType => { 105 + this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').valueChanges.pipe(
  106 + takeUntil(this.destroy$)
  107 + ).subscribe(coapDeviceType => {
104 this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); 108 this.updateCoapDeviceTypeBasedControls(coapDeviceType, true);
105 }); 109 });
106 - this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { 110 + this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
  111 + takeUntil(this.destroy$)
  112 + ).subscribe(() => {
107 this.updateModel(); 113 this.updateModel();
108 }); 114 });
109 } 115 }
110 116
  117 + ngOnDestroy() {
  118 + this.destroy$.next();
  119 + this.destroy$.complete();
  120 + }
  121 +
111 get coapDeviceTypeDefault(): boolean { 122 get coapDeviceTypeDefault(): boolean {
112 const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; 123 const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value;
113 return coapDeviceType === CoapTransportDeviceType.DEFAULT; 124 return coapDeviceType === CoapTransportDeviceType.DEFAULT;
@@ -14,13 +14,15 @@ @@ -14,13 +14,15 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, forwardRef, Input, OnInit } from '@angular/core'; 17 +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
19 import { Store } from '@ngrx/store'; 19 import { Store } from '@ngrx/store';
20 import { AppState } from '@app/core/core.state'; 20 import { AppState } from '@app/core/core.state';
21 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 21 import { coerceBooleanProperty } from '@angular/cdk/coercion';
22 import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; 22 import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models';
23 import { deepClone } from '@core/utils'; 23 import { deepClone } from '@core/utils';
  24 +import { Subject } from 'rxjs';
  25 +import { takeUntil } from 'rxjs/operators';
24 26
25 @Component({ 27 @Component({
26 selector: 'tb-device-profile-configuration', 28 selector: 'tb-device-profile-configuration',
@@ -32,12 +34,14 @@ import { deepClone } from '@core/utils'; @@ -32,12 +34,14 @@ import { deepClone } from '@core/utils';
32 multi: true 34 multi: true
33 }] 35 }]
34 }) 36 })
35 -export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { 37 +export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
36 38
37 deviceProfileType = DeviceProfileType; 39 deviceProfileType = DeviceProfileType;
38 40
39 deviceProfileConfigurationFormGroup: FormGroup; 41 deviceProfileConfigurationFormGroup: FormGroup;
40 42
  43 + private destroy$ = new Subject();
  44 +
41 private requiredValue: boolean; 45 private requiredValue: boolean;
42 get required(): boolean { 46 get required(): boolean {
43 return this.requiredValue; 47 return this.requiredValue;
@@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor @@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor
69 this.deviceProfileConfigurationFormGroup = this.fb.group({ 73 this.deviceProfileConfigurationFormGroup = this.fb.group({
70 configuration: [null, Validators.required] 74 configuration: [null, Validators.required]
71 }); 75 });
72 - this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { 76 + this.deviceProfileConfigurationFormGroup.valueChanges.pipe(
  77 + takeUntil(this.destroy$)
  78 + ).subscribe(() => {
73 this.updateModel(); 79 this.updateModel();
74 }); 80 });
75 } 81 }
76 82
  83 + ngOnDestroy() {
  84 + this.destroy$.next();
  85 + this.destroy$.complete();
  86 + }
  87 +
77 setDisabledState(isDisabled: boolean): void { 88 setDisabledState(isDisabled: boolean): void {
78 this.disabled = isDisabled; 89 this.disabled = isDisabled;
79 if (this.disabled) { 90 if (this.disabled) {
@@ -15,112 +15,107 @@ @@ -15,112 +15,107 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<section [formGroup]="serverFormGroup" style="min-width: 400px;">  
19 - <div class="mat-padding">  
20 - <div fxLayout="column">  
21 - <div fxLayout="row" fxLayoutGap="8px">  
22 - <mat-form-field class="mat-block">  
23 - <mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>  
24 - <mat-select formControlName="securityMode">  
25 - <mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"  
26 - [value]="securityMode">  
27 - {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}  
28 - </mat-option>  
29 - </mat-select>  
30 - </mat-form-field>  
31 - <mat-form-field class="mat-block">  
32 - <mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>  
33 - <input matInput type="text" formControlName="host" required  
34 - matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}"  
35 - matTooltipPosition="above">  
36 - <mat-error *ngIf="serverFormGroup.get('host').hasError('required')">  
37 - {{ 'device-profile.lwm2m.server-host' | translate }}  
38 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
39 - </mat-error>  
40 - </mat-form-field>  
41 - <mat-form-field class="mat-block">  
42 - <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>  
43 - <input matInput type="number" formControlName="port" required  
44 - matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}"  
45 - matTooltipPosition="above">  
46 - <mat-error *ngIf="serverFormGroup.get('port').hasError('required')">  
47 - {{ 'device-profile.lwm2m.server-port' | translate }}  
48 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
49 - </mat-error>  
50 - </mat-form-field>  
51 - <mat-form-field class="mat-block">  
52 - <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>  
53 - <input matInput type="number" formControlName="serverId" required  
54 - matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}"  
55 - matTooltipPosition="above">  
56 - <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">  
57 - {{ 'device-profile.lwm2m.short-id' | translate }}  
58 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
59 - </mat-error>  
60 - </mat-form-field>  
61 - </div>  
62 - </div>  
63 - <div fxLayout="column">  
64 - <div fxLayout="row" fxLayoutGap="10px">  
65 - <mat-form-field class="mat-block">  
66 - <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>  
67 - <input matInput type="number" formControlName="clientHoldOffTime" required  
68 - matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}"  
69 - matTooltipPosition="above">  
70 - <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">  
71 - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }}  
72 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
73 - </mat-error>  
74 - </mat-form-field>  
75 - <mat-form-field class="mat-block">  
76 - <mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label>  
77 - <input matInput type="number" formControlName="bootstrapServerAccountTimeout" required  
78 - matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}"  
79 - matTooltipPosition="above">  
80 - <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">  
81 - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}  
82 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
83 - </mat-error>  
84 - </mat-form-field>  
85 - <mat-checkbox formControlName="bootstrapServerIs" color="primary">  
86 - {{ 'device-profile.lwm2m.bootstrap-server' | translate }}  
87 - </mat-checkbox>  
88 - </div>  
89 - <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||  
90 - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">  
91 - <mat-form-field class="mat-block">  
92 - <mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>  
93 - <textarea matInput  
94 - #serverPublicKey  
95 - maxlength="{{lenMaxServerPublicKey}}"  
96 - cdkTextareaAutosize  
97 - cdkAutosizeMinRows="1"  
98 - cols="1" required  
99 - style="overflow:hidden"  
100 - formControlName="serverPublicKey"  
101 - matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}"  
102 - ></textarea>  
103 - <mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint>  
104 - <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">  
105 - {{ 'device-profile.lwm2m.server-public-key' | translate }}  
106 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
107 - </mat-error>  
108 - <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') &&  
109 - (serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||  
110 - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)">  
111 - {{ 'device-profile.lwm2m.server-public-key' | translate }}  
112 - <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {  
113 - count: 0} }}</strong>  
114 - </mat-error>  
115 - <mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') ||  
116 - serverFormGroup.get('serverPublicKey').hasError('minlength')) &&  
117 - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK">  
118 - {{ 'device-profile.lwm2m.server-public-key' | translate }}  
119 - <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {  
120 - count: lenMaxServerPublicKey } }}</strong>  
121 - </mat-error>  
122 - </mat-form-field>  
123 - </div>  
124 - </div> 18 +<section [formGroup]="serverFormGroup">
  19 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
  20 + <mat-form-field fxFlex>
  21 + <mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>
  22 + <mat-select formControlName="securityMode">
  23 + <mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
  24 + [value]="securityMode">
  25 + {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
  26 + </mat-option>
  27 + </mat-select>
  28 + </mat-form-field>
  29 + <mat-form-field fxFlex>
  30 + <mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>
  31 + <input matInput type="text" formControlName="host" required
  32 + matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}"
  33 + matTooltipPosition="above">
  34 + <mat-error *ngIf="serverFormGroup.get('host').hasError('required')">
  35 + {{ 'device-profile.lwm2m.server-host' | translate }}
  36 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  37 + </mat-error>
  38 + </mat-form-field>
  39 + <mat-form-field fxFlex>
  40 + <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
  41 + <input matInput type="number" formControlName="port" required
  42 + matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}"
  43 + matTooltipPosition="above">
  44 + <mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
  45 + {{ 'device-profile.lwm2m.server-port' | translate }}
  46 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  47 + </mat-error>
  48 + </mat-form-field>
  49 + <mat-form-field fxFlex>
  50 + <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
  51 + <input matInput type="number" formControlName="serverId" required
  52 + matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}"
  53 + matTooltipPosition="above">
  54 + <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">
  55 + {{ 'device-profile.lwm2m.short-id' | translate }}
  56 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  57 + </mat-error>
  58 + </mat-form-field>
  59 + </div>
  60 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
  61 + <mat-form-field fxFlex>
  62 + <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
  63 + <input matInput type="number" formControlName="clientHoldOffTime" required
  64 + matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}"
  65 + matTooltipPosition="above">
  66 + <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
  67 + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }}
  68 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  69 + </mat-error>
  70 + </mat-form-field>
  71 + <mat-form-field fxFlex>
  72 + <mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label>
  73 + <input matInput type="number" formControlName="bootstrapServerAccountTimeout" required
  74 + matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}"
  75 + matTooltipPosition="above">
  76 + <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
  77 + {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}
  78 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  79 + </mat-error>
  80 + </mat-form-field>
  81 + <mat-checkbox fxFlex formControlName="bootstrapServerIs" color="primary">
  82 + {{ 'device-profile.lwm2m.bootstrap-server' | translate }}
  83 + </mat-checkbox>
  84 + <div fxFlex></div>
  85 + </div>
  86 + <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
  87 + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">
  88 + <mat-form-field class="mat-block">
  89 + <mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
  90 + <textarea matInput
  91 + #serverPublicKey
  92 + maxlength="{{lenMaxServerPublicKey}}"
  93 + cdkTextareaAutosize
  94 + cdkAutosizeMinRows="1"
  95 + cols="1" required
  96 + style="overflow:hidden"
  97 + formControlName="serverPublicKey"
  98 + matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}"
  99 + ></textarea>
  100 + <mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint>
  101 + <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
  102 + {{ 'device-profile.lwm2m.server-public-key' | translate }}
  103 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  104 + </mat-error>
  105 + <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') &&
  106 + (serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
  107 + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)">
  108 + {{ 'device-profile.lwm2m.server-public-key' | translate }}
  109 + <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
  110 + count: 0} }}</strong>
  111 + </mat-error>
  112 + <mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') ||
  113 + serverFormGroup.get('serverPublicKey').hasError('minlength')) &&
  114 + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK">
  115 + {{ 'device-profile.lwm2m.server-public-key' | translate }}
  116 + <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
  117 + count: lenMaxServerPublicKey } }}</strong>
  118 + </mat-error>
  119 + </mat-form-field>
125 </div> 120 </div>
126 </section> 121 </section>
@@ -15,12 +15,12 @@ @@ -15,12 +15,12 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<section style="padding-bottom: 16px; margin: 0" mat-dialog-content> 18 +<section style="padding-bottom: 16px; margin: 0">
19 <mat-tab-group dynamicHeight> 19 <mat-tab-group dynamicHeight>
20 <mat-tab label="{{ 'device-profile.lwm2m.model-tab' | translate }}"> 20 <mat-tab label="{{ 'device-profile.lwm2m.model-tab' | translate }}">
21 <ng-template matTabContent> 21 <ng-template matTabContent>
22 <section [formGroup]="lwm2mDeviceProfileFormGroup"> 22 <section [formGroup]="lwm2mDeviceProfileFormGroup">
23 - <div *ngIf="false" class="mat-padding" style="padding-bottom: 0px"> 23 + <div *ngIf="false" class="mat-padding" style="padding-bottom: 0">
24 <mat-form-field class="mat-block"> 24 <mat-form-field class="mat-block">
25 <mat-label>{{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }}</mat-label> 25 <mat-label>{{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }}</mat-label>
26 <mat-select formControlName="clientOnlyObserveAfterConnect" 26 <mat-select formControlName="clientOnlyObserveAfterConnect"
@@ -34,20 +34,16 @@ @@ -34,20 +34,16 @@
34 </mat-select> 34 </mat-select>
35 </mat-form-field> 35 </mat-form-field>
36 </div> 36 </div>
37 - <div class="mat-padding" style="padding-top: 0">  
38 - <tb-profile-lwm2m-object-list  
39 - (addList)="addObjectsList($event)"  
40 - (removeList)="removeObjectsList($event)"  
41 - [required]="required"  
42 - formControlName="objectIds">  
43 - </tb-profile-lwm2m-object-list>  
44 - </div>  
45 - <div class="mat-padding">  
46 - <tb-profile-lwm2m-observe-attr-telemetry  
47 - [required]="required"  
48 - formControlName="observeAttrTelemetry">  
49 - </tb-profile-lwm2m-observe-attr-telemetry>  
50 - </div> 37 + <tb-profile-lwm2m-object-list
  38 + (addList)="addObjectsList($event)"
  39 + (removeList)="removeObjectsList($event)"
  40 + [required]="required"
  41 + formControlName="objectIds">
  42 + </tb-profile-lwm2m-object-list>
  43 + <tb-profile-lwm2m-observe-attr-telemetry
  44 + [required]="required"
  45 + formControlName="observeAttrTelemetry">
  46 + </tb-profile-lwm2m-observe-attr-telemetry>
51 </section> 47 </section>
52 </ng-template> 48 </ng-template>
53 </mat-tab> 49 </mat-tab>
@@ -58,94 +54,73 @@ @@ -58,94 +54,73 @@
58 <mat-accordion multi="true" class="mat-body-1"> 54 <mat-accordion multi="true" class="mat-body-1">
59 <mat-expansion-panel> 55 <mat-expansion-panel>
60 <mat-expansion-panel-header> 56 <mat-expansion-panel-header>
61 - <mat-panel-title>  
62 - <div class="tb-panel-title">{{ 'device-profile.lwm2m.servers' | translate | uppercase }}</div>  
63 - </mat-panel-title> 57 + <mat-panel-title>{{ 'device-profile.lwm2m.servers' | translate }}</mat-panel-title>
64 </mat-expansion-panel-header> 58 </mat-expansion-panel-header>
65 <ng-template matExpansionPanelContent> 59 <ng-template matExpansionPanelContent>
66 - <div fxLayout="column">  
67 - <div fxLayout="row" fxLayoutGap="8px">  
68 - <mat-form-field fxFlex>  
69 - <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>  
70 - <input matInput type="number" formControlName="shortId" required>  
71 - <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('shortId').hasError('required')">  
72 - {{ 'device-profile.lwm2m.short-id' | translate }}  
73 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
74 - </mat-error>  
75 - </mat-form-field>  
76 - <mat-form-field fxFlex>  
77 - <mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label>  
78 - <input matInput type="number" formControlName="lifetime" required>  
79 - <mat-error  
80 - *ngIf="lwm2mDeviceProfileFormGroup.get('lifetime').hasError('required')">  
81 - {{ 'device-profile.lwm2m.lifetime' | translate }}  
82 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
83 - </mat-error>  
84 - </mat-form-field>  
85 - <mat-form-field fxFlex>  
86 - <mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label>  
87 - <input matInput type="number" formControlName="defaultMinPeriod" required>  
88 - <mat-error  
89 - *ngIf="lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').hasError('required')">  
90 - {{ 'device-profile.lwm2m.default-min-period' | translate }}  
91 - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>  
92 - </mat-error>  
93 - </mat-form-field>  
94 - </div>  
95 - <div fxLayout="row" fxLayoutGap="8px">  
96 - <mat-form-field class="mat-block" fxFlex="100">  
97 - <mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label>  
98 - <mat-select formControlName="binding">  
99 - <mat-option *ngFor="let bindingMode of bindingModeTypes"  
100 - [value]="bindingMode">  
101 - {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }}  
102 - </mat-option>  
103 - </mat-select>  
104 - </mat-form-field>  
105 - </div>  
106 - <div>  
107 - <mat-checkbox formControlName="notifIfDisabled" color="primary">  
108 - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }}  
109 - </mat-checkbox>  
110 - </div> 60 + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
  61 + <mat-form-field fxFlex>
  62 + <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
  63 + <input matInput type="number" formControlName="shortId" required>
  64 + <mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('shortId').hasError('required')">
  65 + {{ 'device-profile.lwm2m.short-id' | translate }}
  66 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  67 + </mat-error>
  68 + </mat-form-field>
  69 + <mat-form-field fxFlex>
  70 + <mat-label>{{ 'device-profile.lwm2m.lifetime' | translate }}</mat-label>
  71 + <input matInput type="number" formControlName="lifetime" required>
  72 + <mat-error
  73 + *ngIf="lwm2mDeviceProfileFormGroup.get('lifetime').hasError('required')">
  74 + {{ 'device-profile.lwm2m.lifetime' | translate }}
  75 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  76 + </mat-error>
  77 + </mat-form-field>
  78 + <mat-form-field fxFlex>
  79 + <mat-label>{{ 'device-profile.lwm2m.default-min-period' | translate }}</mat-label>
  80 + <input matInput type="number" formControlName="defaultMinPeriod" required>
  81 + <mat-error
  82 + *ngIf="lwm2mDeviceProfileFormGroup.get('defaultMinPeriod').hasError('required')">
  83 + {{ 'device-profile.lwm2m.default-min-period' | translate }}
  84 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  85 + </mat-error>
  86 + </mat-form-field>
111 </div> 87 </div>
  88 + <mat-form-field class="mat-block">
  89 + <mat-label>{{ 'device-profile.lwm2m.binding' | translate }}</mat-label>
  90 + <mat-select formControlName="binding">
  91 + <mat-option *ngFor="let bindingMode of bindingModeTypes"
  92 + [value]="bindingMode">
  93 + {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }}
  94 + </mat-option>
  95 + </mat-select>
  96 + </mat-form-field>
  97 + <mat-checkbox formControlName="notifIfDisabled" color="primary">
  98 + {{ 'device-profile.lwm2m.notif-if-disabled' | translate }}
  99 + </mat-checkbox>
112 </ng-template> 100 </ng-template>
113 </mat-expansion-panel> 101 </mat-expansion-panel>
114 - </mat-accordion>  
115 - <mat-accordion multi="true" class="mat-body-1">  
116 <mat-expansion-panel> 102 <mat-expansion-panel>
117 <mat-expansion-panel-header> 103 <mat-expansion-panel-header>
118 - <mat-panel-title>  
119 - <div  
120 - class="tb-panel-title">{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}</div>  
121 - </mat-panel-title> 104 + <mat-panel-title>{{ 'device-profile.lwm2m.bootstrap-server' | translate }}</mat-panel-title>
122 </mat-expansion-panel-header> 105 </mat-expansion-panel-header>
123 <ng-template matExpansionPanelContent> 106 <ng-template matExpansionPanelContent>
124 - <div class="mat-padding">  
125 - <tb-profile-lwm2m-device-config-server  
126 - [required]="required"  
127 - formControlName="bootstrapServer"  
128 - [bootstrapServerIs]=true>  
129 - </tb-profile-lwm2m-device-config-server>  
130 - </div> 107 + <tb-profile-lwm2m-device-config-server
  108 + [required]="required"
  109 + formControlName="bootstrapServer"
  110 + [bootstrapServerIs]=true>
  111 + </tb-profile-lwm2m-device-config-server>
131 </ng-template> 112 </ng-template>
132 </mat-expansion-panel> 113 </mat-expansion-panel>
133 - </mat-accordion>  
134 - <mat-accordion multi="true" class="mat-body-1">  
135 <mat-expansion-panel> 114 <mat-expansion-panel>
136 <mat-expansion-panel-header> 115 <mat-expansion-panel-header>
137 - <mat-panel-title>  
138 - <div class="tb-panel-title">{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}</div>  
139 - </mat-panel-title> 116 + <mat-panel-title>{{ 'device-profile.lwm2m.lwm2m-server' | translate }}</mat-panel-title>
140 </mat-expansion-panel-header> 117 </mat-expansion-panel-header>
141 <ng-template matExpansionPanelContent> 118 <ng-template matExpansionPanelContent>
142 - <div class="mat-padding">  
143 - <tb-profile-lwm2m-device-config-server  
144 - [required]="required"  
145 - formControlName="lwm2mServer"  
146 - [bootstrapServerIs]=false>  
147 - </tb-profile-lwm2m-device-config-server>  
148 - </div> 119 + <tb-profile-lwm2m-device-config-server
  120 + [required]="required"
  121 + formControlName="lwm2mServer"
  122 + [bootstrapServerIs]=false>
  123 + </tb-profile-lwm2m-device-config-server>
149 </ng-template> 124 </ng-template>
150 </mat-expansion-panel> 125 </mat-expansion-panel>
151 </mat-accordion> 126 </mat-accordion>
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 /// 15 ///
16 16
17 import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; 17 import { DeviceProfileTransportConfiguration } from '@shared/models/device.models';
18 -import { Component, forwardRef, Input } from '@angular/core'; 18 +import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
19 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 19 import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
20 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 20 import { coerceBooleanProperty } from '@angular/cdk/coercion';
21 import { 21 import {
@@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util @@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util
39 import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; 39 import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
40 import { Direction } from '@shared/models/page/sort-order'; 40 import { Direction } from '@shared/models/page/sort-order';
41 import _ from 'lodash'; 41 import _ from 'lodash';
  42 +import { Subject } from 'rxjs';
  43 +import { takeUntil } from 'rxjs/operators';
42 44
43 @Component({ 45 @Component({
44 selector: 'tb-profile-lwm2m-device-transport-configuration', 46 selector: 'tb-profile-lwm2m-device-transport-configuration',
@@ -49,11 +51,12 @@ import _ from 'lodash'; @@ -49,11 +51,12 @@ import _ from 'lodash';
49 multi: true 51 multi: true
50 }] 52 }]
51 }) 53 })
52 -export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators { 54 +export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators, OnDestroy {
53 55
54 private configurationValue: Lwm2mProfileConfigModels; 56 private configurationValue: Lwm2mProfileConfigModels;
55 private requiredValue: boolean; 57 private requiredValue: boolean;
56 private disabled = false; 58 private disabled = false;
  59 + private destroy$ = new Subject();
57 60
58 bindingModeType = BINDING_MODE; 61 bindingModeType = BINDING_MODE;
59 bindingModeTypes = Object.keys(BINDING_MODE); 62 bindingModeTypes = Object.keys(BINDING_MODE);
@@ -87,17 +90,21 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -87,17 +90,21 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
87 lifetime: [null, Validators.required], 90 lifetime: [null, Validators.required],
88 defaultMinPeriod: [null, Validators.required], 91 defaultMinPeriod: [null, Validators.required],
89 notifIfDisabled: [true, []], 92 notifIfDisabled: [true, []],
90 - binding:[], 93 + binding: [],
91 bootstrapServer: [null, Validators.required], 94 bootstrapServer: [null, Validators.required],
92 lwm2mServer: [null, Validators.required], 95 lwm2mServer: [null, Validators.required],
93 }); 96 });
94 this.lwm2mDeviceConfigFormGroup = this.fb.group({ 97 this.lwm2mDeviceConfigFormGroup = this.fb.group({
95 configurationJson: [null, Validators.required] 98 configurationJson: [null, Validators.required]
96 }); 99 });
97 - this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { 100 + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe(
  101 + takeUntil(this.destroy$)
  102 + ).subscribe((value) => {
98 this.updateDeviceProfileValue(value); 103 this.updateDeviceProfileValue(value);
99 }); 104 });
100 - this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { 105 + this.lwm2mDeviceConfigFormGroup.valueChanges.pipe(
  106 + takeUntil(this.destroy$)
  107 + ).subscribe(() => {
101 this.updateModel(); 108 this.updateModel();
102 }); 109 });
103 this.sortFunction = this.sortObjectKeyPathJson; 110 this.sortFunction = this.sortObjectKeyPathJson;
@@ -110,6 +117,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -110,6 +117,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
110 registerOnTouched(fn: any): void { 117 registerOnTouched(fn: any): void {
111 } 118 }
112 119
  120 + ngOnDestroy() {
  121 + this.destroy$.next();
  122 + this.destroy$.complete();
  123 + }
  124 +
113 setDisabledState(isDisabled: boolean): void { 125 setDisabledState(isDisabled: boolean): void {
114 this.disabled = isDisabled; 126 this.disabled = isDisabled;
115 if (isDisabled) { 127 if (isDisabled) {
@@ -122,11 +134,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -122,11 +134,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
122 } 134 }
123 135
124 writeValue(value: Lwm2mProfileConfigModels | null): void { 136 writeValue(value: Lwm2mProfileConfigModels | null): void {
125 - this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value;  
126 - this.lwm2mDeviceConfigFormGroup.patchValue({  
127 - configurationJson: this.configurationValue  
128 - }, {emitEvent: false});  
129 - this.initWriteValue(); 137 + if (isDefinedAndNotNull(value)) {
  138 + if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) {
  139 + this.configurationValue = value;
  140 + } else {
  141 + this.configurationValue = getDefaultProfileConfig();
  142 + }
  143 + this.lwm2mDeviceConfigFormGroup.patchValue({
  144 + configurationJson: this.configurationValue
  145 + }, {emitEvent: false});
  146 + this.initWriteValue();
  147 + }
130 } 148 }
131 149
132 private initWriteValue = (): void => { 150 private initWriteValue = (): void => {
@@ -252,7 +270,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -252,7 +270,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
252 instanceUpdate.id = instanceId; 270 instanceUpdate.id = instanceId;
253 instanceUpdate.resources.forEach(resource => { 271 instanceUpdate.resources.forEach(resource => {
254 resource.keyName = _.camelCase(resource.name + instanceUpdate.id); 272 resource.keyName = _.camelCase(resource.name + instanceUpdate.id);
255 - }) 273 + });
256 return instanceUpdate; 274 return instanceUpdate;
257 } 275 }
258 276
@@ -15,10 +15,19 @@ @@ -15,10 +15,19 @@
15 /// 15 ///
16 16
17 import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; 17 import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
18 -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 18 +import {
  19 + ControlValueAccessor,
  20 + FormBuilder,
  21 + FormGroup,
  22 + NG_VALIDATORS,
  23 + NG_VALUE_ACCESSOR,
  24 + ValidationErrors,
  25 + Validator,
  26 + Validators
  27 +} from '@angular/forms';
19 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 28 import { coerceBooleanProperty } from '@angular/cdk/coercion';
20 import { Observable } from 'rxjs'; 29 import { Observable } from 'rxjs';
21 -import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; 30 +import { distinctUntilChanged, filter, mergeMap, share, tap } from 'rxjs/operators';
22 import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; 31 import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models';
23 import { DeviceProfileService } from '@core/http/device-profile.service'; 32 import { DeviceProfileService } from '@core/http/device-profile.service';
24 import { Direction } from '@shared/models/page/sort-order'; 33 import { Direction } from '@shared/models/page/sort-order';
@@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link'; @@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link';
33 provide: NG_VALUE_ACCESSOR, 42 provide: NG_VALUE_ACCESSOR,
34 useExisting: forwardRef(() => Lwm2mObjectListComponent), 43 useExisting: forwardRef(() => Lwm2mObjectListComponent),
35 multi: true 44 multi: true
36 - }] 45 + },
  46 + {
  47 + provide: NG_VALIDATORS,
  48 + useExisting: forwardRef(() => Lwm2mObjectListComponent),
  49 + multi: true
  50 + }
  51 + ]
37 }) 52 })
38 -export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validators { 53 +export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validator {
39 54
40 private requiredValue: boolean; 55 private requiredValue: boolean;
41 private dirty = false; 56 private dirty = false;
42 - private lw2mModels: Observable<Array<ObjectLwM2M>>;  
43 private modelValue: Array<string> = []; 57 private modelValue: Array<string> = [];
44 58
45 lwm2mListFormGroup: FormGroup; 59 lwm2mListFormGroup: FormGroup;
@@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
78 } 92 }
79 93
80 private updateValidators = (): void => { 94 private updateValidators = (): void => {
81 - this.lwm2mListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []);  
82 - this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity(); 95 + this.lwm2mListFormGroup.get('objectsList').setValidators(this.required ? [Validators.required] : []);
  96 + this.lwm2mListFormGroup.get('objectsList').updateValueAndValidity();
83 } 97 }
84 98
85 registerOnChange(fn: any): void { 99 registerOnChange(fn: any): void {
@@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
92 ngOnInit() { 106 ngOnInit() {
93 this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges 107 this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges
94 .pipe( 108 .pipe(
  109 + distinctUntilChanged(),
95 tap((value) => { 110 tap((value) => {
96 if (value && typeof value !== 'string') { 111 if (value && typeof value !== 'string') {
97 this.add(value); 112 this.add(value);
@@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
100 } 115 }
101 }), 116 }),
102 filter(searchText => isString(searchText)), 117 filter(searchText => isString(searchText)),
103 - mergeMap(searchText => this.fetchListObjects(searchText)) 118 + mergeMap(searchText => this.fetchListObjects(searchText)),
  119 + share()
104 ); 120 );
105 } 121 }
106 122
@@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
131 } 147 }
132 } 148 }
133 149
  150 + validate(): ValidationErrors | null {
  151 + return this.lwm2mListFormGroup.valid ? null : {
  152 + lwm2mListObj: false
  153 + };
  154 + }
  155 +
134 private add(object: ObjectLwM2M): void { 156 private add(object: ObjectLwM2M): void {
135 if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { 157 if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) {
136 this.modelValue.push(object.keyId); 158 this.modelValue.push(object.keyId);
@@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
157 return object ? object.name : undefined; 179 return object ? object.name : undefined;
158 } 180 }
159 181
160 - private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => { 182 + private fetchListObjects = (searchText: string): Observable<Array<ObjectLwM2M>> => {
161 this.searchText = searchText; 183 this.searchText = searchText;
162 - return this.getLwM2mModelsPage().pipe(  
163 - map(objectLwM2Ms => objectLwM2Ms)  
164 - );  
165 - }  
166 -  
167 - private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {  
168 const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { 184 const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
169 property: 'id', 185 property: 'id',
170 direction: Direction.ASC 186 direction: Direction.ASC
171 }); 187 });
172 - this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(  
173 - publishReplay(1),  
174 - refCount()  
175 - );  
176 - return this.lw2mModels; 188 + return this.deviceProfileService.getLwm2mObjectsPage(pageLink);
177 } 189 }
178 190
179 onFocus = (): void => { 191 onFocus = (): void => {
@@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V @@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
183 } 195 }
184 } 196 }
185 197
186 - private clear = (value: string = ''): void => {  
187 - this.objectInput.nativeElement.value = value; 198 + private clear = (): void => {
188 this.searchText = ''; 199 this.searchText = '';
189 - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value); 200 + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null);
190 setTimeout(() => { 201 setTimeout(() => {
191 this.objectInput.nativeElement.blur(); 202 this.objectInput.nativeElement.blur();
192 this.objectInput.nativeElement.focus(); 203 this.objectInput.nativeElement.focus();
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<section [formGroup]="resourceFormGroup" class="mat-padding"> 18 +<section [formGroup]="resourceFormGroup">
19 <div fxLayout="row" fxFill formArrayName="resources" 19 <div fxLayout="row" fxFill formArrayName="resources"
20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> 20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> 21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
@@ -46,14 +46,14 @@ @@ -46,14 +46,14 @@
46 </div> 46 </div>
47 <div fxFlex="10" fxLayoutAlign="center center"> 47 <div fxFlex="10" fxLayoutAlign="center center">
48 <mat-checkbox formControlName="attribute" color="warn" 48 <mat-checkbox formControlName="attribute" color="warn"
49 - [checked]="updateObserve(i)" 49 + (change)="updateObserve(i)"
50 matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" 50 matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}"
51 matTooltipPosition="above"> 51 matTooltipPosition="above">
52 </mat-checkbox> 52 </mat-checkbox>
53 </div> 53 </div>
54 <div fxFlex="10" fxLayoutAlign="center center"> 54 <div fxFlex="10" fxLayoutAlign="center center">
55 <mat-checkbox formControlName="telemetry" color="primary" 55 <mat-checkbox formControlName="telemetry" color="primary"
56 - [checked]="updateObserve(i)" 56 + (change)="updateObserve(i)"
57 matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" 57 matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}"
58 matTooltipPosition="above"> 58 matTooltipPosition="above">
59 </mat-checkbox> 59 </mat-checkbox>
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 --> 17 -->
18 <section [formGroup]="observeAttrTelemetryFormGroup"> 18 <section [formGroup]="observeAttrTelemetryFormGroup">
19 - <mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M"> 19 + <mat-accordion multi="true" formArrayName="clientLwM2M">
20 <mat-expansion-panel 20 <mat-expansion-panel
21 *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" 21 *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
22 [formGroupName]="i"> 22 [formGroupName]="i">
@@ -24,6 +24,9 @@ @@ -24,6 +24,9 @@
24 } 24 }
25 25
26 :host{ 26 :host{
  27 + section {
  28 + padding: 2px;
  29 + }
27 .instance-list { 30 .instance-list {
28 mat-expansion-panel-header { 31 mat-expansion-panel-header {
29 color: inherit; 32 color: inherit;
@@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>( @@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>(
64 [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], 64 [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'],
65 [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], 65 [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'],
66 [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'], 66 [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'],
67 - [BINDING_MODE.T,'T: TCP connection in standard mode'], 67 + [BINDING_MODE.T, 'T: TCP connection in standard mode'],
68 [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], 68 [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'],
69 [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], 69 [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'],
70 [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'], 70 [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'],
@@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels { @@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels {
162 clientLwM2mSettings: ClientLwM2mSettings; 162 clientLwM2mSettings: ClientLwM2mSettings;
163 observeAttr: ObservableAttributes; 163 observeAttr: ObservableAttributes;
164 bootstrap: BootstrapSecurityConfig; 164 bootstrap: BootstrapSecurityConfig;
165 -  
166 } 165 }
167 166
168 export interface ClientLwM2mSettings { 167 export interface ClientLwM2mSettings {
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, forwardRef, Input, OnInit } from '@angular/core'; 17 +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
18 import { 18 import {
19 ControlValueAccessor, 19 ControlValueAccessor,
20 FormBuilder, 20 FormBuilder,
@@ -39,6 +39,8 @@ import { @@ -39,6 +39,8 @@ import {
39 transportPayloadTypeTranslationMap 39 transportPayloadTypeTranslationMap
40 } from '@shared/models/device.models'; 40 } from '@shared/models/device.models';
41 import { isDefinedAndNotNull } from '@core/utils'; 41 import { isDefinedAndNotNull } from '@core/utils';
  42 +import { Subject } from 'rxjs';
  43 +import { takeUntil } from 'rxjs/operators';
42 44
43 @Component({ 45 @Component({
44 selector: 'tb-mqtt-device-profile-transport-configuration', 46 selector: 'tb-mqtt-device-profile-transport-configuration',
@@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils'; @@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils';
50 multi: true 52 multi: true
51 }] 53 }]
52 }) 54 })
53 -export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { 55 +export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
54 56
55 transportPayloadTypes = Object.keys(TransportPayloadType); 57 transportPayloadTypes = Object.keys(TransportPayloadType);
56 58
@@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
58 60
59 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; 61 mqttDeviceProfileTransportConfigurationFormGroup: FormGroup;
60 62
  63 + private destroy$ = new Subject();
61 private requiredValue: boolean; 64 private requiredValue: boolean;
62 65
63 get required(): boolean { 66 get required(): boolean {
@@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
98 }) 101 })
99 }, {validator: this.uniqueDeviceTopicValidator} 102 }, {validator: this.uniqueDeviceTopicValidator}
100 ); 103 );
101 - this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType')  
102 - .valueChanges.subscribe(payloadType => { 104 + this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType').valueChanges.pipe(
  105 + takeUntil(this.destroy$)
  106 + ).subscribe(payloadType => {
103 this.updateTransportPayloadBasedControls(payloadType, true); 107 this.updateTransportPayloadBasedControls(payloadType, true);
104 }); 108 });
105 - this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { 109 + this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
  110 + takeUntil(this.destroy$)
  111 + ).subscribe(() => {
106 this.updateModel(); 112 this.updateModel();
107 }); 113 });
108 } 114 }
109 115
  116 + ngOnDestroy() {
  117 + this.destroy$.next();
  118 + this.destroy$.complete();
  119 + }
  120 +
110 setDisabledState(isDisabled: boolean): void { 121 setDisabledState(isDisabled: boolean): void {
111 this.disabled = isDisabled; 122 this.disabled = isDisabled;
112 if (this.disabled) { 123 if (this.disabled) {
@@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
192 } 203 }
193 204
194 private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { 205 private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null {
195 - if (control.value) {  
196 - const formValue = control.value as MqttDeviceProfileTransportConfiguration; 206 + if (control.getRawValue()) {
  207 + const formValue = control.getRawValue() as MqttDeviceProfileTransportConfiguration;
197 if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { 208 if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) {
198 return {unique: true}; 209 return {unique: true};
199 } 210 }
@@ -14,17 +14,19 @@ @@ -14,17 +14,19 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import {Component, forwardRef, Input, OnInit} from '@angular/core';  
18 -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';  
19 -import {Store} from '@ngrx/store';  
20 -import {AppState} from '@app/core/core.state';  
21 -import {coerceBooleanProperty} from '@angular/cdk/coercion'; 17 +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
  18 +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
  19 +import { Store } from '@ngrx/store';
  20 +import { AppState } from '@app/core/core.state';
  21 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
22 import { 22 import {
23 DeviceProfileTransportConfiguration, 23 DeviceProfileTransportConfiguration,
24 DeviceTransportType, 24 DeviceTransportType,
25 SnmpDeviceProfileTransportConfiguration 25 SnmpDeviceProfileTransportConfiguration
26 } from '@shared/models/device.models'; 26 } from '@shared/models/device.models';
27 -import {isDefinedAndNotNull} from "@core/utils"; 27 +import { isDefinedAndNotNull } from '@core/utils';
  28 +import { Subject } from 'rxjs';
  29 +import { takeUntil } from 'rxjs/operators';
28 30
29 export interface OidMappingConfiguration { 31 export interface OidMappingConfiguration {
30 isAttribute: boolean; 32 isAttribute: boolean;
@@ -44,8 +46,11 @@ export interface OidMappingConfiguration { @@ -44,8 +46,11 @@ export interface OidMappingConfiguration {
44 multi: true 46 multi: true
45 }] 47 }]
46 }) 48 })
47 -export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { 49 +export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
  50 +
48 snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; 51 snmpDeviceProfileTransportConfigurationFormGroup: FormGroup;
  52 +
  53 + private destroy$ = new Subject();
49 private requiredValue: boolean; 54 private requiredValue: boolean;
50 private configuration = []; 55 private configuration = [];
51 56
@@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control @@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control
71 this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ 76 this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({
72 configuration: [null, Validators.required] 77 configuration: [null, Validators.required]
73 }); 78 });
74 - this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { 79 + this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
  80 + takeUntil(this.destroy$)
  81 + ).subscribe(() => {
75 this.updateModel(); 82 this.updateModel();
76 }); 83 });
77 } 84 }
78 85
  86 + ngOnDestroy() {
  87 + this.destroy$.next();
  88 + this.destroy$.complete();
  89 + }
  90 +
79 registerOnChange(fn: any): void { 91 registerOnChange(fn: any): void {
80 this.propagateChange = fn; 92 this.propagateChange = fn;
81 } 93 }
@@ -32,7 +32,7 @@ import { @@ -32,7 +32,7 @@ import {
32 }) 32 })
33 export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { 33 export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> {
34 34
35 - deviceTransportTypes = Object.keys(DeviceTransportType); 35 + deviceTransportTypes = Object.values(DeviceTransportType);
36 36
37 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; 37 deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
38 38