Entity Admin widgets. Introduce updateAliases method of alias controller.

This commit is contained in:
Igor Kulikov 2020-02-27 17:06:32 +02:00
parent 1d960b96bc
commit 1157df05e9
12 changed files with 73 additions and 43 deletions

File diff suppressed because one or more lines are too long

View File

@ -95,6 +95,7 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
this.customerId = assetEntity.getCustomerId(); this.customerId = assetEntity.getCustomerId();
this.type = assetEntity.getType(); this.type = assetEntity.getType();
this.name = assetEntity.getName(); this.name = assetEntity.getName();
this.label = assetEntity.getLabel();
this.searchText = assetEntity.getSearchText(); this.searchText = assetEntity.getSearchText();
this.additionalInfo = assetEntity.getAdditionalInfo(); this.additionalInfo = assetEntity.getAdditionalInfo();
} }

View File

@ -26,7 +26,7 @@ import { map } from 'rxjs/operators';
export class AliasController implements IAliasController { export class AliasController implements IAliasController {
private entityAliasesChangedSubject = new Subject<Array<string>>(); entityAliasesChangedSubject = new Subject<Array<string>>();
entityAliasesChanged: Observable<Array<string>> = this.entityAliasesChangedSubject.asObservable(); entityAliasesChanged: Observable<Array<string>> = this.entityAliasesChangedSubject.asObservable();
private entityAliasResolvedSubject = new Subject<string>(); private entityAliasResolvedSubject = new Subject<string>();
@ -69,6 +69,23 @@ export class AliasController implements IAliasController {
} }
} }
updateAliases(aliasIds?: Array<string>) {
if (!aliasIds) {
aliasIds = [];
for (const aliasId of Object.keys(this.resolvedAliases)) {
aliasIds.push(aliasId);
}
}
const tasks: Observable<AliasInfo>[] = [];
for (const aliasId of aliasIds) {
this.setAliasUnresolved(aliasId);
tasks.push(this.getAliasInfo(aliasId));
}
forkJoin(tasks).subscribe(() => {
this.entityAliasesChangedSubject.next(aliasIds);
});
}
dashboardStateChanged() { dashboardStateChanged() {
const changedAliasIds: Array<string> = []; const changedAliasIds: Array<string> = [];
for (const aliasId of Object.keys(this.resolvedAliasesToStateEntities)) { for (const aliasId of Object.keys(this.resolvedAliasesToStateEntities)) {
@ -85,7 +102,7 @@ export class AliasController implements IAliasController {
} }
} }
private setAliasUnresolved(aliasId: string) { setAliasUnresolved(aliasId: string) {
delete this.resolvedAliases[aliasId]; delete this.resolvedAliases[aliasId];
delete this.resolvedAliasesObservable[aliasId]; delete this.resolvedAliasesObservable[aliasId];
delete this.resolvedAliasesToStateEntities[aliasId]; delete this.resolvedAliasesToStateEntities[aliasId];
@ -225,7 +242,7 @@ export class AliasController implements IAliasController {
resolveAlarmSource(alarmSource: Datasource): Observable<Datasource> { resolveAlarmSource(alarmSource: Datasource): Observable<Datasource> {
return this.resolveDatasource(alarmSource, true).pipe( return this.resolveDatasource(alarmSource, true).pipe(
map((datasources) => { map((datasources) => {
const datasource = datasources[0]; const datasource = datasources && datasources.length ? datasources[0] : deepClone(alarmSource);
if (datasource.type === DatasourceType.function) { if (datasource.type === DatasourceType.function) {
let name: string; let name: string;
if (datasource.name && datasource.name.length) { if (datasource.name && datasource.name.length) {

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Observable } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { EntityId } from '@app/shared/models/id/entity-id'; import { EntityId } from '@app/shared/models/id/entity-id';
import { import {
DataSet, DataSet,
@ -99,6 +99,7 @@ export interface IAliasController {
getEntityAliases(): EntityAliases; getEntityAliases(): EntityAliases;
updateCurrentAliasEntity(aliasId: string, currentEntity: EntityInfo); updateCurrentAliasEntity(aliasId: string, currentEntity: EntityInfo);
updateEntityAliases(entityAliases: EntityAliases); updateEntityAliases(entityAliases: EntityAliases);
updateAliases(aliasIds?: Array<string>);
dashboardStateChanged(); dashboardStateChanged();
} }
@ -218,6 +219,7 @@ export interface SubscriptionEntityInfo {
export interface IWidgetSubscription { export interface IWidgetSubscription {
options: WidgetSubscriptionOptions;
id: string; id: string;
init$: Observable<IWidgetSubscription>; init$: Observable<IWidgetSubscription>;
ctx: WidgetSubscriptionContext; ctx: WidgetSubscriptionContext;

View File

@ -127,7 +127,7 @@ export class WidgetSubscription implements IWidgetSubscription {
targetDeviceName: string; targetDeviceName: string;
executingSubjects: Array<Subject<any>>; executingSubjects: Array<Subject<any>>;
constructor(subscriptionContext: WidgetSubscriptionContext, options: WidgetSubscriptionOptions) { constructor(subscriptionContext: WidgetSubscriptionContext, public options: WidgetSubscriptionOptions) {
const subscriptionSubject = new ReplaySubject<IWidgetSubscription>(); const subscriptionSubject = new ReplaySubject<IWidgetSubscription>();
this.init$ = subscriptionSubject.asObservable(); this.init$ = subscriptionSubject.asObservable();
this.ctx = subscriptionContext; this.ctx = subscriptionContext;
@ -878,8 +878,8 @@ export class WidgetSubscription implements IWidgetSubscription {
} }
private checkAlarmSource(aliasIds: Array<string>): boolean { private checkAlarmSource(aliasIds: Array<string>): boolean {
if (this.alarmSource && this.alarmSource.entityAliasId) { if (this.options.alarmSource && this.options.alarmSource.entityAliasId) {
return aliasIds.indexOf(this.alarmSource.entityAliasId) > -1; return aliasIds.indexOf(this.options.alarmSource.entityAliasId) > -1;
} else { } else {
return false; return false;
} }
@ -887,11 +887,14 @@ export class WidgetSubscription implements IWidgetSubscription {
private checkSubscriptions(aliasIds: Array<string>): boolean { private checkSubscriptions(aliasIds: Array<string>): boolean {
let subscriptionsChanged = false; let subscriptionsChanged = false;
for (const listener of this.datasourceListeners) { const datasources = this.options.datasources;
if (listener.datasource.entityAliasId) { if (datasources) {
if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) { for (const datasource of datasources) {
subscriptionsChanged = true; if (datasource.entityAliasId) {
break; if (aliasIds.indexOf(datasource.entityAliasId) > -1) {
subscriptionsChanged = true;
break;
}
} }
} }
} }

View File

@ -20,6 +20,7 @@ import { forkJoin, Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { isDefinedAndNotNull } from '@core/utils';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -31,10 +32,12 @@ export class AttributeService {
) { } ) { }
public getEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, public getEntityAttributes(entityId: EntityId, attributeScope: AttributeScope,
config?: RequestConfig): Observable<Array<AttributeData>> { keys?: Array<string>, config?: RequestConfig): Observable<Array<AttributeData>> {
return this.http.get<Array<AttributeData>>(`/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes/` + let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes/${attributeScope}`;
`${attributeScope}`, if (keys && keys.length) {
defaultHttpOptionsFromConfig(config)); url += `?keys=${keys.join(',')}`;
}
return this.http.get<Array<AttributeData>>(url, defaultHttpOptionsFromConfig(config));
} }
public deleteEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array<AttributeData>, public deleteEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, attributes: Array<AttributeData>,
@ -58,7 +61,7 @@ export class AttributeService {
const attributesData: {[key: string]: any} = {}; const attributesData: {[key: string]: any} = {};
const deleteAttributes: AttributeData[] = []; const deleteAttributes: AttributeData[] = [];
attributes.forEach((attribute) => { attributes.forEach((attribute) => {
if (attribute.value !== null) { if (isDefinedAndNotNull(attribute.value)) {
attributesData[attribute.key] = attribute.value; attributesData[attribute.key] = attribute.value;
} else { } else {
deleteAttributes.push(attribute); deleteAttributes.push(attribute);
@ -85,7 +88,7 @@ export class AttributeService {
const timeseriesData: {[key: string]: any} = {}; const timeseriesData: {[key: string]: any} = {};
const deleteTimeseries: AttributeData[] = []; const deleteTimeseries: AttributeData[] = [];
timeseries.forEach((attribute) => { timeseries.forEach((attribute) => {
if (attribute.value !== null) { if (isDefinedAndNotNull(attribute.value)) {
timeseriesData[attribute.key] = attribute.value; timeseriesData[attribute.key] = attribute.value;
} else { } else {
deleteTimeseries.push(attribute); deleteTimeseries.push(attribute);

View File

@ -109,15 +109,20 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl
} }
private validate(): boolean { private validate(): boolean {
for (const resource of this.action.customResources) { if (this.action.customResources) {
if (!resource.url) { for (const resource of this.action.customResources) {
return false; if (!resource.url) {
return false;
}
} }
} }
return true; return true;
} }
public addResource() { public addResource() {
if (!this.action.customResources) {
this.action.customResources = [];
}
this.action.customResources.push({url: ''}); this.action.customResources.push({url: ''});
this.notifyActionUpdated(); this.notifyActionUpdated();
} }

View File

@ -28,7 +28,7 @@ import { PageLink } from '@shared/models/page/page-link';
import { catchError, map, publishReplay, refCount, share, take, tap } from 'rxjs/operators'; import { catchError, map, publishReplay, refCount, share, take, tap } from 'rxjs/operators';
import { entityTypeTranslations } from '@shared/models/entity-type.models'; import { entityTypeTranslations } from '@shared/models/entity-type.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { deepClone, isUndefined } from '@core/utils'; import { deepClone, isDefined, isUndefined } from '@core/utils';
import customSampleJs from '!raw-loader!./custom-sample-js.raw'; import customSampleJs from '!raw-loader!./custom-sample-js.raw';
import customSampleCss from '!raw-loader!./custom-sample-css.raw'; import customSampleCss from '!raw-loader!./custom-sample-css.raw';
@ -72,7 +72,7 @@ export function toCustomAction(action: WidgetActionDescriptorInfo): CustomAction
customFunction: action.customFunction customFunction: action.customFunction
}; };
} }
result.customResources = action ? deepClone(action.customResources) : []; result.customResources = action && isDefined(action.customResources) ? deepClone(action.customResources) : [];
return result; return result;
} }

View File

@ -102,8 +102,4 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
})); }));
} }
widgetForceReInit() {
this.ctx.widgetForceReInit();
}
} }

View File

@ -304,7 +304,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
const dataKeys: Array<DataKey> = []; const dataKeys: Array<DataKey> = [];
const datasource = this.subscription.datasources[0]; const datasource = this.subscription.options.datasources ? this.subscription.options.datasources[0] : null;
if (datasource) { if (datasource) {
datasource.dataKeys.forEach((entityDataKey) => { datasource.dataKeys.forEach((entityDataKey) => {

View File

@ -263,7 +263,6 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.widgetContext.servicesMap = ServicesMap; this.widgetContext.servicesMap = ServicesMap;
this.widgetContext.isEdit = this.isEdit; this.widgetContext.isEdit = this.isEdit;
this.widgetContext.isMobile = this.isMobile; this.widgetContext.isMobile = this.isMobile;
this.widgetContext.widgetForceReInit = this.reInit.bind(this);
this.widgetContext.subscriptionApi = { this.widgetContext.subscriptionApi = {
createSubscription: this.createSubscription.bind(this), createSubscription: this.createSubscription.bind(this),
@ -1096,7 +1095,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.cssParser.createStyleElement(actionNamespace, customCss, 'nonamespace'); this.cssParser.createStyleElement(actionNamespace, customCss, 'nonamespace');
} }
const resourceTasks: Observable<string>[] = []; const resourceTasks: Observable<string>[] = [];
if (customResources.length > 0) { if (isDefined(customResources) && customResources.length > 0) {
customResources.forEach((resource) => { customResources.forEach((resource) => {
resourceTasks.push( resourceTasks.push(
this.resources.loadResource(resource.url).pipe( this.resources.loadResource(resource.url).pipe(

View File

@ -14,7 +14,6 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ExceptionData } from '@shared/models/error.models';
import { IDashboardComponent } from '@home/models/dashboard-component.models'; import { IDashboardComponent } from '@home/models/dashboard-component.models';
import { import {
DataSet, DataSet,
@ -51,7 +50,7 @@ import { AssetService } from '@app/core/http/asset.service';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service';
import { isDefined, formatValue } from '@core/utils'; import { isDefined, formatValue } from '@core/utils';
import { Observable, of, ReplaySubject } from 'rxjs'; import { forkJoin, Observable, of, ReplaySubject } from 'rxjs';
import { WidgetSubscription } from '@core/api/widget-subscription'; import { WidgetSubscription } from '@core/api/widget-subscription';
export interface IWidgetAction { export interface IWidgetAction {
@ -161,8 +160,6 @@ export class WidgetContext {
isEdit: boolean; isEdit: boolean;
isMobile: boolean; isMobile: boolean;
widgetForceReInit?: () => void;
widgetNamespace?: string; widgetNamespace?: string;
subscriptionApi?: WidgetSubscriptionApi; subscriptionApi?: WidgetSubscriptionApi;
@ -187,6 +184,11 @@ export class WidgetContext {
ngZone?: NgZone; ngZone?: NgZone;
rxjs = {
forkJoin,
of
};
detectChanges(updateWidgetParams: boolean = false) { detectChanges(updateWidgetParams: boolean = false) {
if (!this.destroyed) { if (!this.destroyed) {
if (updateWidgetParams) { if (updateWidgetParams) {
@ -208,11 +210,14 @@ export class WidgetContext {
} }
} }
updateAliases(aliasIds?: Array<string>) {
this.aliasController.updateAliases(aliasIds);
}
reset() { reset() {
this.destroyed = false; this.destroyed = false;
this.hideTitlePanel = false; this.hideTitlePanel = false;
this.widgetTitle = undefined; this.widgetTitle = undefined;
this.customHeaderActions = undefined;
this.widgetActions = undefined; this.widgetActions = undefined;
} }
} }
@ -226,7 +231,6 @@ export interface IDynamicWidgetComponent {
rpcErrorText: string; rpcErrorText: string;
rpcRejection: HttpErrorResponse; rpcRejection: HttpErrorResponse;
raf: RafService; raf: RafService;
widgetForceReInit(): void;
[key: string]: any; [key: string]: any;
} }