ThingsBoard Widgets – Milliseconds to Minute Conversion

Issue

I’m taking over a previous member’s code which is very similar to the example HTML code and js functions provided here with a few small changes.

When I open the widget, I can change some attributes about a gateway (eg. high priority boolean, or inactivity timeout). The widget takes time in ms, which looks confusing for people when entering, so the previous member created some set times, relating to the times in ms, see image for visual. Example image of dropdown menu.
The HTML code for this is (I edited out parts for the other fields as not needed):

<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
  (ngSubmit)="save()"  class="edit-entity-form">

    <div formGroupName="attributes" fxLayout="column" fxLayoutGap="8px">
        <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
            <mat-form-field fxFlex class="mat-block">
                <mat-label>Disconnection Timeout</mat-label>
                <mat-select matInput formControlName="inactivityTimeout">
                    <mat-option value=1800000>30 min</mat-option>
                    <mat-option value=3600000>60 min</mat-option>
                    <mat-option value=7200000>120 min</mat-option>
                </mat-select>
            </mat-form-field>
        </div>
    </div> 
</div>
</form>

The corresponding javascript function (from what I’ve been able to figure out) is:

function EditEntityDialogController(instance) {
    let vm = instance;
    
    vm.entityName = entityName;
    vm.entityType = entityId.entityType;
    vm.entitySearchDirection = {
        to: "TO"
    };
    vm.attributes = {};
    vm.oldRelationsData = [];
    vm.relationsToDelete = [];
    vm.entity = {};

    vm.editEntityFormGroup = vm.fb.group({
        entityName: ['', [vm.validators.required]],
        entityType: [null],
        entityLabel: [null],
        type: ['', [vm.validators.required]],
        attributes: vm.fb.group({
            inactivityTimeout: [null, [vm.validators.pattern(/^-?[0-9]+$/)]],
        }),
        oldRelations: vm.fb.array([]),
        relations: vm.fb.array([])
    });
/*This function goes on but it is just a bunch of inner functions like 
.save or .cancel*/

Although this setup isn’t great, it is fine because it actually allows the output to be sent to the server attributes in ms, however, it sends it as a string instead of an integer even though the HTML code says value=180000 instead of value="180000". Image showing string type.

I’m trying to either get this current setup to output as an integer, or change it to a text input (which I’ve been able to do and it works, but only in ms) that displays in minutes but then gets converted to ms.

I’ve tried for nearly 8 hours between yesterday and today, but I’ve had no luck looking at other stackoverflow posts and other resources on the internet such as angular’s documentation.

If anyone is able to help me, that would be greatly appreciated.

Solution

If you want to use for value number instead string you should add [ ] to ‘value’ in mat-option:

<mat-select matInput formControlName="inactivityTimeout">
                <mat-option [value]=1800000>30 min</mat-option>
                <mat-option [value]=3600000>60 min</mat-option>
                <mat-option [value]=7200000>120 min</mat-option>
</mat-select>

here is a short example with text input and a selector that sends numbers:

HTML

<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
  (ngSubmit)="save()"  class="edit-entity-form">
<mat-toolbar fxLayout="row" color="primary">
    <h2>Edit test value</h2>
    <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 style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
    <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"  fxLayoutGap.xs="0">
        <mat-form-field fxFlex class="mat-block">
            <mat-label>Input Test</mat-label>
            <input type="number" step="any" matInput formControlName="inputTest">
        </mat-form-field>
         <mat-form-field fxFlex class="mat-block">
            <mat-label>Selector Test</mat-label>
            <mat-select matInput formControlName="selectorTest">
                <mat-option [value]=1800000>30 min</mat-option>
                <mat-option [value]=3600000>60 min</mat-option>
                <mat-option [value]=7200000>120 min</mat-option>
            </mat-select>
        </mat-form-field>
    </div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
    <button mat-button color="primary"
            type="button"
            [disabled]="(isLoading$ | async)"
            (click)="cancel()" cdkFocusInitial>
        Cancel
    </button>
    <button mat-button mat-raised-button color="primary"
            type="submit"
            [disabled]="(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty">
        Save
    </button>
</div>

JS:

let $injector = widgetContext.$scope.$injector;
let customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));
let attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));

openEditEntityDialog();

function openEditEntityDialog() {
    customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();
}

function EditEntityDialogController(instance) {
    let vm = instance;
    vm.attributes = {};
    vm.editEntityFormGroup = vm.fb.group({
        inputTest: [null],
        selectorTest: [null]
    });

    getEntityInfo();

    vm.cancel = function() {
        vm.dialogRef.close(null);
    };

    vm.save = function() {
        vm.editEntityFormGroup.markAsPristine();
        widgetContext.rxjs.forkJoin([
            saveAttributes(entityId),
        ]).subscribe(
            function () {
                widgetContext.updateAliases();
                vm.dialogRef.close(null);
            }
        );
    };

    function getEntityAttributes(attributes) {
        for (var i = 0; i < attributes.length; i++) {
            vm.attributes[attributes[i].key] = attributes[i].value;
        }
    }

    function getEntityInfo() {

    attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',['inputTest','selectorTest']).subscribe(
            function (data) {
                getEntityAttributes(data);
                vm.editEntityFormGroup.patchValue({
                    inputTest: vm.attributes.inputTest,
                    selectorTest: vm.attributes.selectorTest
                }, {emitEvent: false});
            }
        );
    }

    function saveAttributes(entityId) {
        let attributes = vm.editEntityFormGroup.value;
        let attributesArray = [];
        for (let key in attributes) {
            if (attributes[key] !== vm.attributes[key]) {
                attributesArray.push({key: key, value: attributes[key]});
            }
        }
        if (attributesArray.length > 0) {
            return attributeService.saveEntityAttributes(entityId, "SERVER_SCOPE", attributesArray);
        }
        return widgetContext.rxjs.of([]);
    }
}

just add this code to the custom widget action.

Answered By – Женя Калитка

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published