Commit 87874c3eb66c0eada6c432edd10381ded4cbaeec
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 | |
17 | 17 | import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; |
18 | 18 | import { |
19 | - AggregationType, calculateIntervalComparisonEndTime, | |
20 | - calculateIntervalEndTime, calculateIntervalStartEndTime, | |
19 | + AggregationType, | |
20 | + calculateIntervalComparisonEndTime, | |
21 | + calculateIntervalEndTime, | |
22 | + calculateIntervalStartEndTime, | |
21 | 23 | getCurrentTime, |
22 | - getCurrentTimeForComparison, getTime, | |
24 | + getTime, | |
23 | 25 | SubscriptionTimewindow |
24 | 26 | } from '@shared/models/time/time.models'; |
25 | 27 | import { UtilsService } from '@core/services/utils.service'; |
26 | -import { deepClone } from '@core/utils'; | |
28 | +import { deepClone, isNumeric } from '@core/utils'; | |
27 | 29 | import Timeout = NodeJS.Timeout; |
28 | -import * as moment_ from 'moment'; | |
29 | 30 | |
30 | 31 | export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void; |
31 | 32 | |
... | ... | @@ -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 | 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 | 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 | 38 | } from '@shared/models/telemetry/telemetry.models'; |
39 | 39 | import { UtilsService } from '@core/services/utils.service'; |
40 | 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 | 42 | import { PageData } from '@shared/models/page/page-data'; |
43 | 43 | import { DataAggregator } from '@core/api/data-aggregator'; |
44 | 44 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
... | ... | @@ -667,8 +667,7 @@ export class EntityDataSubscription { |
667 | 667 | if (prevDataCb) { |
668 | 668 | dataAggregator.updateOnDataCb(prevDataCb); |
669 | 669 | } |
670 | - } | |
671 | - if (!this.history && !isUpdate) { | |
670 | + } else if (!this.history && !isUpdate) { | |
672 | 671 | this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); |
673 | 672 | } |
674 | 673 | } |
... | ... | @@ -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 | 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 | 747 | return Number(val); |
753 | - } else { | |
754 | - return val; | |
755 | 748 | } |
749 | + return val; | |
756 | 750 | } |
757 | 751 | |
758 | 752 | private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData { | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service'; |
27 | 27 | |
28 | 28 | const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; |
29 | 29 | const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; |
30 | +const dashboardLayoutHandler = 'tbMobileDashboardLayoutHandler'; | |
30 | 31 | const navigationHandler = 'tbMobileNavigationHandler'; |
31 | 32 | const mobileHandler = 'tbMobileHandler'; |
32 | 33 | |
... | ... | @@ -43,6 +44,7 @@ export class MobileService { |
43 | 44 | |
44 | 45 | private reloadUserObservable: Observable<boolean>; |
45 | 46 | private lastDashboardId: string; |
47 | + private toggleLayoutFunction: () => void; | |
46 | 48 | |
47 | 49 | constructor(@Inject(WINDOW) private window: Window, |
48 | 50 | private router: Router, |
... | ... | @@ -65,12 +67,26 @@ export class MobileService { |
65 | 67 | } |
66 | 68 | } |
67 | 69 | |
68 | - public onDashboardLoaded() { | |
70 | + public onDashboardLoaded(hasRightLayout: boolean, rightLayoutOpened: boolean) { | |
69 | 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 | 90 | public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): |
75 | 91 | Observable<WidgetMobileActionResult<T>> { |
76 | 92 | if (this.mobileApp) { |
... | ... | @@ -110,6 +126,11 @@ export class MobileService { |
110 | 126 | const reloadUserMessage: ReloadUserMessage = message.data; |
111 | 127 | this.reloadUser(reloadUserMessage); |
112 | 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 | 149 | @Input() |
150 | 150 | currentState: string; |
151 | 151 | |
152 | + private hideToolbarValue = false; | |
153 | + | |
152 | 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 | 163 | @Input() |
156 | 164 | syncStateWithQueryParam = true; |
... | ... | @@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
269 | 277 | return !this.widgetEditMode && !this.hideToolbar && |
270 | 278 | (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch()); |
271 | 279 | } |
280 | + | |
272 | 281 | set toolbarOpened(toolbarOpened: boolean) { |
273 | 282 | } |
274 | 283 | |
... | ... | @@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
329 | 338 | this.dashboardCtx.aliasController.updateAliases(); |
330 | 339 | setTimeout(() => { |
331 | 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 | 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 | 362 | private init(data: any) { |
... | ... | @@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
430 | 447 | } |
431 | 448 | |
432 | 449 | ngOnDestroy(): void { |
450 | + if (this.isMobileApp) { | |
451 | + this.mobileService.unregisterToggleLayoutFunction(); | |
452 | + } | |
433 | 453 | this.rxSubscriptions.forEach((subscription) => { |
434 | 454 | subscription.unsubscribe(); |
435 | 455 | }); |
... | ... | @@ -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 | 501 | public displayTitle(): boolean { |
473 | 502 | if (this.dashboard.configuration.settings && |
474 | 503 | isDefined(this.dashboard.configuration.settings.showTitle)) { |
... | ... | @@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
546 | 575 | } |
547 | 576 | |
548 | 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 | 581 | public toggleLayouts() { |
553 | 582 | this.isRightLayoutOpened = !this.isRightLayoutOpened; |
583 | + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); | |
554 | 584 | } |
555 | 585 | |
556 | 586 | public openRightLayout() { |
557 | 587 | this.isRightLayoutOpened = true; |
588 | + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); | |
558 | 589 | } |
559 | 590 | |
560 | 591 | public mainLayoutWidth(): string { |
... | ... | @@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC |
782 | 813 | this.updateLayouts(layoutsData); |
783 | 814 | } |
784 | 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 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<form (ngSubmit)="save()"> | |
18 | +<form (ngSubmit)="save()" style="width: 750px;"> | |
19 | 19 | <mat-toolbar color="primary"> |
20 | 20 | <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2> |
21 | 21 | <span fxFlex></span> |
... | ... | @@ -28,8 +28,8 @@ |
28 | 28 | <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
29 | 29 | </mat-progress-bar> |
30 | 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 | 33 | <mat-form-field class="mat-block"> |
34 | 34 | <mat-label translate>dashboard.state-controller</mat-label> |
35 | 35 | <mat-select required formControlName="stateControllerId"> |
... | ... | @@ -38,112 +38,116 @@ |
38 | 38 | </mat-option> |
39 | 39 | </mat-select> |
40 | 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 | 44 | {{ 'dashboard.display-title' | translate }} |
47 | - </mat-checkbox> | |
45 | + </mat-slide-toggle> | |
48 | 46 | <tb-color-input fxFlex |
49 | 47 | label="{{'dashboard.title-color' | translate}}" |
50 | 48 | icon="format_color_fill" |
51 | 49 | openOnInput |
52 | 50 | formControlName="titleColor"> |
53 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 149 | {{ 'dashboard.autofill-height' | translate }} |
146 | - </mat-checkbox> | |
150 | + </mat-slide-toggle> | |
147 | 151 | <mat-form-field fxFlex class="mat-block"> |
148 | 152 | <mat-label translate>dashboard.mobile-row-height</mat-label> |
149 | 153 | <input matInput formControlName="mobileRowHeight" type="number" step="any" min="5" |
... | ... | @@ -158,7 +162,7 @@ |
158 | 162 | {{ 'dashboard.max-mobile-row-height-message' | translate }} |
159 | 163 | </mat-error> |
160 | 164 | </mat-form-field> |
161 | - </div> | |
165 | + </fieldset> | |
162 | 166 | </div> |
163 | 167 | </fieldset> |
164 | 168 | </div> | ... | ... |
... | ... | @@ -14,13 +14,19 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 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 | 28 | :host ::ng-deep { |
23 | - .mat-checkbox-label { | |
29 | + .mat-slide-toggle-content { | |
24 | 30 | white-space: normal; |
25 | 31 | } |
26 | 32 | } | ... | ... |
... | ... | @@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS |
71 | 71 | this.gridSettings = this.data.gridSettings; |
72 | 72 | |
73 | 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 | 77 | this.settingsFormGroup = this.fb.group({ |
75 | 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 | 102 | this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe( |
89 | 103 | (stateControllerId: StateControllerId) => { |
... | ... | @@ -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 | 148 | } else { |
96 | 149 | this.settingsFormGroup = this.fb.group({}); |
97 | 150 | } |
98 | 151 | |
99 | 152 | if (this.gridSettings) { |
153 | + const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight; | |
100 | 154 | this.gridSettingsFormGroup = this.fb.group({ |
101 | - color: [this.gridSettings.color || 'rgba(0,0,0,0.870588)', []], | |
102 | 155 | columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], |
103 | 156 | margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, |
104 | 157 | [Validators.required, Validators.min(0), Validators.max(50)]], |
... | ... | @@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS |
106 | 159 | backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []], |
107 | 160 | backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []], |
108 | 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 | 175 | } else { |
114 | 176 | this.gridSettingsFormGroup = this.fb.group({}); |
115 | 177 | } |
... | ... | @@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS |
133 | 195 | let settings: DashboardSettings = null; |
134 | 196 | let gridSettings: GridSettings = null; |
135 | 197 | if (this.settings) { |
136 | - settings = {...this.settings, ...this.settingsFormGroup.value}; | |
198 | + settings = {...this.settings, ...this.settingsFormGroup.getRawValue()}; | |
137 | 199 | } |
138 | 200 | if (this.gridSettings) { |
139 | - gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.value}; | |
201 | + gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.getRawValue()}; | |
140 | 202 | } |
141 | 203 | this.dialogRef.close({settings, gridSettings}); |
142 | 204 | } | ... | ... |
... | ... | @@ -26,7 +26,6 @@ |
26 | 26 | [style.backgroundImage]="backgroundImage" |
27 | 27 | [ngStyle]="dashboardStyle"> |
28 | 28 | <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center" |
29 | - [ngStyle]="{'color': layoutCtx.gridSettings.color}" | |
30 | 29 | style="display: flex; z-index: 1; pointer-events: none;" |
31 | 30 | class="mat-headline tb-absolute-fill"> |
32 | 31 | <span *ngIf="!isEdit"> | ... | ... |
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 | 41 | @Component({ |
42 | 42 | selector: 'tb-security-config-lwm2m-server', |
43 | 43 | templateUrl: './security-config-lwm2m-server.component.html', |
44 | - styleUrls: ['./security-config-lwm2m-server.component.scss'], | |
44 | + styleUrls: [], | |
45 | 45 | providers: [ |
46 | 46 | { |
47 | 47 | provide: NG_VALUE_ACCESSOR, | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 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 | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
19 | 19 | import { Store } from '@ngrx/store'; |
20 | 20 | import { AppState } from '@app/core/core.state'; |
... | ... | @@ -33,6 +33,8 @@ import { |
33 | 33 | transportPayloadTypeTranslationMap, |
34 | 34 | } from '@shared/models/device.models'; |
35 | 35 | import { isDefinedAndNotNull } from '@core/utils'; |
36 | +import { Subject } from 'rxjs'; | |
37 | +import { takeUntil } from 'rxjs/operators'; | |
36 | 38 | |
37 | 39 | @Component({ |
38 | 40 | selector: 'tb-coap-device-profile-transport-configuration', |
... | ... | @@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils'; |
44 | 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 | 51 | coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); |
50 | 52 | |
... | ... | @@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control |
56 | 58 | |
57 | 59 | coapDeviceProfileTransportConfigurationFormGroup: FormGroup; |
58 | 60 | |
61 | + private destroy$ = new Subject(); | |
59 | 62 | private requiredValue: boolean; |
60 | 63 | |
61 | 64 | private transportPayloadTypeConfiguration = this.fb.group({ |
... | ... | @@ -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 | 108 | this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); |
105 | 109 | }); |
106 | - this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | |
110 | + this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( | |
111 | + takeUntil(this.destroy$) | |
112 | + ).subscribe(() => { | |
107 | 113 | this.updateModel(); |
108 | 114 | }); |
109 | 115 | } |
110 | 116 | |
117 | + ngOnDestroy() { | |
118 | + this.destroy$.next(); | |
119 | + this.destroy$.complete(); | |
120 | + } | |
121 | + | |
111 | 122 | get coapDeviceTypeDefault(): boolean { |
112 | 123 | const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; |
113 | 124 | return coapDeviceType === CoapTransportDeviceType.DEFAULT; | ... | ... |
... | ... | @@ -14,13 +14,15 @@ |
14 | 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 | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
19 | 19 | import { Store } from '@ngrx/store'; |
20 | 20 | import { AppState } from '@app/core/core.state'; |
21 | 21 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
22 | 22 | import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; |
23 | 23 | import { deepClone } from '@core/utils'; |
24 | +import { Subject } from 'rxjs'; | |
25 | +import { takeUntil } from 'rxjs/operators'; | |
24 | 26 | |
25 | 27 | @Component({ |
26 | 28 | selector: 'tb-device-profile-configuration', |
... | ... | @@ -32,12 +34,14 @@ import { deepClone } from '@core/utils'; |
32 | 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 | 39 | deviceProfileType = DeviceProfileType; |
38 | 40 | |
39 | 41 | deviceProfileConfigurationFormGroup: FormGroup; |
40 | 42 | |
43 | + private destroy$ = new Subject(); | |
44 | + | |
41 | 45 | private requiredValue: boolean; |
42 | 46 | get required(): boolean { |
43 | 47 | return this.requiredValue; |
... | ... | @@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor |
69 | 73 | this.deviceProfileConfigurationFormGroup = this.fb.group({ |
70 | 74 | configuration: [null, Validators.required] |
71 | 75 | }); |
72 | - this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { | |
76 | + this.deviceProfileConfigurationFormGroup.valueChanges.pipe( | |
77 | + takeUntil(this.destroy$) | |
78 | + ).subscribe(() => { | |
73 | 79 | this.updateModel(); |
74 | 80 | }); |
75 | 81 | } |
76 | 82 | |
83 | + ngOnDestroy() { | |
84 | + this.destroy$.next(); | |
85 | + this.destroy$.complete(); | |
86 | + } | |
87 | + | |
77 | 88 | setDisabledState(isDisabled: boolean): void { |
78 | 89 | this.disabled = isDisabled; |
79 | 90 | if (this.disabled) { | ... | ... |
... | ... | @@ -15,112 +15,107 @@ |
15 | 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 | 120 | </div> |
126 | 121 | </section> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 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 | 19 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
20 | 20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
21 | 21 | import { |
... | ... | @@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util |
39 | 39 | import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; |
40 | 40 | import { Direction } from '@shared/models/page/sort-order'; |
41 | 41 | import _ from 'lodash'; |
42 | +import { Subject } from 'rxjs'; | |
43 | +import { takeUntil } from 'rxjs/operators'; | |
42 | 44 | |
43 | 45 | @Component({ |
44 | 46 | selector: 'tb-profile-lwm2m-device-transport-configuration', |
... | ... | @@ -49,11 +51,12 @@ import _ from 'lodash'; |
49 | 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 | 56 | private configurationValue: Lwm2mProfileConfigModels; |
55 | 57 | private requiredValue: boolean; |
56 | 58 | private disabled = false; |
59 | + private destroy$ = new Subject(); | |
57 | 60 | |
58 | 61 | bindingModeType = BINDING_MODE; |
59 | 62 | bindingModeTypes = Object.keys(BINDING_MODE); |
... | ... | @@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
86 | 89 | lifetime: [null, Validators.required], |
87 | 90 | defaultMinPeriod: [null, Validators.required], |
88 | 91 | notifIfDisabled: [true, []], |
89 | - binding:[], | |
92 | + binding: [], | |
90 | 93 | bootstrapServer: [null, Validators.required], |
91 | 94 | lwm2mServer: [null, Validators.required], |
92 | 95 | clientStrategy: [1, []], |
... | ... | @@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
96 | 99 | this.lwm2mDeviceConfigFormGroup = this.fb.group({ |
97 | 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 | 105 | this.updateDeviceProfileValue(value); |
101 | 106 | }); |
102 | - this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { | |
107 | + this.lwm2mDeviceConfigFormGroup.valueChanges.pipe( | |
108 | + takeUntil(this.destroy$) | |
109 | + ).subscribe(() => { | |
103 | 110 | this.updateModel(); |
104 | 111 | }); |
105 | 112 | this.sortFunction = this.sortObjectKeyPathJson; |
... | ... | @@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
112 | 119 | registerOnTouched(fn: any): void { |
113 | 120 | } |
114 | 121 | |
122 | + ngOnDestroy() { | |
123 | + this.destroy$.next(); | |
124 | + this.destroy$.complete(); | |
125 | + } | |
126 | + | |
115 | 127 | setDisabledState(isDisabled: boolean): void { |
116 | 128 | this.disabled = isDisabled; |
117 | 129 | if (isDisabled) { |
... | ... | @@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
124 | 136 | } |
125 | 137 | |
126 | 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 | 152 | private initWriteValue = (): void => { |
... | ... | @@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
257 | 275 | instanceUpdate.id = instanceId; |
258 | 276 | instanceUpdate.resources.forEach(resource => { |
259 | 277 | resource.keyName = _.camelCase(resource.name + instanceUpdate.id); |
260 | - }) | |
278 | + }); | |
261 | 279 | return instanceUpdate; |
262 | 280 | } |
263 | 281 | ... | ... |
... | ... | @@ -15,10 +15,19 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 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 | 28 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
20 | 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 | 31 | import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; |
23 | 32 | import { DeviceProfileService } from '@core/http/device-profile.service'; |
24 | 33 | import { Direction } from '@shared/models/page/sort-order'; |
... | ... | @@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link'; |
33 | 42 | provide: NG_VALUE_ACCESSOR, |
34 | 43 | useExisting: forwardRef(() => Lwm2mObjectListComponent), |
35 | 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 | 55 | private requiredValue: boolean; |
41 | 56 | private dirty = false; |
42 | - private lw2mModels: Observable<Array<ObjectLwM2M>>; | |
43 | 57 | private modelValue: Array<string> = []; |
44 | 58 | |
45 | 59 | lwm2mListFormGroup: FormGroup; |
... | ... | @@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
78 | 92 | } |
79 | 93 | |
80 | 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 | 99 | registerOnChange(fn: any): void { |
... | ... | @@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
92 | 106 | ngOnInit() { |
93 | 107 | this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges |
94 | 108 | .pipe( |
109 | + distinctUntilChanged(), | |
95 | 110 | tap((value) => { |
96 | 111 | if (value && typeof value !== 'string') { |
97 | 112 | this.add(value); |
... | ... | @@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
100 | 115 | } |
101 | 116 | }), |
102 | 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 | 147 | } |
132 | 148 | } |
133 | 149 | |
150 | + validate(): ValidationErrors | null { | |
151 | + return this.lwm2mListFormGroup.valid ? null : { | |
152 | + lwm2mListObj: false | |
153 | + }; | |
154 | + } | |
155 | + | |
134 | 156 | private add(object: ObjectLwM2M): void { |
135 | 157 | if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { |
136 | 158 | this.modelValue.push(object.keyId); |
... | ... | @@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
157 | 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 | 183 | this.searchText = searchText; |
162 | - return this.getLwM2mModelsPage().pipe( | |
163 | - map(objectLwM2Ms => objectLwM2Ms) | |
164 | - ); | |
165 | - } | |
166 | - | |
167 | - private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> { | |
168 | 184 | const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { |
169 | 185 | property: 'id', |
170 | 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 | 191 | onFocus = (): void => { |
... | ... | @@ -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 | 199 | this.searchText = ''; |
189 | - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value); | |
200 | + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null); | |
190 | 201 | setTimeout(() => { |
191 | 202 | this.objectInput.nativeElement.blur(); |
192 | 203 | this.objectInput.nativeElement.focus(); | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<section [formGroup]="resourceFormGroup" class="mat-padding"> | |
18 | +<section [formGroup]="resourceFormGroup"> | |
19 | 19 | <div fxLayout="row" fxFill formArrayName="resources" |
20 | 20 | *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> |
21 | 21 | <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> |
... | ... | @@ -46,14 +46,14 @@ |
46 | 46 | </div> |
47 | 47 | <div fxFlex="10" fxLayoutAlign="center center"> |
48 | 48 | <mat-checkbox formControlName="attribute" color="warn" |
49 | - [checked]="updateObserve(i)" | |
49 | + (change)="updateObserve(i)" | |
50 | 50 | matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" |
51 | 51 | matTooltipPosition="above"> |
52 | 52 | </mat-checkbox> |
53 | 53 | </div> |
54 | 54 | <div fxFlex="10" fxLayoutAlign="center center"> |
55 | 55 | <mat-checkbox formControlName="telemetry" color="primary" |
56 | - [checked]="updateObserve(i)" | |
56 | + (change)="updateObserve(i)" | |
57 | 57 | matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" |
58 | 58 | matTooltipPosition="above"> |
59 | 59 | </mat-checkbox> | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 18 | <section [formGroup]="observeAttrTelemetryFormGroup"> |
19 | - <mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M"> | |
19 | + <mat-accordion multi="true" formArrayName="clientLwM2M"> | |
20 | 20 | <mat-expansion-panel |
21 | 21 | *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" |
22 | 22 | [formGroupName]="i"> | ... | ... |
... | ... | @@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>( |
64 | 64 | [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], |
65 | 65 | [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], |
66 | 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 | 68 | [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], |
69 | 69 | [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], |
70 | 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 | 162 | clientLwM2mSettings: ClientLwM2mSettings; |
163 | 163 | observeAttr: ObservableAttributes; |
164 | 164 | bootstrap: BootstrapSecurityConfig; |
165 | - | |
166 | 165 | } |
167 | 166 | |
168 | 167 | export interface ClientLwM2mSettings { | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 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 | 18 | import { |
19 | 19 | ControlValueAccessor, |
20 | 20 | FormBuilder, |
... | ... | @@ -39,6 +39,8 @@ import { |
39 | 39 | transportPayloadTypeTranslationMap |
40 | 40 | } from '@shared/models/device.models'; |
41 | 41 | import { isDefinedAndNotNull } from '@core/utils'; |
42 | +import { Subject } from 'rxjs'; | |
43 | +import { takeUntil } from 'rxjs/operators'; | |
42 | 44 | |
43 | 45 | @Component({ |
44 | 46 | selector: 'tb-mqtt-device-profile-transport-configuration', |
... | ... | @@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils'; |
50 | 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 | 57 | transportPayloadTypes = Object.keys(TransportPayloadType); |
56 | 58 | |
... | ... | @@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control |
58 | 60 | |
59 | 61 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; |
60 | 62 | |
63 | + private destroy$ = new Subject(); | |
61 | 64 | private requiredValue: boolean; |
62 | 65 | |
63 | 66 | get required(): boolean { |
... | ... | @@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control |
98 | 101 | }) |
99 | 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 | 107 | this.updateTransportPayloadBasedControls(payloadType, true); |
104 | 108 | }); |
105 | - this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | |
109 | + this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( | |
110 | + takeUntil(this.destroy$) | |
111 | + ).subscribe(() => { | |
106 | 112 | this.updateModel(); |
107 | 113 | }); |
108 | 114 | } |
109 | 115 | |
116 | + ngOnDestroy() { | |
117 | + this.destroy$.next(); | |
118 | + this.destroy$.complete(); | |
119 | + } | |
120 | + | |
110 | 121 | setDisabledState(isDisabled: boolean): void { |
111 | 122 | this.disabled = isDisabled; |
112 | 123 | if (this.disabled) { |
... | ... | @@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control |
192 | 203 | } |
193 | 204 | |
194 | 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 | 208 | if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { |
198 | 209 | return {unique: true}; |
199 | 210 | } | ... | ... |
... | ... | @@ -14,17 +14,19 @@ |
14 | 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 | 22 | import { |
23 | 23 | DeviceProfileTransportConfiguration, |
24 | 24 | DeviceTransportType, |
25 | 25 | SnmpDeviceProfileTransportConfiguration |
26 | 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 | 31 | export interface OidMappingConfiguration { |
30 | 32 | isAttribute: boolean; |
... | ... | @@ -44,8 +46,11 @@ export interface OidMappingConfiguration { |
44 | 46 | multi: true |
45 | 47 | }] |
46 | 48 | }) |
47 | -export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { | |
49 | +export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { | |
50 | + | |
48 | 51 | snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; |
52 | + | |
53 | + private destroy$ = new Subject(); | |
49 | 54 | private requiredValue: boolean; |
50 | 55 | private configuration = []; |
51 | 56 | |
... | ... | @@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control |
71 | 76 | this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ |
72 | 77 | configuration: [null, Validators.required] |
73 | 78 | }); |
74 | - this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | |
79 | + this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( | |
80 | + takeUntil(this.destroy$) | |
81 | + ).subscribe(() => { | |
75 | 82 | this.updateModel(); |
76 | 83 | }); |
77 | 84 | } |
78 | 85 | |
86 | + ngOnDestroy() { | |
87 | + this.destroy$.next(); | |
88 | + this.destroy$.complete(); | |
89 | + } | |
90 | + | |
79 | 91 | registerOnChange(fn: any): void { |
80 | 92 | this.propagateChange = fn; |
81 | 93 | } | ... | ... |
... | ... | @@ -32,7 +32,7 @@ import { |
32 | 32 | }) |
33 | 33 | export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { |
34 | 34 | |
35 | - deviceTransportTypes = Object.keys(DeviceTransportType); | |
35 | + deviceTransportTypes = Object.values(DeviceTransportType); | |
36 | 36 | |
37 | 37 | deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; |
38 | 38 | ... | ... |
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 | <mat-icon *ngIf="icon">{{icon}}</mat-icon> |
21 | 21 | <span *ngIf="label">{{label}}</span> |
22 | 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 | 24 | <div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div> |
25 | 25 | </div> |
26 | 26 | <input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required"> | ... | ... |
... | ... | @@ -45,7 +45,6 @@ export interface WidgetLayouts { |
45 | 45 | |
46 | 46 | export interface GridSettings { |
47 | 47 | backgroundColor?: string; |
48 | - color?: string; | |
49 | 48 | columns?: number; |
50 | 49 | margin?: number; |
51 | 50 | backgroundSizeMode?: string; |
... | ... | @@ -93,6 +92,7 @@ export interface DashboardSettings { |
93 | 92 | showDashboardExport?: boolean; |
94 | 93 | showUpdateDashboardImage?: boolean; |
95 | 94 | toolbarAlwaysOpen?: boolean; |
95 | + hideToolbar?: boolean; | |
96 | 96 | titleColor?: string; |
97 | 97 | } |
98 | 98 | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 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 | 19 | export interface WindowMessage { |
20 | 20 | type: WindowMessageType; | ... | ... |
... | ... | @@ -721,6 +721,7 @@ |
721 | 721 | "maximum-upload-file-size": "Maximum upload file size: {{ size }}", |
722 | 722 | "cannot-upload-file": "Cannot upload file", |
723 | 723 | "settings": "Settings", |
724 | + "layout-settings": "Layout settings", | |
724 | 725 | "columns-count": "Columns count", |
725 | 726 | "columns-count-required": "Columns count is required.", |
726 | 727 | "min-columns-count-message": "Only 10 minimum column count is allowed.", |
... | ... | @@ -743,15 +744,19 @@ |
743 | 744 | "mobile-row-height-required": "Mobile row height value is required.", |
744 | 745 | "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", |
745 | 746 | "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", |
747 | + "title-settings": "Title settings", | |
746 | 748 | "display-title": "Display dashboard title", |
747 | - "toolbar-always-open": "Keep toolbar opened", | |
748 | 749 | "title-color": "Title color", |
750 | + "toolbar-settings": "Toolbar settings", | |
751 | + "hide-toolbar": "Hide toolbar", | |
752 | + "toolbar-always-open": "Keep toolbar opened", | |
749 | 753 | "display-dashboards-selection": "Display dashboards selection", |
750 | 754 | "display-entities-selection": "Display entities selection", |
751 | 755 | "display-filters": "Display filters", |
752 | 756 | "display-dashboard-timewindow": "Display timewindow", |
753 | 757 | "display-dashboard-export": "Display export", |
754 | 758 | "display-update-dashboard-image": "Display update dashboard image", |
759 | + "dashboard-logo-settings": "Dashboard logo settings", | |
755 | 760 | "display-dashboard-logo": "Display logo in dashboard fullscreen mode", |
756 | 761 | "dashboard-logo-image": "Dashboard logo image", |
757 | 762 | "import": "Import dashboard", | ... | ... |