Browse Source

Merge branch 'master' into release-3.5

release-3.5
Igor Kulikov 3 years ago
parent
commit
ca1cede894
  1. 9
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  2. 6
      ui-ngx/src/app/core/services/menu.service.ts
  3. 34
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
  4. 17
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss
  5. 4
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html
  6. 2
      ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw
  7. 2
      ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw
  8. 4
      ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw
  9. 14
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts
  10. 4
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  11. 88
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  12. 2
      ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts
  13. 6
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts
  14. 3
      ui-ngx/src/app/shared/models/widget.models.ts
  15. 4
      ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md
  16. 10
      ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md
  17. 4
      ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_js.md
  18. 4
      ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_create_dialog_html.md
  19. 10
      ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md
  20. 4
      ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_js.md
  21. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

9
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -86,6 +86,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -294,7 +295,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
return;
}
if (inactivityTimeout <= 0L) {
inactivityTimeout = defaultInactivityTimeoutInSec;
inactivityTimeout = defaultInactivityTimeoutMs;
}
log.trace("on Device Activity Timeout Update device id {} inactivityTimeout {}", deviceId, inactivityTimeout);
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
@ -442,6 +443,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
Map<TenantId, Pair<AtomicInteger, AtomicInteger>> devicesActivity = new HashMap<>();
partitionedEntities.forEach((tpi, deviceIds) -> {
log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size());
Set<DeviceId> idsFromRemovedTenant = new HashSet<>();
for (DeviceId deviceId : deviceIds) {
DeviceStateData stateData;
try {
@ -453,9 +455,11 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
try {
updateInactivityStateIfExpired(ts, deviceId, stateData);
} catch (Exception e) {
log.warn("[{}] Failed to update inactivity state", deviceId, e);
if (e instanceof TenantNotFoundException) {
idsFromRemovedTenant.add(deviceId);
continue;
} else {
log.warn("[{}] Failed to update inactivity state [{}]", deviceId, e.getMessage());
}
}
Pair<AtomicInteger, AtomicInteger> tenantDevicesActivity = devicesActivity.computeIfAbsent(stateData.getTenantId(),
@ -466,6 +470,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService<Dev
tenantDevicesActivity.getRight().incrementAndGet();
}
}
deviceIds.removeAll(idsFromRemovedTenant);
});
devicesActivity.forEach((tenantId, tenantDevicesActivity) -> {
int active = tenantDevicesActivity.getLeft().get();

6
ui-ngx/src/app/core/services/menu.service.ts

@ -361,7 +361,8 @@ export class MenuService {
name: 'alarm.alarms',
type: 'link',
path: '/alarms',
icon: 'notifications'
icon: 'mdi:alert-outline',
isMdiIcon: true
},
{
id: 'dashboards',
@ -813,7 +814,8 @@ export class MenuService {
name: 'alarm.alarms',
type: 'link',
path: '/alarms',
icon: 'notifications'
icon: 'mdi:alert-outline',
isMdiIcon: true
},
{
id: 'dashboards',

34
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html

@ -160,17 +160,41 @@
</ng-container>
<ng-container matColumnDef="key">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 40%"> {{ 'attribute.key' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let attribute">
{{ attribute.key }}
<mat-cell *matCellDef="let attribute" class="attribute">
<div fxLayout="row" style="align-items: center">
<span class="ellipsis">{{ attribute.key }}</span>
<tb-copy-button
class="attribute-copy"
[disabled]="isLoading$ | async"
[copyText]="attribute.key"
tooltipText="{{ 'attribute.copy-key' | translate }}"
tooltipPosition="above"
icon="content_copy"
[style]="{padding: '4px', 'font-size': '16px', color: 'rgba(0,0,0,.87)'}"
>
</tb-copy-button>
</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef style="width: 60%"> {{ 'attribute.value' | translate }} </mat-header-cell>
<mat-cell *matCellDef="let attribute"
class="tb-value-cell"
class="tb-value-cell attribute"
(click)="editAttribute($event, attribute)">
<div fxLayout="row" style="align-items: center">
<span style="overflow: hidden;text-overflow: ellipsis;" fxFlex>{{attribute.value | tbJson}}</span>
<div fxFlex fxLayout="row" style="align-items: center">
<div fxFlex fxLayout="row" style="overflow: hidden; align-items: center;">
<span class="ellipsis">{{attribute.value | tbJson}}</span>
<tb-copy-button
class="attribute-copy"
[disabled]="isLoading$ | async"
[copyText]="attribute.value | tbJson"
tooltipText="{{ 'attribute.copy-value' | translate }}"
tooltipPosition="above"
icon="content_copy"
[style]="{padding: '4px', 'font-size': '16px', color: 'rgba(0,0,0,.87)'}"
>
</tb-copy-button>
</div>
<span [fxShow]="!isClientSideTelemetryTypeMap.get(attributeScope)">
<mat-icon>edit</mat-icon>
</span>

17
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss

@ -51,6 +51,7 @@
overflow: auto;
.mat-mdc-table {
table-layout: fixed;
min-width: 450px;
}
}
@ -65,6 +66,22 @@
overflow: hidden;
text-overflow: ellipsis;
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
}
.attribute-copy {
visibility: hidden;
transition: visibility 0.1s;
}
.attribute:hover {
.attribute-copy {
visibility: visible;
}
}
}
}

4
ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html

@ -27,6 +27,10 @@
(ngModelChange)="notifyActionUpdated()"
placeholder="{{ 'widget.resource-url' | translate }}"/>
</mat-form-field>
<mat-checkbox [(ngModel)]="resource.isModule"
(ngModelChange)="notifyActionUpdated()">
{{ 'widget.resource-is-module' | translate }}
</mat-checkbox>
<button mat-icon-button color="primary"
[disabled]="isLoading$ | async"
type="button"

2
ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw

@ -9,7 +9,6 @@
}
.edit-entity-form .boolean-value-input .checkbox-label {
margin-bottom: 8px;
color: rgba(0,0,0,0.54);
font-size: 12px;
}
@ -61,7 +60,6 @@
}
.add-entity-form .boolean-value-input .checkbox-label {
margin-bottom: 8px;
color: rgba(0,0,0,0.54);
font-size: 12px;
}

2
ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw

@ -111,7 +111,7 @@
<!-- <button mat-icon-button color="primary"-->
<!-- aria-label="Remove"-->
<!-- type="button"-->
<!-- (click)="removeOldRelation(i)"-->
<!-- (click)="removeOldRelation(i, relation.value)"-->
<!-- matTooltip="Remove relation"-->
<!-- matTooltipPosition="above">-->
<!-- <mat-icon>close</mat-icon>-->

4
ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw

@ -136,9 +136,9 @@
// vm.relations().markAsDirty();
// };
//
// vm.removeOldRelation = function(index) {
// vm.removeOldRelation = function(index, relation) {
// vm.oldRelations().removeAt(index);
// vm.relationsToDelete.push(vm.oldRelationsData[index]);
// vm.relationsToDelete.push(relation);
// vm.oldRelations().markAsDirty();
// };
//

14
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts

@ -33,6 +33,8 @@ import { HOME_COMPONENTS_MODULE_TOKEN, SHARED_HOME_COMPONENTS_MODULE_TOKEN } fro
@Injectable()
export class CustomDialogService {
private customModules: Array<Type<any>>
constructor(
private translate: TranslateService,
private authService: AuthService,
@ -44,12 +46,18 @@ export class CustomDialogService {
) {
}
setAdditionalModules(modules: Array<Type<any>>) {
this.customModules = modules;
}
customDialog(template: string, controller: (instance: CustomDialogComponent) => void, data?: any,
config?: MatDialogConfig): Observable<any> {
const modules = [this.sharedModule, CommonModule, this.sharedHomeComponentsModule, this.homeComponentsModule];
if (Array.isArray(this.customModules)) {
modules.push(...this.customModules);
}
return this.dynamicComponentFactoryService.createDynamicComponentFactory(
class CustomDialogComponentInstance extends CustomDialogComponent {},
template,
[this.sharedModule, CommonModule, this.sharedHomeComponentsModule, this.homeComponentsModule]).pipe(
class CustomDialogComponentInstance extends CustomDialogComponent {}, template, modules).pipe(
mergeMap((factory) => {
const dialogData: CustomDialogContainerData = {
controller,

4
ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts

@ -23,7 +23,6 @@ import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-t
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component';
import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entities-hierarchy-widget.component';
import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service';
import { RpcWidgetsModule } from '@home/components/widget/lib/rpc/rpc-widgets.module';
import {
DateRangeNavigatorPanelComponent,
@ -33,7 +32,6 @@ import { MultipleInputWidgetComponent } from '@home/components/widget/lib/multip
import { TripAnimationComponent } from '@home/components/widget/trip-animation/trip-animation.component';
import { PhotoCameraInputWidgetComponent } from '@home/components/widget/lib/photo-camera-input.component';
import { GatewayFormComponent } from '@home/components/widget/lib/gateway/gateway-form.component';
import { ImportExportService } from '@home/components/import-export/import-export.service';
import { NavigationCardsWidgetComponent } from '@home/components/widget/lib/navigation-cards-widget.component';
import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navigation-card-widget.component';
import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-overview-widget.component';
@ -93,8 +91,6 @@ import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
MarkdownWidgetComponent
],
providers: [
CustomDialogService,
ImportExportService,
{provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule }
]
})

88
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -29,8 +29,10 @@ import {
OnChanges,
OnDestroy,
OnInit,
Optional,
Renderer2,
SimpleChanges,
Type,
ViewChild,
ViewContainerRef,
ViewEncapsulation
@ -89,7 +91,7 @@ import {
import { EntityId } from '@shared/models/id/entity-id';
import { ActivatedRoute, Router } from '@angular/router';
import cssjs from '@core/css/css';
import { ResourcesService } from '@core/services/resources.service';
import { ModulesWithFactories, ResourcesService } from '@core/services/resources.service';
import { catchError, map, switchMap } from 'rxjs/operators';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TimeService } from '@core/services/time.service';
@ -115,6 +117,8 @@ import { DialogService } from '@core/services/dialog.service';
import { PopoverPlacement } from '@shared/components/popover.models';
import { TbPopoverService } from '@shared/components/popover.service';
import { DASHBOARD_PAGE_COMPONENT_TOKEN } from '@home/components/tokens';
import { MODULES_MAP } from '@shared/models/constants';
import { IModulesMap } from '@modules/common/modules-map.models';
@Component({
selector: 'tb-widget',
@ -190,6 +194,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
private popoverService: TbPopoverService,
@Inject(EMBED_DASHBOARD_DIALOG_TOKEN) private embedDashboardDialogComponent: ComponentType<any>,
@Inject(DASHBOARD_PAGE_COMPONENT_TOKEN) private dashboardPageComponent: ComponentType<any>,
@Optional() @Inject(MODULES_MAP) private modulesMap: IModulesMap,
private widgetService: WidgetService,
private resources: ResourcesService,
private timeService: TimeService,
@ -1160,8 +1165,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
if (isDefined(customHtml) && customHtml.length > 0) {
htmlTemplate = customHtml;
}
this.loadCustomActionResources(actionNamespace, customCss, customResources).subscribe(
() => {
this.loadCustomActionResources(actionNamespace, customCss, customResources, descriptor).subscribe({
next: () => {
if (isDefined(customPrettyFunction) && customPrettyFunction.length > 0) {
try {
if (!additionalParams) {
@ -1169,16 +1174,17 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
}
const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId',
'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction);
this.widgetContext.customDialog.setAdditionalModules(descriptor.customModules);
customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel);
} catch (e) {
console.error(e);
}
}
},
(errorMessages: string[]) => {
error: (errorMessages: string[]) => {
this.processResourcesLoadErrors(errorMessages);
}
);
});
break;
case WidgetActionType.mobileAction:
const mobileAction = descriptor.mobileAction;
@ -1473,33 +1479,69 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
}
}
private loadCustomActionResources(actionNamespace: string, customCss: string, customResources: Array<WidgetResource>): Observable<any> {
private loadCustomActionResources(actionNamespace: string, customCss: string, customResources: Array<WidgetResource>, actionDescriptor: WidgetActionDescriptor): Observable<any> {
const resourceTasks: Observable<string>[] = [];
const modulesTasks: Observable<ModulesWithFactories | string>[] = [];
if (isDefined(customCss) && customCss.length > 0) {
this.cssParser.cssPreviewNamespace = actionNamespace;
this.cssParser.createStyleElement(actionNamespace, customCss, 'nonamespace');
}
const resourceTasks: Observable<string>[] = [];
if (isDefined(customResources) && customResources.length > 0) {
customResources.forEach((resource) => {
resourceTasks.push(
this.resources.loadResource(resource.url).pipe(
catchError(e => of(`Failed to load custom action resource: '${resource.url}'`))
)
);
customResources.forEach(resource => {
if (resource.isModule) {
modulesTasks.push(
this.resources.loadFactories(resource.url, this.modulesMap).pipe(
catchError((e: Error) => of(e?.message ? e.message : `Failed to load custom action resource module: '${resource.url}'`))
)
);
} else {
resourceTasks.push(
this.resources.loadResource(resource.url).pipe(
catchError(() => of(`Failed to load custom action resource: '${resource.url}'`))
)
);
}
});
return forkJoin(resourceTasks).pipe(
switchMap(msgs => {
let errors: string[];
if (msgs && msgs.length) {
errors = msgs.filter(msg => msg && msg.length > 0);
if (modulesTasks.length) {
const modulesObservable: Observable<string | Type<any>[]> = forkJoin(modulesTasks).pipe(
map(res => {
const msg = res.find(r => typeof r === 'string');
if (msg) {
return msg as string;
} else {
const modulesWithFactoriesList = res as ModulesWithFactories[];
const resModulesWithFactories: ModulesWithFactories = {
modules: modulesWithFactoriesList.map(mf => mf.modules).flat(),
factories: modulesWithFactoriesList.map(mf => mf.factories).flat()
};
return resModulesWithFactories.modules;
}
if (errors && errors.length) {
return throwError(errors);
})
);
resourceTasks.push(modulesObservable.pipe(
map((resolvedModules) => {
if (typeof resolvedModules === 'string') {
return resolvedModules;
} else {
return of(null);
actionDescriptor.customModules = resolvedModules;
return null;
}
}
));
})));
}
return forkJoin(resourceTasks).pipe(
switchMap(msgs => {
const errors = msgs.filter(msg => msg && msg.length > 0);
if (errors.length > 0) {
return throwError(() => errors);
} else {
return of(null);
}}
));
} else {
return of(null);
}

2
ui-ngx/src/app/modules/home/pages/alarm/alarm-routing.module.ts

@ -42,7 +42,7 @@ const routes: Routes = [
title: 'alarm.alarms',
breadcrumb: {
label: 'alarm.alarms',
icon: 'notifications'
icon: 'mdi:alert-outline'
},
isPage: true,
alarmsMode: AlarmsMode.ALL

6
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts

@ -26,7 +26,7 @@ import { mergeMap, Observable, of } from 'rxjs';
import { Dashboard } from '@app/shared/models/dashboard.models';
import { DashboardService } from '@core/http/dashboard.service';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { map } from 'rxjs/operators';
import { catchError, map } from 'rxjs/operators';
import { UserSettingsService } from '@core/http/user-settings.service';
import { UserDashboardAction } from '@shared/models/user-settings.models';
import { Store } from '@ngrx/store';
@ -47,7 +47,9 @@ export class DashboardResolver implements Resolve<Dashboard> {
return this.dashboardService.getDashboard(dashboardId).pipe(
mergeMap((dashboard) =>
(getCurrentAuthUser(this.store).isPublic ? of(null) :
this.userSettingService.reportUserDashboardAction(dashboardId, UserDashboardAction.VISIT, {ignoreLoading: true})).pipe(
this.userSettingService.reportUserDashboardAction(dashboardId, UserDashboardAction.VISIT,
{ignoreLoading: true, ignoreErrors: true})).pipe(
catchError(() => of(dashboard)),
map(() => dashboard)
)),
map((dashboard) => this.dashboardUtils.validateAndUpdateDashboard(dashboard))

3
ui-ngx/src/app/shared/models/widget.models.ts

@ -32,7 +32,7 @@ import {
} from '@shared/models/query/query.models';
import { PopoverPlacement } from '@shared/components/popover.models';
import { PageComponent } from '@shared/components/page.component';
import { AfterViewInit, Directive, EventEmitter, Inject, OnInit } from '@angular/core';
import { AfterViewInit, Directive, EventEmitter, Inject, OnInit, Type } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
@ -560,6 +560,7 @@ export interface CustomActionDescriptor {
customResources?: Array<WidgetResource>;
customHtml?: string;
customCss?: string;
customModules?: Type<any>[];
}
export interface WidgetActionDescriptor extends CustomActionDescriptor {

4
ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_create_dialog_html.md

@ -82,7 +82,7 @@
<div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
<label class="checkbox-label">Boolean Value</label>
<mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
{{ (addEntityFormGroup.get('attributes.booleanValue').value ? "value.true" : "value.false") | translate }}
</mat-checkbox>
</div>
</div>
@ -98,7 +98,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">

10
ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_html.md

@ -5,7 +5,7 @@
<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
(ngSubmit)="save()" class="edit-entity-form">
<mat-toolbar fxLayout="row" color="primary">
<h2>Edit </h2>
<h2>Edit {{entityType.toLowerCase()}} {{entityName}}</h2>
<span fxFlex></span>
<button mat-icon-button (click)="cancel()" type="button">
<mat-icon class="material-icons">close</mat-icon>
@ -67,7 +67,7 @@
<div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
<label class="checkbox-label">Boolean Value</label>
<mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
{{ (editEntityFormGroup.get('attributes.booleanValue').value ? "value.true" : "value.false") | translate }}
</mat-checkbox>
</div>
</div>
@ -84,7 +84,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">
@ -109,7 +109,7 @@
<button mat-icon-button color="primary"
aria-label="Remove"
type="button"
(click)="removeOldRelation(i)"
(click)="removeOldRelation(i, relation.value)"
matTooltip="Remove relation"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
@ -130,7 +130,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">

4
ui-ngx/src/assets/help/en_US/widget/action/examples/custom_pretty_edit_dialog_js.md

@ -82,9 +82,9 @@ function EditEntityDialogController(instance) {
vm.relations().markAsDirty();
};
vm.removeOldRelation = function(index) {
vm.removeOldRelation = function(index, relation) {
vm.oldRelations().removeAt(index);
vm.relationsToDelete.push(vm.oldRelationsData[index]);
vm.relationsToDelete.push(relation);
vm.oldRelations().markAsDirty();
};

4
ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_create_dialog_html.md

@ -82,7 +82,7 @@
<div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
<label class="checkbox-label">Boolean Value</label>
<mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
{{ (addEntityFormGroup.get('attributes.booleanValue').value ? "value.true" : "value.false") | translate }}
</mat-checkbox>
</div>
</div>
@ -98,7 +98,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">

10
ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_html.md

@ -5,7 +5,7 @@
<form #editEntityForm="ngForm" [formGroup]="editEntityFormGroup"
(ngSubmit)="save()" class="edit-entity-form">
<mat-toolbar fxLayout="row" color="primary">
<h2>Edit </h2>
<h2>Edit {{entityType.toLowerCase()}} {{entityName}}</h2>
<span fxFlex></span>
<button mat-icon-button (click)="cancel()" type="button">
<mat-icon class="material-icons">close</mat-icon>
@ -67,7 +67,7 @@
<div class="boolean-value-input" fxLayout="column" fxLayoutAlign="center start" fxFlex>
<label class="checkbox-label">Boolean Value</label>
<mat-checkbox formControlName="booleanValue" style="margin-bottom: 40px;">
{{ (editEntityFormGroup.get('attributes.booleanValue').value ? "value.true" : "value.false") | translate }}
</mat-checkbox>
</div>
</div>
@ -84,7 +84,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">
@ -109,7 +109,7 @@
<button mat-icon-button color="primary"
aria-label="Remove"
type="button"
(click)="removeOldRelation(i)"
(click)="removeOldRelation(i, relation.value)"
matTooltip="Remove relation"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
@ -130,7 +130,7 @@
<mat-label>Direction</mat-label>
<mat-select formControlName="direction" name="direction">
<mat-option *ngFor="let direction of entitySearchDirection | keyvalue" [value]="direction.value">
{{ ("relation.search-direction." + direction.value) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="relation.get('direction').hasError('required')">

4
ui-ngx/src/assets/help/en_US/widget/action/examples_custom_pretty/custom_pretty_edit_dialog_js.md

@ -82,9 +82,9 @@ function EditEntityDialogController(instance) {
vm.relations().markAsDirty();
};
vm.removeOldRelation = function(index) {
vm.removeOldRelation = function(index, relation) {
vm.oldRelations().removeAt(index);
vm.relationsToDelete.push(vm.oldRelationsData[index]);
vm.relationsToDelete.push(relation);
vm.oldRelations().markAsDirty();
};

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -691,7 +691,9 @@
"selected-attributes": "{ count, plural, =1 {1 attribute} other {# attributes} } selected",
"selected-telemetry": "{ count, plural, =1 {1 telemetry unit} other {# telemetry units} } selected",
"no-attributes-text": "No attributes found",
"no-telemetry-text": "No telemetry found"
"no-telemetry-text": "No telemetry found",
"copy-key": "Copy key",
"copy-value": "Copy value"
},
"api-usage": {
"api-features": "API features",

Loading…
Cancel
Save