multiple-input-widget.component.html 12.3 KB
<!--

    Copyright © 2016-2024 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 #formContainer class="tb-multiple-input"
      [formGroup]="multipleInputFormGroup"
      tb-toast toastTarget="{{ toastTargetId }}"
      (ngSubmit)="saveForm()" novalidate autocomplete="off">
  <div class="tb-multiple-input-container" *ngIf="entityDetected && isAllParametersValid">
    <fieldset *ngFor="let source of sources" [ngClass]="{'fields-group': settings.showGroupTitle}">
      <legend class="group-title" *ngIf="settings.showGroupTitle">{{ getGroupTitle(source.datasource) }}
      </legend>
      <div class="tb-multiple-input-layout"
           [style]="{'grid-template-columns': 'repeat(' + columns + ', 1fr)', 'column-gap': settings.columnGap + 'px', 'row-gap': settings.rowGap + 'px'}">
        <ng-container *ngFor="let key of visibleKeys(source)">
          <mat-form-field *ngIf="key.settings.dataKeyValueType === 'string'"
                          [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
            <mat-label>{{key.label}}</mat-label>
            <input matInput
                   formControlName="{{key.formId}}"
                   [required]="key.settings.required"
                   [readonly]="key.settings.isEditable === 'readonly'"
                   type="text"
                   (focus)="key.isFocused = true; focusInputElement($event)"
                   (blur)="key.isFocused = false; inputChanged(source, key)">
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl" matIconPrefix>
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
              {{ getErrorMessageText(key.settings, 'required') }}
            </mat-error>
          </mat-form-field>
          <mat-form-field *ngIf="['double', 'integer'].includes(key.settings.dataKeyValueType)"
                          [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
            <mat-label>{{key.label}}</mat-label>
            <input matInput
                   formControlName="{{key.formId}}"
                   [required]="key.settings.required"
                   [readonly]="key.settings.isEditable === 'readonly'"
                   type="number"
                   step="{{key.settings.step}}"
                   min="{{key.settings.minValue}}"
                   max="{{key.settings.maxValue}}"
                   (focus)="key.isFocused = true; focusInputElement($event)"
                   (blur)="key.isFocused = false; inputChanged(source, key)">
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl" matIconPrefix>
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
              {{ getErrorMessageText(key.settings,'required') }}
            </mat-error>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('min')">
              {{ getErrorMessageText(key.settings,'min') }}
            </mat-error>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('max')">
              {{ getErrorMessageText(key.settings,'max') }}
            </mat-error>
          </mat-form-field>
          <mat-form-field *ngIf="key.settings.dataKeyValueType === 'JSON'"
                          [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
            <mat-label>{{key.label}}</mat-label>
            <input
              matInput
              type="text"
              formControlName="{{key.formId}}"
              tb-json-to-string
              [readonly]="key.settings.isEditable === 'readonly'"
              [required]="key.settings.required"
              (focus)="key.isFocused = true; focusInputElement($event)"
              (blur)="key.isFocused = false; inputChanged(source, key)"
            />
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl" matIconPrefix>
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <button [disabled]="key.settings.isEditable === 'disabled' || key.settings.isEditable === 'readonly'"
                    type="button"
                    matSuffix mat-icon-button
                    (click)="openEditJSONDialog($event, key, source)">
              <mat-icon>open_in_new</mat-icon>
            </button>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required') && !multipleInputFormGroup.get(key.formId).hasError('invalidJSON')">
              {{ getErrorMessageText(key.settings,'required') }}
            </mat-error>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('invalidJSON')">
              {{ getErrorMessageText(key.settings,'invalidJSON') | translate }}
            </mat-error>
          </mat-form-field>
          <mat-form-field *ngIf="['dateTime','date', 'time'].includes(key.settings.dataKeyValueType)"
                          [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
            <mat-label>{{key.label}}</mat-label>
            <mat-datetimepicker-toggle [for]="datePicker" matPrefix></mat-datetimepicker-toggle>
            <mat-datetimepicker #datePicker type="{{datePickerType(key.settings.dataKeyValueType)}}"
                                openOnFocus="true"></mat-datetimepicker>
            <input matInput formControlName="{{key.formId}}"
                   [required]="key.settings.required"
                   [readonly]="key.settings.isEditable === 'readonly'"
                   [matDatetimepicker]="datePicker"
                   (focus)="key.isFocused = true;"
                   (blur)="key.isFocused = false;"
                   (dateChange)="inputChanged(source, key)">
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
              {{ getErrorMessageText(key.settings, 'required') }}
            </mat-error>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('matDatepickerParse')">
              {{ getErrorMessageText(key.settings, 'invalidDate') }}
            </mat-error>
          </mat-form-field>
          <mat-form-field *ngIf="key.settings.dataKeyValueType === 'select'"
                          [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing">
            <mat-label>{{key.label}}</mat-label>
            <mat-select formControlName="{{key.formId}}"
                        [required]="key.settings.required"
                        (focus)="key.isFocused = true;"
                        (selectionChange)="key.isFocused = false; inputChanged(source, key)">
              <mat-option *ngFor="let option of key.settings.selectOptions"
                          [value]="option.value"
                          [disabled]="key.settings.isEditable === 'readonly'">
                {{ getCustomTranslationText(option.label ? option.label : option.value) }}
              </mat-option>
            </mat-select>
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl" matIconPrefix>
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
              {{ getErrorMessageText(key.settings, 'required') }}
            </mat-error>
          </mat-form-field>
          <mat-form-field *ngIf="key.settings.dataKeyValueType === 'color'"
                          class="color-input" [appearance]="key.settings.appearance" [subscriptSizing]="key.settings.subscriptSizing"
                          (click)="colorInput.openColorPickerPopup($event)">
            <mat-label>{{key.label}}</mat-label>
            <input matInput
                   formControlName="{{key.formId}}"
                   [required]="key.settings.required"
                   [readonly]="key.settings.isEditable === 'readonly'"
                   type="text"
                   (keydown)="$event.preventDefault();">
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl" matIconPrefix>
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <tb-color-input #colorInput asBoxInput matSuffix
                            colorClearButton
                            [disabled]="multipleInputFormGroup.get(key.formId).disabled"
                            [readonly]="key.settings.isEditable === 'readonly'"
                            [ngModel]="multipleInputFormGroup.get(key.formId).value"
                            (ngModelChange)="colorChanged(source, key, $event)"
                            [ngModelOptions]="{ standalone: true }">
            </tb-color-input>
            <mat-error *ngIf="multipleInputFormGroup.get(key.formId).hasError('required')">
              {{ getErrorMessageText(key.settings, 'required') }}
            </mat-error>
          </mat-form-field>
          <mat-checkbox *ngIf="key.settings.dataKeyValueType === 'booleanCheckbox'"
                        formControlName="{{key.formId}}"
                        (change)="inputChanged(source, key)">
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl">
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <span class="label-wrapper">{{key.label}}</span>
          </mat-checkbox>
          <mat-slide-toggle *ngIf="key.settings.dataKeyValueType === 'booleanSwitch'"
                            formControlName="{{key.formId}}"
                            [labelPosition]="key.settings.slideToggleLabelPosition"
                            (change)="inputChanged(source, key)">
            <ng-container *ngIf="key.settings.icon || key.settings.customIconUrl">
              <ng-container *ngTemplateOutlet="iconPrefix; context: {key: key}"></ng-container>
            </ng-container>
            <span class="label-wrapper">{{key.label}}</span>
          </mat-slide-toggle>
        </ng-container>
      </div>
    </fieldset>
  </div>
  <div class="mat-padding tb-multiple-input--buttons-container"
       *ngIf="entityDetected && isAllParametersValid && settings.showActionButtons">
    <button mat-button color="primary" type="button"
            (click)="discardAll()" class="tb-multiple-input--buttons-container__button"
            [disabled]="!multipleInputFormGroup.dirty">
      {{ resetButtonLabel }}
    </button>
    <button mat-button mat-raised-button color="primary" type="submit"
            class="tb-multiple-input--buttons-container__button"
            [disabled]="!multipleInputFormGroup.dirty || invalid()">
      {{ saveButtonLabel }}
    </button>
  </div>
  <div class="tb-multiple-input--errors-container" *ngIf="(!entityDetected || !isAllParametersValid) && datasourceDetected">
    <div class="tb-multiple-input--errors-container__error" [fxHide]="entityDetected">
      {{ 'widgets.input-widgets.no-entity-selected' | translate }}
    </div>
    <div class="tb-multiple-input--errors-container__error" [fxShow]="entityDetected && !isAllParametersValid">
      {{ 'widgets.input-widgets.not-allowed-entity' | translate }}
    </div>
  </div>
</form>
<ng-template #iconPrefix let-key="key">
  <tb-icon *ngIf="!key.settings.customIconUrl; else customToggleIcon">{{key.settings.icon}}</tb-icon>
  <ng-template #customToggleIcon>
    <img class="mat-icon" [src]="key.settings.customIconUrl | image | async" alt="icon">
  </ng-template>
</ng-template>