Commit 6cd7e3df4bb9a20a57bf351e702aaf131ec0ed91
Committed by
GitHub
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
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
deleted
100644 → 0
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, |
@@ -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"> |
@@ -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 |