Merge pull request #12150 from vvlladd28/feature/edit-alias/datasource-widgets
Add edit alias from dasource widgets
This commit is contained in:
commit
404ab062a0
@ -31,6 +31,14 @@
|
||||
(click)="clear()">
|
||||
<mat-icon class="material-icons">close</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="selectEntityAliasFormGroup.get('entityAlias').value?.id && !disabled"
|
||||
type="button"
|
||||
matSuffix mat-icon-button aria-label="Edit"
|
||||
matTooltip="{{ 'device-profile.edit' | translate }}"
|
||||
matTooltipPosition="above"
|
||||
(click)="editEntityAlias($event)">
|
||||
<mat-icon class="material-icons">edit</mat-icon>
|
||||
</button>
|
||||
<button *ngIf="!selectEntityAliasFormGroup.get('entityAlias').value && !disabled"
|
||||
style="margin-right: 8px;"
|
||||
type="button"
|
||||
|
||||
@ -20,4 +20,5 @@ import { EntityAlias } from '@shared/models/alias.models';
|
||||
|
||||
export interface EntityAliasSelectCallbacks {
|
||||
createEntityAlias: (alias: string, allowedEntityTypes: Array<EntityType>) => Observable<EntityAlias>;
|
||||
editEntityAlias: (alias: EntityAlias, allowedEntityTypes: Array<EntityType>) => Observable<EntityAlias>;
|
||||
}
|
||||
|
||||
@ -14,24 +14,22 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core';
|
||||
import { Component, ElementRef, forwardRef, Input, OnInit, SkipSelf, ViewChild } from '@angular/core';
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
FormGroupDirective,
|
||||
NG_VALUE_ACCESSOR,
|
||||
NgForm
|
||||
NgForm,
|
||||
} from '@angular/forms';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { map, mergeMap, share, tap } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@app/core/core.state';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { EntityService } from '@core/http/entity.service';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
import { EntityAlias } from '@shared/models/alias.models';
|
||||
import { IAliasController } from '@core/api/widget-api.models';
|
||||
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
|
||||
@ -54,9 +52,9 @@ import { ErrorStateMatcher } from '@angular/material/core';
|
||||
useExisting: EntityAliasSelectComponent
|
||||
}*/]
|
||||
})
|
||||
export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, AfterViewInit, ErrorStateMatcher {
|
||||
export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit, ErrorStateMatcher {
|
||||
|
||||
selectEntityAliasFormGroup: UntypedFormGroup;
|
||||
selectEntityAliasFormGroup: FormGroup;
|
||||
|
||||
modelValue: string | null;
|
||||
|
||||
@ -75,15 +73,9 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
@ViewChild('entityAliasAutocomplete') entityAliasAutocomplete: MatAutocomplete;
|
||||
@ViewChild('autocomplete', { read: MatAutocompleteTrigger }) autoCompleteTrigger: MatAutocompleteTrigger;
|
||||
|
||||
|
||||
private requiredValue: boolean;
|
||||
get tbRequired(): boolean {
|
||||
return this.requiredValue;
|
||||
}
|
||||
@Input()
|
||||
set tbRequired(value: boolean) {
|
||||
this.requiredValue = coerceBooleanProperty(value);
|
||||
}
|
||||
@coerceBoolean()
|
||||
tbRequired: boolean;
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
@ -98,16 +90,13 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
|
||||
private dirty = false;
|
||||
|
||||
private creatingEntityAlias = false;
|
||||
private propagateChange = (_v: any) => { };
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
|
||||
constructor(@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
|
||||
private entityService: EntityService,
|
||||
public translate: TranslateService,
|
||||
public truncate: TruncatePipe,
|
||||
private fb: UntypedFormBuilder) {
|
||||
private fb: FormBuilder) {
|
||||
this.selectEntityAliasFormGroup = this.fb.group({
|
||||
entityAlias: [null]
|
||||
});
|
||||
@ -117,7 +106,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
registerOnTouched(_fn: any): void {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -134,7 +123,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
this.filteredEntityAliases = this.selectEntityAliasFormGroup.get('entityAlias').valueChanges
|
||||
.pipe(
|
||||
tap(value => {
|
||||
let modelValue;
|
||||
let modelValue: EntityAlias;
|
||||
if (typeof value === 'string' || !value) {
|
||||
modelValue = null;
|
||||
} else {
|
||||
@ -151,14 +140,12 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
);
|
||||
}
|
||||
|
||||
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
||||
const customErrorState = this.tbRequired && !this.modelValue;
|
||||
return originalErrorState || customErrorState;
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
if (this.disabled) {
|
||||
@ -225,7 +212,7 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
}
|
||||
|
||||
textIsNotEmpty(text: string): boolean {
|
||||
return (text && text != null && text.length > 0) ? true : false;
|
||||
return text?.length > 0;
|
||||
}
|
||||
|
||||
entityAliasEnter($event: KeyboardEvent) {
|
||||
@ -237,10 +224,23 @@ export class EntityAliasSelectComponent implements ControlValueAccessor, OnInit,
|
||||
}
|
||||
}
|
||||
|
||||
editEntityAlias($event: Event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
if (this.callbacks && this.callbacks.editEntityAlias) {
|
||||
this.callbacks.editEntityAlias(this.selectEntityAliasFormGroup.get('entityAlias').value,
|
||||
this.allowedEntityTypes).subscribe((alias) => {
|
||||
if (alias) {
|
||||
this.modelValue = alias.id;
|
||||
this.selectEntityAliasFormGroup.get('entityAlias').patchValue(alias, {emitEvent: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createEntityAlias($event: Event, alias: string, focusOnCancel = true) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
this.creatingEntityAlias = true;
|
||||
if (this.callbacks && this.callbacks.createEntityAlias) {
|
||||
this.callbacks.createEntityAlias(alias, this.allowedEntityTypes).subscribe((newAlias) => {
|
||||
if (!newAlias) {
|
||||
|
||||
@ -136,8 +136,7 @@
|
||||
</div>
|
||||
<ng-template #searchNotEmpty>
|
||||
<span>
|
||||
{{ translate.get('entity.no-key-matching',
|
||||
{key: truncate.transform(searchText, true, 6, '...')}) | async }}
|
||||
{{ 'entity.no-key-matching' | translate : {key: searchText | truncate: true: 6: '...'} }}
|
||||
</span>
|
||||
<span *ngIf="!isEntityDatasource; else createEntityKey">
|
||||
<a translate (click)="createKey(searchText)">entity.create-new-key</a>
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
|
||||
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
DestroyRef,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
Input,
|
||||
@ -33,20 +33,18 @@ import {
|
||||
import {
|
||||
AbstractControl,
|
||||
ControlValueAccessor,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
FormGroupDirective,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
NgForm,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
ValidationErrors,
|
||||
Validator
|
||||
} from '@angular/forms';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@app/core/core.state';
|
||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
||||
import { filter, map, mergeMap, share, tap } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete';
|
||||
import { MatChipGrid, MatChipInputEvent, MatChipRow } from '@angular/material/chips';
|
||||
@ -58,7 +56,6 @@ import { DataKeySettingsFunction } from './data-keys.component.models';
|
||||
import { alarmFields } from '@shared/models/alarm.models';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { ErrorStateMatcher } from '@angular/material/core';
|
||||
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import {
|
||||
DataKeyConfigDialogComponent,
|
||||
@ -74,6 +71,7 @@ import { DatasourceComponent } from '@home/components/widget/config/datasource.c
|
||||
import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component';
|
||||
import { TbPopoverService } from '@shared/components/popover.service';
|
||||
import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-data-keys',
|
||||
@ -115,11 +113,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
return this.datasourceComponent.hideDataKeyDecimals;
|
||||
}
|
||||
|
||||
datasourceTypes = DatasourceType;
|
||||
widgetTypes = widgetType;
|
||||
dataKeyTypes = DataKeyType;
|
||||
|
||||
keysListFormGroup: UntypedFormGroup;
|
||||
keysListFormGroup: FormGroup;
|
||||
|
||||
modelValue: Array<DataKey> | null;
|
||||
|
||||
@ -218,23 +215,21 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
|
||||
private dirty = false;
|
||||
|
||||
private propagateChange = (v: any) => { };
|
||||
private propagateChange = (_v: any) => { };
|
||||
|
||||
private keysRequired = this._keysRequired.bind(this);
|
||||
private keysValidator = this._keysValidator.bind(this);
|
||||
|
||||
constructor(private store: Store<AppState>,
|
||||
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
|
||||
constructor(@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
|
||||
private datasourceComponent: DatasourceComponent,
|
||||
public translate: TranslateService,
|
||||
private translate: TranslateService,
|
||||
private utils: UtilsService,
|
||||
private dialog: MatDialog,
|
||||
private fb: UntypedFormBuilder,
|
||||
private cd: ChangeDetectorRef,
|
||||
private fb: FormBuilder,
|
||||
private popoverService: TbPopoverService,
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
private renderer: Renderer2,
|
||||
public truncate: TruncatePipe) {
|
||||
private destroyRef: DestroyRef) {
|
||||
}
|
||||
|
||||
updateValidators() {
|
||||
@ -246,7 +241,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
this.keysListFormGroup.get('keys').updateValueAndValidity();
|
||||
}
|
||||
|
||||
private _keysRequired(control: AbstractControl): ValidationErrors | null {
|
||||
private _keysRequired(_control: AbstractControl): ValidationErrors | null {
|
||||
const value = this.modelValue;
|
||||
if (value && Array.isArray(value) && value.length) {
|
||||
return null;
|
||||
@ -255,7 +250,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
}
|
||||
}
|
||||
|
||||
private _keysValidator(control: AbstractControl): ValidationErrors | null {
|
||||
private _keysValidator(_control: AbstractControl): ValidationErrors | null {
|
||||
const value = this.modelValue;
|
||||
if (value && Array.isArray(value)) {
|
||||
if (value.some(v => isObject(v) && (!v.type || !v.name))) {
|
||||
@ -274,7 +269,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
}
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
registerOnTouched(_fn: any): void {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -314,6 +309,15 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
mergeMap(name => this.fetchKeys(name) ),
|
||||
share()
|
||||
);
|
||||
|
||||
this.aliasController.entityAliasesChanged.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe(aliasIds => {
|
||||
if (aliasIds.includes(this.entityAliasId)) {
|
||||
this.clearSearchCache();
|
||||
this.dirty = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public maxDataKeysText(): string {
|
||||
@ -413,7 +417,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
}
|
||||
}
|
||||
|
||||
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
|
||||
const customErrorState = this.keysListFormGroup.get('keys').hasError('dataKey');
|
||||
return originalErrorState || customErrorState;
|
||||
@ -442,7 +446,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
validate(c: UntypedFormControl) {
|
||||
validate(_c: FormControl) {
|
||||
return (this.keysListFormGroup.get('keys').hasError('dataKey')) ? {
|
||||
dataKeys: {
|
||||
valid: false,
|
||||
@ -637,8 +641,12 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
fetchObservable = of([]);
|
||||
}
|
||||
this.fetchObservable$ = fetchObservable.pipe(
|
||||
publishReplay(1),
|
||||
refCount()
|
||||
share({
|
||||
connector: () => new ReplaySubject(1),
|
||||
resetOnError: false,
|
||||
resetOnComplete: false,
|
||||
resetOnRefCountZero: false
|
||||
})
|
||||
);
|
||||
}
|
||||
return this.fetchObservable$;
|
||||
@ -650,7 +658,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange
|
||||
}
|
||||
|
||||
textIsNotEmpty(text: string): boolean {
|
||||
return text && text.length > 0;
|
||||
return text?.length > 0;
|
||||
}
|
||||
|
||||
clear(value: string = '', focus = true) {
|
||||
|
||||
@ -165,6 +165,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
|
||||
widgetConfigCallbacks: WidgetConfigCallbacks = {
|
||||
createEntityAlias: this.createEntityAlias.bind(this),
|
||||
editEntityAlias: this.editEntityAlias.bind(this),
|
||||
createFilter: this.createFilter.bind(this),
|
||||
generateDataKey: this.generateDataKey.bind(this),
|
||||
fetchEntityKeysForDevice: this.fetchEntityKeysForDevice.bind(this),
|
||||
@ -841,6 +842,27 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe
|
||||
);
|
||||
}
|
||||
|
||||
private editEntityAlias(alias: EntityAlias, allowedEntityTypes: Array<EntityType>): Observable<EntityAlias> {
|
||||
return this.dialog.open<EntityAliasDialogComponent, EntityAliasDialogData,
|
||||
EntityAlias>(EntityAliasDialogComponent, {
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
isAdd: false,
|
||||
allowedEntityTypes,
|
||||
entityAliases: this.dashboard.configuration.entityAliases,
|
||||
alias: deepClone(alias)
|
||||
}
|
||||
}).afterClosed().pipe(
|
||||
tap((entityAlias) => {
|
||||
if (entityAlias) {
|
||||
this.dashboard.configuration.entityAliases[entityAlias.id] = entityAlias;
|
||||
this.aliasController.updateEntityAliases(this.dashboard.configuration.entityAliases);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private createFilter(filter: string): Observable<Filter> {
|
||||
const singleFilter: Filter = {id: null, filter, keyFilters: [], editable: true};
|
||||
return this.dialog.open<FilterDialogComponent, FilterDialogData,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user