27 changed files with 1065 additions and 179 deletions
@ -0,0 +1,53 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<form [formGroup]="attributeLwm2mDialogFormGroup" (ngSubmit)="save()" style="width: 500px;"> |
||||
|
<mat-toolbar color="primary"> |
||||
|
<div fxFlex fxLayout="column" fxLayoutAlign="start"> |
||||
|
<h2>{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' : |
||||
|
'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}</h2> |
||||
|
</div> |
||||
|
<span fxFlex></span> |
||||
|
<button mat-icon-button |
||||
|
(click)="cancel()" |
||||
|
type="button"> |
||||
|
<mat-icon class="material-icons">close</mat-icon> |
||||
|
</button> |
||||
|
</mat-toolbar> |
||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
||||
|
</mat-progress-bar> |
||||
|
<div mat-dialog-content> |
||||
|
<tb-lwm2m-attributes-key-list |
||||
|
formControlName="keyFilters" |
||||
|
titleText="{{data.destName}}"> |
||||
|
</tb-lwm2m-attributes-key-list> |
||||
|
</div> |
||||
|
<div mat-dialog-actions fxLayoutAlign="end center"> |
||||
|
<button mat-button color="primary" |
||||
|
type="button" |
||||
|
[disabled]="(isLoading$ | async)" |
||||
|
(click)="cancel()" cdkFocusInitial> |
||||
|
{{ (readonly ? 'action.close' : 'action.cancel') | translate }} |
||||
|
</button> |
||||
|
<button mat-raised-button color="primary" |
||||
|
*ngIf="!readonly" |
||||
|
type="submit" |
||||
|
[disabled]="(isLoading$ | async) || attributeLwm2mDialogFormGroup.invalid || !attributeLwm2mDialogFormGroup.dirty"> |
||||
|
{{ 'action.save' | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,90 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import {Component, Inject, OnInit, SkipSelf} from "@angular/core"; |
||||
|
import {ErrorStateMatcher} from "@angular/material/core"; |
||||
|
import {DialogComponent} from "@shared/components/dialog.component"; |
||||
|
import {Store} from "@ngrx/store"; |
||||
|
import {AppState} from "@core/core.state"; |
||||
|
import {Router} from "@angular/router"; |
||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; |
||||
|
import {FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm} from "@angular/forms"; |
||||
|
import {TranslateService} from "@ngx-translate/core"; |
||||
|
import {JsonObject} from "@angular/compiler-cli/ngcc/src/packages/entry_point"; |
||||
|
|
||||
|
export interface Lwm2mAttributesDialogData { |
||||
|
readonly: boolean; |
||||
|
attributeLwm2m: JsonObject; |
||||
|
destName: string |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-lwm2m-attributes-dialog', |
||||
|
templateUrl: './lwm2m-attributes-dialog.component.html', |
||||
|
styleUrls: ['./lwm2m-attributes.component.scss'], |
||||
|
providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], |
||||
|
}) |
||||
|
export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, Object> implements OnInit, ErrorStateMatcher { |
||||
|
|
||||
|
readonly = this.data.readonly; |
||||
|
|
||||
|
attributeLwm2m = this.data.attributeLwm2m; |
||||
|
|
||||
|
submitted = false; |
||||
|
|
||||
|
dirtyValue = false; |
||||
|
|
||||
|
attributeLwm2mDialogFormGroup: FormGroup; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
protected router: Router, |
||||
|
@Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData, |
||||
|
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, |
||||
|
public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>, |
||||
|
private fb: FormBuilder, |
||||
|
public translate: TranslateService) { |
||||
|
super(store, router, dialogRef); |
||||
|
|
||||
|
this.attributeLwm2mDialogFormGroup = this.fb.group({ |
||||
|
keyFilters: [{}, []] |
||||
|
}); |
||||
|
this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m}); |
||||
|
this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => { |
||||
|
this.attributeLwm2m = attributes; |
||||
|
}); |
||||
|
if (this.readonly) { |
||||
|
this.attributeLwm2mDialogFormGroup.disable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
} |
||||
|
|
||||
|
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { |
||||
|
const originalErrorState = this.errorStateMatcher.isErrorState(control, form); |
||||
|
const customErrorState = !!(control && control.invalid && this.submitted); |
||||
|
return originalErrorState || customErrorState; |
||||
|
} |
||||
|
|
||||
|
save(): void { |
||||
|
this.submitted = true; |
||||
|
this.dialogRef.close(this.attributeLwm2m); |
||||
|
} |
||||
|
|
||||
|
cancel(): void { |
||||
|
this.dialogRef.close(null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<section fxLayout="column" class="tb-kv-map" [formGroup]="kvListFormGroup"> |
||||
|
<div> |
||||
|
<mat-label translate class="tb-title no-padding">device-profile.lwm2m.attribute-lwm2m-destination</mat-label> |
||||
|
<mat-label class="tb-editor-area-title-panel">{{ titleText }}</mat-label> |
||||
|
</div> |
||||
|
<div fxLayout="row" fxLayoutGap="8px" style="max-height: 40px; margin-top: 8px;"> |
||||
|
<mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-name</mat-label> |
||||
|
<mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-value</mat-label> |
||||
|
<div [fxShow]="!disabled" style="width: 40px;"></div> |
||||
|
</div> |
||||
|
<div fxLayout="column" formArrayName="keyVals" |
||||
|
*ngFor="let keyValControl of keyValsFormArray().controls; let $index = index"> |
||||
|
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
||||
|
<mat-form-field class="mat-block" style="max-heights: 400px"> |
||||
|
<mat-label></mat-label> |
||||
|
<mat-select [formControl]="keyValControl.get('key')"> |
||||
|
<mat-option *ngFor="let attributeLwm2m of attrKeys" |
||||
|
[value]="attributeLwm2m"> |
||||
|
{{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="mat-block" |
||||
|
style="max-height: 40px;"> |
||||
|
<mat-label></mat-label> |
||||
|
<input [formControl]="keyValControl.get('value')" matInput |
||||
|
placeholder="{{ ('key-val.value') | translate }}"/> |
||||
|
</mat-form-field> |
||||
|
<button mat-button mat-icon-button color="primary" |
||||
|
[fxShow]="!disabled" |
||||
|
type="button" |
||||
|
(click)="removeKeyVal($index)" |
||||
|
[disabled]="isLoading$ | async" |
||||
|
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-remove-tip' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon>close</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
|
<mat-error *ngIf="keyValControl.get('key').hasError('required')" style="font-size: smaller"> |
||||
|
{{ 'device-profile.lwm2m.key-name' | translate }} |
||||
|
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> |
||||
|
</mat-error> |
||||
|
<mat-error fxLayout="row" *ngIf="keyValControl.get('key').hasError('validAttributeKey')" |
||||
|
style="font-size: smaller"> |
||||
|
{{ 'device-profile.lwm2m.valid-attribute-lwm2m-key' | translate: {attrEnums: attrKeys} }} |
||||
|
</mat-error> |
||||
|
<mat-error fxLayout="row" *ngIf="keyValControl.get('value').hasError('validAttributeValue')" |
||||
|
style="font-size: smaller"> |
||||
|
{{ 'device-profile.lwm2m.valid-attribute-lwm2m-value' | translate: {attrEnums: attrKeys} }} |
||||
|
</mat-error> |
||||
|
</div> |
||||
|
<span [fxShow]="!keyValsFormArray().length" |
||||
|
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}" |
||||
|
class="no-data-found" translate>{{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}}</span> |
||||
|
<div style="margin-top: 8px;"> |
||||
|
<button mat-button mat-raised-button color="primary" |
||||
|
[fxShow]="!disabled" |
||||
|
[disabled]="isLoading$ | async" |
||||
|
(click)="addKeyVal()" |
||||
|
type="button" |
||||
|
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-add-tip' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
{{ 'action.add' | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,227 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import {Component, forwardRef, Input, OnInit} from "@angular/core"; |
||||
|
import { |
||||
|
AbstractControl, |
||||
|
ControlValueAccessor, |
||||
|
FormArray, |
||||
|
FormBuilder, |
||||
|
FormControl, |
||||
|
FormGroup, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
Validator, |
||||
|
Validators |
||||
|
} from "@angular/forms"; |
||||
|
import {Subscription} from "rxjs"; |
||||
|
import {PageComponent} from "@shared/components/page.component"; |
||||
|
import {Store} from "@ngrx/store"; |
||||
|
import {AppState} from "@core/core.state"; |
||||
|
import { |
||||
|
ATTRIBUTE_KEYS, |
||||
|
ATTRIBUTE_LWM2M_ENUM, |
||||
|
ATTRIBUTE_LWM2M_MAP |
||||
|
} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models"; |
||||
|
import {isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull} from "@core/utils"; |
||||
|
|
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-lwm2m-attributes-key-list', |
||||
|
templateUrl: './lwm2m-attributes-key-list.component.html', |
||||
|
styleUrls: ['./lwm2m-attributes.component.scss'], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent), |
||||
|
multi: true, |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { |
||||
|
|
||||
|
attrKeys = ATTRIBUTE_KEYS; |
||||
|
|
||||
|
attrKey = ATTRIBUTE_LWM2M_ENUM; |
||||
|
|
||||
|
attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP; |
||||
|
|
||||
|
@Input() disabled: boolean; |
||||
|
|
||||
|
@Input() titleText: string; |
||||
|
|
||||
|
@Input() noDataText: string; |
||||
|
|
||||
|
kvListFormGroup: FormGroup; |
||||
|
|
||||
|
private propagateChange = null; |
||||
|
|
||||
|
private valueChangeSubscription: Subscription = null; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: FormBuilder) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.kvListFormGroup = this.fb.group({}); |
||||
|
this.kvListFormGroup.addControl('keyVals', |
||||
|
this.fb.array([])); |
||||
|
} |
||||
|
|
||||
|
keyValsFormArray(): FormArray { |
||||
|
return this.kvListFormGroup.get('keyVals') as FormArray; |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState?(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (this.disabled) { |
||||
|
this.kvListFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.kvListFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(keyValMap: { [key: string]: string }): void { |
||||
|
if (this.valueChangeSubscription) { |
||||
|
this.valueChangeSubscription.unsubscribe(); |
||||
|
} |
||||
|
const keyValsControls: Array<AbstractControl> = []; |
||||
|
if (keyValMap) { |
||||
|
for (const property of Object.keys(keyValMap)) { |
||||
|
if (Object.prototype.hasOwnProperty.call(keyValMap, property)) { |
||||
|
keyValsControls.push(this.fb.group({ |
||||
|
key: [property, [Validators.required, this.attributeLwm2mKeyValidator]], |
||||
|
value: [keyValMap[property], this.attributeLwm2mValueValidator(property)] |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls)); |
||||
|
this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { |
||||
|
// this.updateValidate();
|
||||
|
this.updateModel(); |
||||
|
}); |
||||
|
if (this.disabled) { |
||||
|
this.kvListFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.kvListFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public removeKeyVal(index: number) { |
||||
|
(this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index); |
||||
|
} |
||||
|
|
||||
|
public addKeyVal() { |
||||
|
const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray; |
||||
|
keyValsFormArray.push(this.fb.group({ |
||||
|
key: ['', [Validators.required, this.attributeLwm2mKeyValidator]], |
||||
|
value: ['', []] |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
public validate(c?: FormControl) { |
||||
|
const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; |
||||
|
let valid = true; |
||||
|
for (const entry of kvList) { |
||||
|
if (isUndefinedOrNull(entry.key) || isEmptyStr(entry.key) || !ATTRIBUTE_KEYS.includes(entry.key)) { |
||||
|
valid = false; |
||||
|
break; |
||||
|
} |
||||
|
if (entry.key !== 'ver' && isNaN(Number(entry.value))) { |
||||
|
valid = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
return (valid) ? null : { |
||||
|
keyVals: { |
||||
|
valid: false, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private updateValidate (c?: FormControl) { |
||||
|
const kvList = this.kvListFormGroup.get('keyVals') as FormArray; |
||||
|
kvList.controls.forEach(fg => { |
||||
|
if (fg.get('key').value==='ver') { |
||||
|
fg.get('value').setValidators(null); |
||||
|
fg.get('value').setErrors(null); |
||||
|
} |
||||
|
else { |
||||
|
fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator); |
||||
|
fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value'))); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private updateModel() { |
||||
|
this.updateValidate(); |
||||
|
if (this.validate() === null) { |
||||
|
const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; |
||||
|
const keyValMap: { [key: string]: string | number } = {}; |
||||
|
kvList.forEach((entry) => { |
||||
|
if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) { |
||||
|
keyValMap[entry.key] = entry.value.toString(); |
||||
|
} else { |
||||
|
keyValMap[entry.key] = Number(entry.value) |
||||
|
} |
||||
|
}); |
||||
|
this.propagateChange(keyValMap); |
||||
|
} |
||||
|
else { |
||||
|
this.propagateChange(null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private attributeLwm2mKeyValidator = (control: AbstractControl) => { |
||||
|
const key = control.value as string; |
||||
|
if (isDefinedAndNotNull(key) && !isEmpty(key)) { |
||||
|
if (!ATTRIBUTE_KEYS.includes(key)) { |
||||
|
return { |
||||
|
validAttributeKey: true |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private attributeLwm2mValueNumberValidator = (control: AbstractControl) => { |
||||
|
if (isNaN(Number(control.value)) || Number(control.value) < 0) { |
||||
|
return { |
||||
|
'validAttributeValue': true |
||||
|
}; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private attributeLwm2mValueValidator = (property: string): Object [] => { |
||||
|
return property === 'ver'? [] : [this.attributeLwm2mValueNumberValidator]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2021 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div fxLayout="row" [formGroup]="attributeLwm2mFormGroup"> |
||||
|
<div fxFlex fxLayout="column" class="resource-name-lw-end" fxLayoutAlign="center" |
||||
|
[matTooltip]="isToolTipLabel()" matTooltipPosition="above"> |
||||
|
{{attributeLwm2mToString()}} |
||||
|
</div> |
||||
|
<button type="button" |
||||
|
[disabled]="isDisableBtn()" |
||||
|
mat-button mat-icon-button |
||||
|
(click)="editAttributesLwm2m($event)" |
||||
|
[matTooltip]="(isIconView() ? 'action.view' : isIconEditAdd() ? 'action.edit' : 'action.add' ) | translate" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon |
||||
|
class="material-icons">{{isIconView() ? 'visibility' : isIconEditAdd() ? 'edit' : 'add' }}</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
@ -0,0 +1,61 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
:host { |
||||
|
.tb-kv-map { |
||||
|
span.no-data-found { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
height: 40px; |
||||
|
|
||||
|
&.disabled { |
||||
|
color: rgba(0, 0, 0, .38); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
:host ::ng-deep { |
||||
|
.mat-form-field-wrapper { |
||||
|
padding-bottom: 0; |
||||
|
} |
||||
|
.mat-form-field-infix { |
||||
|
border-top: 0; |
||||
|
} |
||||
|
.mat-form-field-underline { |
||||
|
bottom: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.vertical-padding { |
||||
|
padding: 0 0 10px 20px; |
||||
|
} |
||||
|
|
||||
|
.resource-name-lw-end{ |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
text-align:end; |
||||
|
//width: 80px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.resource-name-lw{ |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,159 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2021 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import {Component, EventEmitter, forwardRef, Inject, Input, Output} from "@angular/core"; |
||||
|
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from "@angular/forms"; |
||||
|
import {coerceBooleanProperty} from "@angular/cdk/coercion"; |
||||
|
import {Store} from "@ngrx/store"; |
||||
|
import {AppState} from "@core/core.state"; |
||||
|
import {DeviceProfileService} from "@core/http/device-profile.service"; |
||||
|
import {WINDOW} from "@core/services/window.service"; |
||||
|
import {deepClone, isDefinedAndNotNull, isEmpty} from "@core/utils"; |
||||
|
import { |
||||
|
Lwm2mAttributesDialogComponent, |
||||
|
Lwm2mAttributesDialogData |
||||
|
} from "@home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component"; |
||||
|
import {MatDialog} from "@angular/material/dialog"; |
||||
|
import {TranslateService} from "@ngx-translate/core"; |
||||
|
import {ATTRIBUTE_LWM2M_LABEL} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models"; |
||||
|
|
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-profile-lwm2m-attributes', |
||||
|
templateUrl: './lwm2m-attributes.component.html', |
||||
|
styleUrls: ['./lwm2m-attributes.component.scss'], |
||||
|
providers: [{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => Lwm2mAttributesComponent), |
||||
|
multi: true |
||||
|
}] |
||||
|
}) |
||||
|
export class Lwm2mAttributesComponent implements ControlValueAccessor { |
||||
|
attributeLwm2mFormGroup: FormGroup; |
||||
|
attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL; |
||||
|
|
||||
|
private requiredValue: boolean; |
||||
|
private dirty = false; |
||||
|
|
||||
|
@Input() |
||||
|
attributeLwm2m: {}; |
||||
|
|
||||
|
@Input() |
||||
|
isAttributeTelemetry: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
destName: string; |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Output() |
||||
|
updateAttributeLwm2m = new EventEmitter<any>(); |
||||
|
|
||||
|
@Input() |
||||
|
set required(value: boolean) { |
||||
|
this.requiredValue = coerceBooleanProperty(value); |
||||
|
} |
||||
|
|
||||
|
private propagateChange = (v: any) => { |
||||
|
} |
||||
|
|
||||
|
constructor(private store: Store<AppState>, |
||||
|
private dialog: MatDialog, |
||||
|
private fb: FormBuilder, |
||||
|
private deviceProfileService: DeviceProfileService, |
||||
|
private translate: TranslateService, |
||||
|
@Inject(WINDOW) private window: Window) {} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (isDisabled) { |
||||
|
this.attributeLwm2mFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.attributeLwm2mFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.attributeLwm2mFormGroup = this.fb.group({ |
||||
|
attributeLwm2m: [this.attributeLwm2m] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
writeValue(value: {} | null): void {} |
||||
|
|
||||
|
attributeLwm2mToString = (): string => { |
||||
|
return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data'); |
||||
|
} |
||||
|
|
||||
|
private attributeLwm2mLabelToString = (): string => { |
||||
|
let label = JSON.stringify(this.attributeLwm2m); |
||||
|
label = deepClone(label.replace('{', '')); |
||||
|
label = deepClone(label.replace('}', '')); |
||||
|
this.attributeLwm2mLabel.forEach((value: string, key: string) => { |
||||
|
const dest = '\"' + key + '\"\:'; |
||||
|
label = deepClone(label.replace(dest, value)); |
||||
|
}); |
||||
|
return label; |
||||
|
} |
||||
|
|
||||
|
isDisableBtn (): boolean { |
||||
|
return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) && |
||||
|
!isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled; |
||||
|
} |
||||
|
|
||||
|
isIconView (): boolean { |
||||
|
return this.isAttributeTelemetry || this.disabled; |
||||
|
} |
||||
|
|
||||
|
isIconEditAdd (): boolean { |
||||
|
return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m); |
||||
|
} |
||||
|
|
||||
|
isToolTipLabel (): string { |
||||
|
return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') : |
||||
|
this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') : |
||||
|
this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip'); |
||||
|
} |
||||
|
|
||||
|
public editAttributesLwm2m = ($event: Event): void => { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, Object>(Lwm2mAttributesDialogComponent, { |
||||
|
disableClose: true, |
||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
||||
|
data: { |
||||
|
readonly: this.disabled, |
||||
|
attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m), |
||||
|
destName: this.destName |
||||
|
} |
||||
|
}).afterClosed().subscribe((result) => { |
||||
|
if (result) { |
||||
|
this.attributeLwm2m = result; |
||||
|
this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m}); |
||||
|
this.updateAttributeLwm2m.next(this.attributeLwm2m); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue