Commit 87874c3eb66c0eada6c432edd10381ded4cbaeec

Authored by nickAS21
2 parents 6fbd34d6 6cd7e3df

Merge branch 'master' into lwm2m_create_coap_resource

# Conflicts:
#	ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html
Showing 28 changed files with 509 additions and 353 deletions
@@ -16,16 +16,17 @@ @@ -16,16 +16,17 @@
16 16
17 import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; 17 import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models';
18 import { 18 import {
19 - AggregationType, calculateIntervalComparisonEndTime,  
20 - calculateIntervalEndTime, calculateIntervalStartEndTime, 19 + AggregationType,
  20 + calculateIntervalComparisonEndTime,
  21 + calculateIntervalEndTime,
  22 + calculateIntervalStartEndTime,
21 getCurrentTime, 23 getCurrentTime,
22 - getCurrentTimeForComparison, getTime, 24 + getTime,
23 SubscriptionTimewindow 25 SubscriptionTimewindow
24 } from '@shared/models/time/time.models'; 26 } from '@shared/models/time/time.models';
25 import { UtilsService } from '@core/services/utils.service'; 27 import { UtilsService } from '@core/services/utils.service';
26 -import { deepClone } from '@core/utils'; 28 +import { deepClone, isNumeric } from '@core/utils';
27 import Timeout = NodeJS.Timeout; 29 import Timeout = NodeJS.Timeout;
28 -import * as moment_ from 'moment';  
29 30
30 export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void; 31 export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void;
31 32
@@ -407,24 +408,11 @@ export class DataAggregator { @@ -407,24 +408,11 @@ export class DataAggregator {
407 } 408 }
408 } 409 }
409 410
410 - private isNumeric(val: any): boolean {  
411 - return (val - parseFloat( val ) + 1) >= 0;  
412 - }  
413 -  
414 private convertValue(val: string): any { 411 private convertValue(val: string): any {
415 - if (!this.noAggregation || val && this.isNumeric(val)) { 412 + if (!this.noAggregation || val && isNumeric(val) && Number(val).toString() === val) {
416 return Number(val); 413 return Number(val);
417 - } else {  
418 - return val;  
419 - }  
420 - }  
421 -  
422 - private getCurrentTime() {  
423 - if (this.subsTw.timeForComparison) {  
424 - return getCurrentTimeForComparison(this.subsTw.timeForComparison as moment_.unitOfTime.DurationConstructor, this.subsTw.timezone);  
425 - } else {  
426 - return getCurrentTime(this.subsTw.timezone);  
427 } 414 }
  415 + return val;
428 } 416 }
429 417
430 } 418 }
@@ -38,7 +38,7 @@ import { @@ -38,7 +38,7 @@ import {
38 } from '@shared/models/telemetry/telemetry.models'; 38 } from '@shared/models/telemetry/telemetry.models';
39 import { UtilsService } from '@core/services/utils.service'; 39 import { UtilsService } from '@core/services/utils.service';
40 import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service'; 40 import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service';
41 -import { deepClone, isDefined, isDefinedAndNotNull, isObject, objectHashCode } from '@core/utils'; 41 +import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isObject, objectHashCode } from '@core/utils';
42 import { PageData } from '@shared/models/page/page-data'; 42 import { PageData } from '@shared/models/page/page-data';
43 import { DataAggregator } from '@core/api/data-aggregator'; 43 import { DataAggregator } from '@core/api/data-aggregator';
44 import { NULL_UUID } from '@shared/models/id/has-uuid'; 44 import { NULL_UUID } from '@shared/models/id/has-uuid';
@@ -667,8 +667,7 @@ export class EntityDataSubscription { @@ -667,8 +667,7 @@ export class EntityDataSubscription {
667 if (prevDataCb) { 667 if (prevDataCb) {
668 dataAggregator.updateOnDataCb(prevDataCb); 668 dataAggregator.updateOnDataCb(prevDataCb);
669 } 669 }
670 - }  
671 - if (!this.history && !isUpdate) { 670 + } else if (!this.history && !isUpdate) {
672 this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); 671 this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb);
673 } 672 }
674 } 673 }
@@ -743,16 +742,11 @@ export class EntityDataSubscription { @@ -743,16 +742,11 @@ export class EntityDataSubscription {
743 } 742 }
744 } 743 }
745 744
746 - private isNumeric(val: any): boolean {  
747 - return (val - parseFloat( val ) + 1) >= 0;  
748 - }  
749 -  
750 private convertValue(val: string): any { 745 private convertValue(val: string): any {
751 - if (val && this.isNumeric(val) && Number(val).toString() === val) { 746 + if (val && isNumeric(val) && Number(val).toString() === val) {
752 return Number(val); 747 return Number(val);
753 - } else {  
754 - return val;  
755 } 748 }
  749 + return val;
756 } 750 }
757 751
758 private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData { 752 private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData {
@@ -231,7 +231,6 @@ export class DashboardUtilsService { @@ -231,7 +231,6 @@ export class DashboardUtilsService {
231 private createDefaultGridSettings(): GridSettings { 231 private createDefaultGridSettings(): GridSettings {
232 return { 232 return {
233 backgroundColor: '#eeeeee', 233 backgroundColor: '#eeeeee',
234 - color: 'rgba(0,0,0,0.870588)',  
235 columns: 24, 234 columns: 24,
236 margin: 10, 235 margin: 10,
237 backgroundSizeMode: '100%' 236 backgroundSizeMode: '100%'
@@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service'; @@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service';
27 27
28 const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; 28 const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler';
29 const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; 29 const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler';
  30 +const dashboardLayoutHandler = 'tbMobileDashboardLayoutHandler';
30 const navigationHandler = 'tbMobileNavigationHandler'; 31 const navigationHandler = 'tbMobileNavigationHandler';
31 const mobileHandler = 'tbMobileHandler'; 32 const mobileHandler = 'tbMobileHandler';
32 33
@@ -43,6 +44,7 @@ export class MobileService { @@ -43,6 +44,7 @@ export class MobileService {
43 44
44 private reloadUserObservable: Observable<boolean>; 45 private reloadUserObservable: Observable<boolean>;
45 private lastDashboardId: string; 46 private lastDashboardId: string;
  47 + private toggleLayoutFunction: () => void;
46 48
47 constructor(@Inject(WINDOW) private window: Window, 49 constructor(@Inject(WINDOW) private window: Window,
48 private router: Router, 50 private router: Router,
@@ -65,12 +67,26 @@ export class MobileService { @@ -65,12 +67,26 @@ export class MobileService {
65 } 67 }
66 } 68 }
67 69
68 - public onDashboardLoaded() { 70 + public onDashboardLoaded(hasRightLayout: boolean, rightLayoutOpened: boolean) {
69 if (this.mobileApp) { 71 if (this.mobileApp) {
70 - this.mobileChannel.callHandler(dashboardLoadedHandler); 72 + this.mobileChannel.callHandler(dashboardLoadedHandler, hasRightLayout, rightLayoutOpened);
71 } 73 }
72 } 74 }
73 75
  76 + public onDashboardRightLayoutChanged(opened: boolean) {
  77 + if (this.mobileApp) {
  78 + this.mobileChannel.callHandler(dashboardLayoutHandler, opened);
  79 + }
  80 + }
  81 +
  82 + public registerToggleLayoutFunction(toggleLayoutFunction: () => void) {
  83 + this.toggleLayoutFunction = toggleLayoutFunction;
  84 + }
  85 +
  86 + public unregisterToggleLayoutFunction() {
  87 + this.toggleLayoutFunction = null;
  88 + }
  89 +
74 public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): 90 public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]):
75 Observable<WidgetMobileActionResult<T>> { 91 Observable<WidgetMobileActionResult<T>> {
76 if (this.mobileApp) { 92 if (this.mobileApp) {
@@ -110,6 +126,11 @@ export class MobileService { @@ -110,6 +126,11 @@ export class MobileService {
110 const reloadUserMessage: ReloadUserMessage = message.data; 126 const reloadUserMessage: ReloadUserMessage = message.data;
111 this.reloadUser(reloadUserMessage); 127 this.reloadUser(reloadUserMessage);
112 break; 128 break;
  129 + case 'toggleDashboardLayout':
  130 + if (this.toggleLayoutFunction) {
  131 + this.toggleLayoutFunction();
  132 + }
  133 + break;
113 } 134 }
114 } 135 }
115 } 136 }
@@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
149 @Input() 149 @Input()
150 currentState: string; 150 currentState: string;
151 151
  152 + private hideToolbarValue = false;
  153 +
152 @Input() 154 @Input()
153 - hideToolbar: boolean; 155 + set hideToolbar(hideToolbar: boolean) {
  156 + this.hideToolbarValue = hideToolbar;
  157 + }
  158 +
  159 + get hideToolbar(): boolean {
  160 + return (this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit;
  161 + }
154 162
155 @Input() 163 @Input()
156 syncStateWithQueryParam = true; 164 syncStateWithQueryParam = true;
@@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
269 return !this.widgetEditMode && !this.hideToolbar && 277 return !this.widgetEditMode && !this.hideToolbar &&
270 (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch()); 278 (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch());
271 } 279 }
  280 +
272 set toolbarOpened(toolbarOpened: boolean) { 281 set toolbarOpened(toolbarOpened: boolean) {
273 } 282 }
274 283
@@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
329 this.dashboardCtx.aliasController.updateAliases(); 338 this.dashboardCtx.aliasController.updateAliases();
330 setTimeout(() => { 339 setTimeout(() => {
331 this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName()); 340 this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName());
332 - this.mobileService.onDashboardLoaded(); 341 + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened);
333 }); 342 });
334 } 343 }
335 } 344 }
@@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
340 this.isMobile = !state.matches; 349 this.isMobile = !state.matches;
341 } 350 }
342 )); 351 ));
  352 + if (this.isMobileApp) {
  353 + this.mobileService.registerToggleLayoutFunction(() => {
  354 + setTimeout(() => {
  355 + this.toggleLayouts();
  356 + this.cd.detectChanges();
  357 + });
  358 + });
  359 + }
343 } 360 }
344 361
345 private init(data: any) { 362 private init(data: any) {
@@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
430 } 447 }
431 448
432 ngOnDestroy(): void { 449 ngOnDestroy(): void {
  450 + if (this.isMobileApp) {
  451 + this.mobileService.unregisterToggleLayoutFunction();
  452 + }
433 this.rxSubscriptions.forEach((subscription) => { 453 this.rxSubscriptions.forEach((subscription) => {
434 subscription.unsubscribe(); 454 subscription.unsubscribe();
435 }); 455 });
@@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
469 } 489 }
470 } 490 }
471 491
  492 + private hideToolbarSetting(): boolean {
  493 + if (this.dashboard.configuration.settings &&
  494 + isDefined(this.dashboard.configuration.settings.hideToolbar)) {
  495 + return this.dashboard.configuration.settings.hideToolbar;
  496 + } else {
  497 + return false;
  498 + }
  499 + }
  500 +
472 public displayTitle(): boolean { 501 public displayTitle(): boolean {
473 if (this.dashboard.configuration.settings && 502 if (this.dashboard.configuration.settings &&
474 isDefined(this.dashboard.configuration.settings.showTitle)) { 503 isDefined(this.dashboard.configuration.settings.showTitle)) {
@@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
546 } 575 }
547 576
548 public showRightLayoutSwitch(): boolean { 577 public showRightLayoutSwitch(): boolean {
549 - return this.isMobile && this.layouts.right.show; 578 + return this.isMobile && !this.isMobileApp && this.layouts.right.show;
550 } 579 }
551 580
552 public toggleLayouts() { 581 public toggleLayouts() {
553 this.isRightLayoutOpened = !this.isRightLayoutOpened; 582 this.isRightLayoutOpened = !this.isRightLayoutOpened;
  583 + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened);
554 } 584 }
555 585
556 public openRightLayout() { 586 public openRightLayout() {
557 this.isRightLayoutOpened = true; 587 this.isRightLayoutOpened = true;
  588 + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened);
558 } 589 }
559 590
560 public mainLayoutWidth(): string { 591 public mainLayoutWidth(): string {
@@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
782 this.updateLayouts(layoutsData); 813 this.updateLayouts(layoutsData);
783 } 814 }
784 setTimeout(() => { 815 setTimeout(() => {
785 - this.mobileService.onDashboardLoaded(); 816 + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened);
786 }); 817 });
787 } 818 }
788 819
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<form (ngSubmit)="save()"> 18 +<form (ngSubmit)="save()" style="width: 750px;">
19 <mat-toolbar color="primary"> 19 <mat-toolbar color="primary">
20 <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2> 20 <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2>
21 <span fxFlex></span> 21 <span fxFlex></span>
@@ -28,8 +28,8 @@ @@ -28,8 +28,8 @@
28 <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> 28 <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
29 </mat-progress-bar> 29 </mat-progress-bar>
30 <div mat-dialog-content> 30 <div mat-dialog-content>
31 - <fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="16px">  
32 - <div *ngIf="settings" [formGroup]="settingsFormGroup"> 31 + <fieldset [disabled]="isLoading$ | async">
  32 + <div *ngIf="settings" [formGroup]="settingsFormGroup" fxLayout="column">
33 <mat-form-field class="mat-block"> 33 <mat-form-field class="mat-block">
34 <mat-label translate>dashboard.state-controller</mat-label> 34 <mat-label translate>dashboard.state-controller</mat-label>
35 <mat-select required formControlName="stateControllerId"> 35 <mat-select required formControlName="stateControllerId">
@@ -38,112 +38,116 @@ @@ -38,112 +38,116 @@
38 </mat-option> 38 </mat-option>
39 </mat-select> 39 </mat-select>
40 </mat-form-field> 40 </mat-form-field>
41 - <div fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">  
42 - <mat-checkbox fxFlex formControlName="toolbarAlwaysOpen">  
43 - {{ 'dashboard.toolbar-always-open' | translate }}  
44 - </mat-checkbox>  
45 - <mat-checkbox fxFlex formControlName="showTitle"> 41 + <fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px">
  42 + <legend class="group-title" translate>dashboard.title-settings</legend>
  43 + <mat-slide-toggle fxFlex formControlName="showTitle">
46 {{ 'dashboard.display-title' | translate }} 44 {{ 'dashboard.display-title' | translate }}
47 - </mat-checkbox> 45 + </mat-slide-toggle>
48 <tb-color-input fxFlex 46 <tb-color-input fxFlex
49 label="{{'dashboard.title-color' | translate}}" 47 label="{{'dashboard.title-color' | translate}}"
50 icon="format_color_fill" 48 icon="format_color_fill"
51 openOnInput 49 openOnInput
52 formControlName="titleColor"> 50 formControlName="titleColor">
53 </tb-color-input> 51 </tb-color-input>
54 - </div>  
55 - <div fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 8px;"  
56 - fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">  
57 - <mat-checkbox fxFlex formControlName="showDashboardsSelect"> 52 + </fieldset>
  53 + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
  54 + <legend class="group-title" translate>dashboard.dashboard-logo-settings</legend>
  55 + <mat-slide-toggle formControlName="showDashboardLogo">
  56 + {{ 'dashboard.display-dashboard-logo' | translate }}
  57 + </mat-slide-toggle>
  58 + <tb-image-input fxFlex
  59 + label="{{'dashboard.dashboard-logo-image' | translate}}"
  60 + formControlName="dashboardLogoUrl">
  61 + </tb-image-input>
  62 + </fieldset>
  63 + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
  64 + <legend class="group-title" translate>dashboard.toolbar-settings</legend>
  65 + <mat-slide-toggle formControlName="hideToolbar">
  66 + {{ 'dashboard.hide-toolbar' | translate }}
  67 + </mat-slide-toggle>
  68 + <mat-slide-toggle formControlName="toolbarAlwaysOpen">
  69 + {{ 'dashboard.toolbar-always-open' | translate }}
  70 + </mat-slide-toggle>
  71 + <mat-slide-toggle formControlName="showDashboardsSelect">
58 {{ 'dashboard.display-dashboards-selection' | translate }} 72 {{ 'dashboard.display-dashboards-selection' | translate }}
59 - </mat-checkbox>  
60 - <mat-checkbox fxFlex formControlName="showEntitiesSelect"> 73 + </mat-slide-toggle>
  74 + <mat-slide-toggle formControlName="showEntitiesSelect">
61 {{ 'dashboard.display-entities-selection' | translate }} 75 {{ 'dashboard.display-entities-selection' | translate }}
62 - </mat-checkbox>  
63 - <mat-checkbox fxFlex formControlName="showFilters"> 76 + </mat-slide-toggle>
  77 + <mat-slide-toggle formControlName="showFilters">
64 {{ 'dashboard.display-filters' | translate }} 78 {{ 'dashboard.display-filters' | translate }}
65 - </mat-checkbox>  
66 - <mat-checkbox fxFlex formControlName="showDashboardTimewindow"> 79 + </mat-slide-toggle>
  80 + <mat-slide-toggle formControlName="showDashboardTimewindow">
67 {{ 'dashboard.display-dashboard-timewindow' | translate }} 81 {{ 'dashboard.display-dashboard-timewindow' | translate }}
68 - </mat-checkbox>  
69 - <mat-checkbox fxFlex formControlName="showDashboardExport"> 82 + </mat-slide-toggle>
  83 + <mat-slide-toggle formControlName="showDashboardExport">
70 {{ 'dashboard.display-dashboard-export' | translate }} 84 {{ 'dashboard.display-dashboard-export' | translate }}
71 - </mat-checkbox>  
72 - <mat-checkbox fxFlex formControlName="showUpdateDashboardImage"> 85 + </mat-slide-toggle>
  86 + <mat-slide-toggle formControlName="showUpdateDashboardImage">
73 {{ 'dashboard.display-update-dashboard-image' | translate }} 87 {{ 'dashboard.display-update-dashboard-image' | translate }}
74 - </mat-checkbox>  
75 - </div>  
76 - <mat-checkbox formControlName="showDashboardLogo">  
77 - {{ 'dashboard.display-dashboard-logo' | translate }}  
78 - </mat-checkbox>  
79 - <tb-image-input fxFlex *ngIf="settingsFormGroup.get('showDashboardLogo').value"  
80 - label="{{'dashboard.dashboard-logo-image' | translate}}"  
81 - formControlName="dashboardLogoUrl">  
82 - </tb-image-input> 88 + </mat-slide-toggle>
  89 + </fieldset>
83 </div> 90 </div>
84 - <div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup">  
85 - <tb-color-input fxFlex  
86 - label="{{'layout.color' | translate}}"  
87 - icon="format_color_fill"  
88 - openOnInput  
89 - formControlName="color">  
90 - </tb-color-input>  
91 - <mat-form-field class="mat-block">  
92 - <mat-label translate>dashboard.columns-count</mat-label>  
93 - <input matInput formControlName="columns" type="number" step="any" min="10"  
94 - max="1000" required>  
95 - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')">  
96 - {{ 'dashboard.columns-count-required' | translate }}  
97 - </mat-error>  
98 - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')">  
99 - {{ 'dashboard.min-columns-count-message' | translate }}  
100 - </mat-error>  
101 - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')">  
102 - {{ 'dashboard.max-columns-count-message' | translate }}  
103 - </mat-error>  
104 - </mat-form-field>  
105 - <mat-form-field fxFlex class="mat-block">  
106 - <mat-label translate>dashboard.widgets-margins</mat-label>  
107 - <input matInput formControlName="margin" type="number" step="any" min="0"  
108 - max="50" required>  
109 - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')">  
110 - {{ 'dashboard.margin-required' | translate }}  
111 - </mat-error>  
112 - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')">  
113 - {{ 'dashboard.min-margin-message' | translate }}  
114 - </mat-error>  
115 - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')">  
116 - {{ 'dashboard.max-margin-message' | translate }}  
117 - </mat-error>  
118 - </mat-form-field>  
119 - <mat-checkbox fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;">  
120 - {{ 'dashboard.autofill-height' | translate }}  
121 - </mat-checkbox>  
122 - <tb-color-input fxFlex  
123 - label="{{'dashboard.background-color' | translate}}"  
124 - icon="format_color_fill"  
125 - openOnInput  
126 - formControlName="backgroundColor">  
127 - </tb-color-input>  
128 - <tb-image-input fxFlex  
129 - label="{{'dashboard.background-image' | translate}}"  
130 - formControlName="backgroundImageUrl">  
131 - </tb-image-input>  
132 - <mat-form-field class="mat-block">  
133 - <mat-label translate>dashboard.background-size-mode</mat-label>  
134 - <mat-select formControlName="backgroundSizeMode">  
135 - <mat-option value="100%">Fit width</mat-option>  
136 - <mat-option value="auto 100%">Fit height</mat-option>  
137 - <mat-option value="cover">Cover</mat-option>  
138 - <mat-option value="contain">Contain</mat-option>  
139 - <mat-option value="auto">Original size</mat-option>  
140 - </mat-select>  
141 - </mat-form-field>  
142 - <small translate>dashboard.mobile-layout</small>  
143 - <div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs fxLayoutGap="8px" style="margin-top: 8px">  
144 - <mat-checkbox fxFlex formControlName="mobileAutoFillHeight"> 91 + <div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup" fxLayout="column">
  92 + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
  93 + <legend class="group-title" translate>dashboard.layout-settings</legend>
  94 + <mat-form-field class="mat-block">
  95 + <mat-label translate>dashboard.columns-count</mat-label>
  96 + <input matInput formControlName="columns" type="number" step="any" min="10"
  97 + max="1000" required>
  98 + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')">
  99 + {{ 'dashboard.columns-count-required' | translate }}
  100 + </mat-error>
  101 + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')">
  102 + {{ 'dashboard.min-columns-count-message' | translate }}
  103 + </mat-error>
  104 + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')">
  105 + {{ 'dashboard.max-columns-count-message' | translate }}
  106 + </mat-error>
  107 + </mat-form-field>
  108 + <mat-form-field fxFlex class="mat-block">
  109 + <mat-label translate>dashboard.widgets-margins</mat-label>
  110 + <input matInput formControlName="margin" type="number" step="any" min="0"
  111 + max="50" required>
  112 + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')">
  113 + {{ 'dashboard.margin-required' | translate }}
  114 + </mat-error>
  115 + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')">
  116 + {{ 'dashboard.min-margin-message' | translate }}
  117 + </mat-error>
  118 + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')">
  119 + {{ 'dashboard.max-margin-message' | translate }}
  120 + </mat-error>
  121 + </mat-form-field>
  122 + <mat-slide-toggle fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;">
  123 + {{ 'dashboard.autofill-height' | translate }}
  124 + </mat-slide-toggle>
  125 + <tb-color-input fxFlex
  126 + label="{{'dashboard.background-color' | translate}}"
  127 + icon="format_color_fill"
  128 + openOnInput
  129 + formControlName="backgroundColor">
  130 + </tb-color-input>
  131 + <tb-image-input fxFlex
  132 + label="{{'dashboard.background-image' | translate}}"
  133 + formControlName="backgroundImageUrl">
  134 + </tb-image-input>
  135 + <mat-form-field class="mat-block">
  136 + <mat-label translate>dashboard.background-size-mode</mat-label>
  137 + <mat-select formControlName="backgroundSizeMode">
  138 + <mat-option value="100%">Fit width</mat-option>
  139 + <mat-option value="auto 100%">Fit height</mat-option>
  140 + <mat-option value="cover">Cover</mat-option>
  141 + <mat-option value="contain">Contain</mat-option>
  142 + <mat-option value="auto">Original size</mat-option>
  143 + </mat-select>
  144 + </mat-form-field>
  145 + </fieldset>
  146 + <fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px">
  147 + <legend class="group-title" translate>dashboard.mobile-layout</legend>
  148 + <mat-slide-toggle fxFlex formControlName="mobileAutoFillHeight">
145 {{ 'dashboard.autofill-height' | translate }} 149 {{ 'dashboard.autofill-height' | translate }}
146 - </mat-checkbox> 150 + </mat-slide-toggle>
147 <mat-form-field fxFlex class="mat-block"> 151 <mat-form-field fxFlex class="mat-block">
148 <mat-label translate>dashboard.mobile-row-height</mat-label> 152 <mat-label translate>dashboard.mobile-row-height</mat-label>
149 <input matInput formControlName="mobileRowHeight" type="number" step="any" min="5" 153 <input matInput formControlName="mobileRowHeight" type="number" step="any" min="5"
@@ -158,7 +162,7 @@ @@ -158,7 +162,7 @@
158 {{ 'dashboard.max-mobile-row-height-message' | translate }} 162 {{ 'dashboard.max-mobile-row-height-message' | translate }}
159 </mat-error> 163 </mat-error>
160 </mat-form-field> 164 </mat-form-field>
161 - </div> 165 + </fieldset>
162 </div> 166 </div>
163 </fieldset> 167 </fieldset>
164 </div> 168 </div>
@@ -14,13 +14,19 @@ @@ -14,13 +14,19 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 :host { 16 :host {
17 - small {  
18 - white-space: normal; 17 + .fields-group {
  18 + padding: 0 8px 8px;
  19 + margin: 10px 0;
  20 + border: 1px groove rgba(0, 0, 0, .25);
  21 + border-radius: 4px;
  22 + legend {
  23 + color: rgba(0, 0, 0, .7);
  24 + }
19 } 25 }
20 } 26 }
21 27
22 :host ::ng-deep { 28 :host ::ng-deep {
23 - .mat-checkbox-label { 29 + .mat-slide-toggle-content {
24 white-space: normal; 30 white-space: normal;
25 } 31 }
26 } 32 }
@@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS @@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
71 this.gridSettings = this.data.gridSettings; 71 this.gridSettings = this.data.gridSettings;
72 72
73 if (this.settings) { 73 if (this.settings) {
  74 + const showTitle = isUndefined(this.settings.showTitle) ? true : this.settings.showTitle;
  75 + const showDashboardLogo = isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo;
  76 + const hideToolbar = isUndefined(this.settings.hideToolbar) ? false : this.settings.hideToolbar;
74 this.settingsFormGroup = this.fb.group({ 77 this.settingsFormGroup = this.fb.group({
75 stateControllerId: [isUndefined(this.settings.stateControllerId) ? 'entity' : this.settings.stateControllerId, []], 78 stateControllerId: [isUndefined(this.settings.stateControllerId) ? 'entity' : this.settings.stateControllerId, []],
76 - toolbarAlwaysOpen: [isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen, []],  
77 - showTitle: [isUndefined(this.settings.showTitle) ? true : this.settings.showTitle, []],  
78 - titleColor: [isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor, []],  
79 - showDashboardsSelect: [isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect, []],  
80 - showEntitiesSelect: [isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect, []],  
81 - showFilters: [isUndefined(this.settings.showFilters) ? true : this.settings.showFilters, []],  
82 - showDashboardLogo: [isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo, []],  
83 - dashboardLogoUrl: [isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl, []],  
84 - showDashboardTimewindow: [isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow, []],  
85 - showDashboardExport: [isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport, []],  
86 - showUpdateDashboardImage: [isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage, []] 79 + showTitle: [showTitle, []],
  80 + titleColor: [{value: isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor,
  81 + disabled: !showTitle}, []],
  82 + showDashboardLogo: [showDashboardLogo, []],
  83 + dashboardLogoUrl: [{value: isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl,
  84 + disabled: !showDashboardLogo}, []],
  85 + hideToolbar: [hideToolbar, []],
  86 + toolbarAlwaysOpen: [{value: isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen,
  87 + disabled: hideToolbar}, []],
  88 + showDashboardsSelect: [{value: isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect,
  89 + disabled: hideToolbar}, []],
  90 + showEntitiesSelect: [{value: isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect,
  91 + disabled: hideToolbar}, []],
  92 + showFilters: [{value: isUndefined(this.settings.showFilters) ? true : this.settings.showFilters,
  93 + disabled: hideToolbar}, []],
  94 + showDashboardTimewindow: [{value: isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow,
  95 + disabled: hideToolbar}, []],
  96 + showDashboardExport: [{value: isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport,
  97 + disabled: hideToolbar}, []],
  98 + showUpdateDashboardImage: [
  99 + {value: isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage,
  100 + disabled: hideToolbar}, []]
87 }); 101 });
88 this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe( 102 this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe(
89 (stateControllerId: StateControllerId) => { 103 (stateControllerId: StateControllerId) => {
@@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS @@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
92 } 106 }
93 } 107 }
94 ); 108 );
  109 + this.settingsFormGroup.get('showTitle').valueChanges.subscribe(
  110 + (showTitleValue: boolean) => {
  111 + if (showTitleValue) {
  112 + this.settingsFormGroup.get('titleColor').enable();
  113 + } else {
  114 + this.settingsFormGroup.get('titleColor').disable();
  115 + }
  116 + }
  117 + );
  118 + this.settingsFormGroup.get('showDashboardLogo').valueChanges.subscribe(
  119 + (showDashboardLogoValue: boolean) => {
  120 + if (showDashboardLogoValue) {
  121 + this.settingsFormGroup.get('dashboardLogoUrl').enable();
  122 + } else {
  123 + this.settingsFormGroup.get('dashboardLogoUrl').disable();
  124 + }
  125 + }
  126 + );
  127 + this.settingsFormGroup.get('hideToolbar').valueChanges.subscribe(
  128 + (hideToolbarValue: boolean) => {
  129 + if (hideToolbarValue) {
  130 + this.settingsFormGroup.get('toolbarAlwaysOpen').disable();
  131 + this.settingsFormGroup.get('showDashboardsSelect').disable();
  132 + this.settingsFormGroup.get('showEntitiesSelect').disable();
  133 + this.settingsFormGroup.get('showFilters').disable();
  134 + this.settingsFormGroup.get('showDashboardTimewindow').disable();
  135 + this.settingsFormGroup.get('showDashboardExport').disable();
  136 + this.settingsFormGroup.get('showUpdateDashboardImage').disable();
  137 + } else {
  138 + this.settingsFormGroup.get('toolbarAlwaysOpen').enable();
  139 + this.settingsFormGroup.get('showDashboardsSelect').enable();
  140 + this.settingsFormGroup.get('showEntitiesSelect').enable();
  141 + this.settingsFormGroup.get('showFilters').enable();
  142 + this.settingsFormGroup.get('showDashboardTimewindow').enable();
  143 + this.settingsFormGroup.get('showDashboardExport').enable();
  144 + this.settingsFormGroup.get('showUpdateDashboardImage').enable();
  145 + }
  146 + }
  147 + );
95 } else { 148 } else {
96 this.settingsFormGroup = this.fb.group({}); 149 this.settingsFormGroup = this.fb.group({});
97 } 150 }
98 151
99 if (this.gridSettings) { 152 if (this.gridSettings) {
  153 + const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight;
100 this.gridSettingsFormGroup = this.fb.group({ 154 this.gridSettingsFormGroup = this.fb.group({
101 - color: [this.gridSettings.color || 'rgba(0,0,0,0.870588)', []],  
102 columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], 155 columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]],
103 margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, 156 margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10,
104 [Validators.required, Validators.min(0), Validators.max(50)]], 157 [Validators.required, Validators.min(0), Validators.max(50)]],
@@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS @@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
106 backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []], 159 backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []],
107 backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []], 160 backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []],
108 backgroundSizeMode: [this.gridSettings.backgroundSizeMode || '100%', []], 161 backgroundSizeMode: [this.gridSettings.backgroundSizeMode || '100%', []],
109 - mobileAutoFillHeight: [isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight, []],  
110 - mobileRowHeight: [isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight,  
111 - [Validators.required, Validators.min(5), Validators.max(200)]] 162 + mobileAutoFillHeight: [mobileAutoFillHeight, []],
  163 + mobileRowHeight: [{ value: isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight,
  164 + disabled: mobileAutoFillHeight}, [Validators.required, Validators.min(5), Validators.max(200)]]
112 }); 165 });
  166 + this.gridSettingsFormGroup.get('mobileAutoFillHeight').valueChanges.subscribe(
  167 + (mobileAutoFillHeightValue: boolean) => {
  168 + if (mobileAutoFillHeightValue) {
  169 + this.gridSettingsFormGroup.get('mobileRowHeight').disable();
  170 + } else {
  171 + this.gridSettingsFormGroup.get('mobileRowHeight').enable();
  172 + }
  173 + }
  174 + );
113 } else { 175 } else {
114 this.gridSettingsFormGroup = this.fb.group({}); 176 this.gridSettingsFormGroup = this.fb.group({});
115 } 177 }
@@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS @@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
133 let settings: DashboardSettings = null; 195 let settings: DashboardSettings = null;
134 let gridSettings: GridSettings = null; 196 let gridSettings: GridSettings = null;
135 if (this.settings) { 197 if (this.settings) {
136 - settings = {...this.settings, ...this.settingsFormGroup.value}; 198 + settings = {...this.settings, ...this.settingsFormGroup.getRawValue()};
137 } 199 }
138 if (this.gridSettings) { 200 if (this.gridSettings) {
139 - gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.value}; 201 + gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.getRawValue()};
140 } 202 }
141 this.dialogRef.close({settings, gridSettings}); 203 this.dialogRef.close({settings, gridSettings});
142 } 204 }
@@ -26,7 +26,6 @@ @@ -26,7 +26,6 @@
26 [style.backgroundImage]="backgroundImage" 26 [style.backgroundImage]="backgroundImage"
27 [ngStyle]="dashboardStyle"> 27 [ngStyle]="dashboardStyle">
28 <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center" 28 <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center"
29 - [ngStyle]="{'color': layoutCtx.gridSettings.color}"  
30 style="display: flex; z-index: 1; pointer-events: none;" 29 style="display: flex; z-index: 1; pointer-events: none;"
31 class="mat-headline tb-absolute-fill"> 30 class="mat-headline tb-absolute-fill">
32 <span *ngIf="!isEdit"> 31 <span *ngIf="!isEdit">
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,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);
@@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
86 lifetime: [null, Validators.required], 89 lifetime: [null, Validators.required],
87 defaultMinPeriod: [null, Validators.required], 90 defaultMinPeriod: [null, Validators.required],
88 notifIfDisabled: [true, []], 91 notifIfDisabled: [true, []],
89 - binding:[], 92 + binding: [],
90 bootstrapServer: [null, Validators.required], 93 bootstrapServer: [null, Validators.required],
91 lwm2mServer: [null, Validators.required], 94 lwm2mServer: [null, Validators.required],
92 clientStrategy: [1, []], 95 clientStrategy: [1, []],
@@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
96 this.lwm2mDeviceConfigFormGroup = this.fb.group({ 99 this.lwm2mDeviceConfigFormGroup = this.fb.group({
97 configurationJson: [null, Validators.required] 100 configurationJson: [null, Validators.required]
98 }); 101 });
99 - this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { 102 + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe(
  103 + takeUntil(this.destroy$)
  104 + ).subscribe((value) => {
100 this.updateDeviceProfileValue(value); 105 this.updateDeviceProfileValue(value);
101 }); 106 });
102 - this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { 107 + this.lwm2mDeviceConfigFormGroup.valueChanges.pipe(
  108 + takeUntil(this.destroy$)
  109 + ).subscribe(() => {
103 this.updateModel(); 110 this.updateModel();
104 }); 111 });
105 this.sortFunction = this.sortObjectKeyPathJson; 112 this.sortFunction = this.sortObjectKeyPathJson;
@@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
112 registerOnTouched(fn: any): void { 119 registerOnTouched(fn: any): void {
113 } 120 }
114 121
  122 + ngOnDestroy() {
  123 + this.destroy$.next();
  124 + this.destroy$.complete();
  125 + }
  126 +
115 setDisabledState(isDisabled: boolean): void { 127 setDisabledState(isDisabled: boolean): void {
116 this.disabled = isDisabled; 128 this.disabled = isDisabled;
117 if (isDisabled) { 129 if (isDisabled) {
@@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
124 } 136 }
125 137
126 writeValue(value: Lwm2mProfileConfigModels | null): void { 138 writeValue(value: Lwm2mProfileConfigModels | null): void {
127 - this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value;  
128 - this.lwm2mDeviceConfigFormGroup.patchValue({  
129 - configurationJson: this.configurationValue  
130 - }, {emitEvent: false});  
131 - this.initWriteValue(); 139 + if (isDefinedAndNotNull(value)) {
  140 + if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) {
  141 + this.configurationValue = value;
  142 + } else {
  143 + this.configurationValue = getDefaultProfileConfig();
  144 + }
  145 + this.lwm2mDeviceConfigFormGroup.patchValue({
  146 + configurationJson: this.configurationValue
  147 + }, {emitEvent: false});
  148 + this.initWriteValue();
  149 + }
132 } 150 }
133 151
134 private initWriteValue = (): void => { 152 private initWriteValue = (): void => {
@@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
257 instanceUpdate.id = instanceId; 275 instanceUpdate.id = instanceId;
258 instanceUpdate.resources.forEach(resource => { 276 instanceUpdate.resources.forEach(resource => {
259 resource.keyName = _.camelCase(resource.name + instanceUpdate.id); 277 resource.keyName = _.camelCase(resource.name + instanceUpdate.id);
260 - }) 278 + });
261 return instanceUpdate; 279 return instanceUpdate;
262 } 280 }
263 281
@@ -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
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <mat-icon *ngIf="icon">{{icon}}</mat-icon> 20 <mat-icon *ngIf="icon">{{icon}}</mat-icon>
21 <span *ngIf="label">{{label}}</span> 21 <span *ngIf="label">{{label}}</span>
22 </mat-label> 22 </mat-label>
23 - <div matPrefix class="tb-color-preview" (click)="showColorPicker()" style="margin-right: 5px;"> 23 + <div matPrefix class="tb-color-preview" (click)="!disabled && showColorPicker()" style="margin-right: 5px;">
24 <div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div> 24 <div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div>
25 </div> 25 </div>
26 <input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required"> 26 <input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required">
@@ -45,7 +45,6 @@ export interface WidgetLayouts { @@ -45,7 +45,6 @@ export interface WidgetLayouts {
45 45
46 export interface GridSettings { 46 export interface GridSettings {
47 backgroundColor?: string; 47 backgroundColor?: string;
48 - color?: string;  
49 columns?: number; 48 columns?: number;
50 margin?: number; 49 margin?: number;
51 backgroundSizeMode?: string; 50 backgroundSizeMode?: string;
@@ -93,6 +92,7 @@ export interface DashboardSettings { @@ -93,6 +92,7 @@ export interface DashboardSettings {
93 showDashboardExport?: boolean; 92 showDashboardExport?: boolean;
94 showUpdateDashboardImage?: boolean; 93 showUpdateDashboardImage?: boolean;
95 toolbarAlwaysOpen?: boolean; 94 toolbarAlwaysOpen?: boolean;
  95 + hideToolbar?: boolean;
96 titleColor?: string; 96 titleColor?: string;
97 } 97 }
98 98
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage'; 17 +export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage' | 'toggleDashboardLayout';
18 18
19 export interface WindowMessage { 19 export interface WindowMessage {
20 type: WindowMessageType; 20 type: WindowMessageType;
@@ -721,6 +721,7 @@ @@ -721,6 +721,7 @@
721 "maximum-upload-file-size": "Maximum upload file size: {{ size }}", 721 "maximum-upload-file-size": "Maximum upload file size: {{ size }}",
722 "cannot-upload-file": "Cannot upload file", 722 "cannot-upload-file": "Cannot upload file",
723 "settings": "Settings", 723 "settings": "Settings",
  724 + "layout-settings": "Layout settings",
724 "columns-count": "Columns count", 725 "columns-count": "Columns count",
725 "columns-count-required": "Columns count is required.", 726 "columns-count-required": "Columns count is required.",
726 "min-columns-count-message": "Only 10 minimum column count is allowed.", 727 "min-columns-count-message": "Only 10 minimum column count is allowed.",
@@ -743,15 +744,19 @@ @@ -743,15 +744,19 @@
743 "mobile-row-height-required": "Mobile row height value is required.", 744 "mobile-row-height-required": "Mobile row height value is required.",
744 "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", 745 "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.",
745 "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", 746 "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.",
  747 + "title-settings": "Title settings",
746 "display-title": "Display dashboard title", 748 "display-title": "Display dashboard title",
747 - "toolbar-always-open": "Keep toolbar opened",  
748 "title-color": "Title color", 749 "title-color": "Title color",
  750 + "toolbar-settings": "Toolbar settings",
  751 + "hide-toolbar": "Hide toolbar",
  752 + "toolbar-always-open": "Keep toolbar opened",
749 "display-dashboards-selection": "Display dashboards selection", 753 "display-dashboards-selection": "Display dashboards selection",
750 "display-entities-selection": "Display entities selection", 754 "display-entities-selection": "Display entities selection",
751 "display-filters": "Display filters", 755 "display-filters": "Display filters",
752 "display-dashboard-timewindow": "Display timewindow", 756 "display-dashboard-timewindow": "Display timewindow",
753 "display-dashboard-export": "Display export", 757 "display-dashboard-export": "Display export",
754 "display-update-dashboard-image": "Display update dashboard image", 758 "display-update-dashboard-image": "Display update dashboard image",
  759 + "dashboard-logo-settings": "Dashboard logo settings",
755 "display-dashboard-logo": "Display logo in dashboard fullscreen mode", 760 "display-dashboard-logo": "Display logo in dashboard fullscreen mode",
756 "dashboard-logo-image": "Dashboard logo image", 761 "dashboard-logo-image": "Dashboard logo image",
757 "import": "Import dashboard", 762 "import": "Import dashboard",