Merge pull request #8438 from ChantsovaEkaterina/feature/custom-action-extension-module-support
Widget extension module support in custom action
This commit is contained in:
commit
e4bd615b82
@ -27,6 +27,10 @@
|
|||||||
(ngModelChange)="notifyActionUpdated()"
|
(ngModelChange)="notifyActionUpdated()"
|
||||||
placeholder="{{ 'widget.resource-url' | translate }}"/>
|
placeholder="{{ 'widget.resource-url' | translate }}"/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<mat-checkbox [(ngModel)]="resource.isModule"
|
||||||
|
(ngModelChange)="notifyActionUpdated()">
|
||||||
|
{{ 'widget.resource-is-module' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
<button mat-icon-button color="primary"
|
<button mat-icon-button color="primary"
|
||||||
[disabled]="isLoading$ | async"
|
[disabled]="isLoading$ | async"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -33,6 +33,8 @@ import { HOME_COMPONENTS_MODULE_TOKEN, SHARED_HOME_COMPONENTS_MODULE_TOKEN } fro
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class CustomDialogService {
|
export class CustomDialogService {
|
||||||
|
|
||||||
|
private customModules: Array<Type<any>>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
private authService: AuthService,
|
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,
|
customDialog(template: string, controller: (instance: CustomDialogComponent) => void, data?: any,
|
||||||
config?: MatDialogConfig): Observable<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(
|
return this.dynamicComponentFactoryService.createDynamicComponentFactory(
|
||||||
class CustomDialogComponentInstance extends CustomDialogComponent {},
|
class CustomDialogComponentInstance extends CustomDialogComponent {}, template, modules).pipe(
|
||||||
template,
|
|
||||||
[this.sharedModule, CommonModule, this.sharedHomeComponentsModule, this.homeComponentsModule]).pipe(
|
|
||||||
mergeMap((factory) => {
|
mergeMap((factory) => {
|
||||||
const dialogData: CustomDialogContainerData = {
|
const dialogData: CustomDialogContainerData = {
|
||||||
controller,
|
controller,
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-t
|
|||||||
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
|
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
|
||||||
import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component';
|
import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component';
|
||||||
import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entities-hierarchy-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 { RpcWidgetsModule } from '@home/components/widget/lib/rpc/rpc-widgets.module';
|
||||||
import {
|
import {
|
||||||
DateRangeNavigatorPanelComponent,
|
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 { TripAnimationComponent } from '@home/components/widget/trip-animation/trip-animation.component';
|
||||||
import { PhotoCameraInputWidgetComponent } from '@home/components/widget/lib/photo-camera-input.component';
|
import { PhotoCameraInputWidgetComponent } from '@home/components/widget/lib/photo-camera-input.component';
|
||||||
import { GatewayFormComponent } from '@home/components/widget/lib/gateway/gateway-form.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 { NavigationCardsWidgetComponent } from '@home/components/widget/lib/navigation-cards-widget.component';
|
||||||
import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navigation-card-widget.component';
|
import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navigation-card-widget.component';
|
||||||
import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-overview-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
|
MarkdownWidgetComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CustomDialogService,
|
|
||||||
ImportExportService,
|
|
||||||
{provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule }
|
{provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -29,8 +29,10 @@ import {
|
|||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
|
Optional,
|
||||||
Renderer2,
|
Renderer2,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
|
Type,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
@ -89,7 +91,7 @@ import {
|
|||||||
import { EntityId } from '@shared/models/id/entity-id';
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import cssjs from '@core/css/css';
|
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 { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
import { ActionNotificationShow } from '@core/notification/notification.actions';
|
import { ActionNotificationShow } from '@core/notification/notification.actions';
|
||||||
import { TimeService } from '@core/services/time.service';
|
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 { PopoverPlacement } from '@shared/components/popover.models';
|
||||||
import { TbPopoverService } from '@shared/components/popover.service';
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { DASHBOARD_PAGE_COMPONENT_TOKEN } from '@home/components/tokens';
|
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({
|
@Component({
|
||||||
selector: 'tb-widget',
|
selector: 'tb-widget',
|
||||||
@ -190,6 +194,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
|||||||
private popoverService: TbPopoverService,
|
private popoverService: TbPopoverService,
|
||||||
@Inject(EMBED_DASHBOARD_DIALOG_TOKEN) private embedDashboardDialogComponent: ComponentType<any>,
|
@Inject(EMBED_DASHBOARD_DIALOG_TOKEN) private embedDashboardDialogComponent: ComponentType<any>,
|
||||||
@Inject(DASHBOARD_PAGE_COMPONENT_TOKEN) private dashboardPageComponent: ComponentType<any>,
|
@Inject(DASHBOARD_PAGE_COMPONENT_TOKEN) private dashboardPageComponent: ComponentType<any>,
|
||||||
|
@Optional() @Inject(MODULES_MAP) private modulesMap: IModulesMap,
|
||||||
private widgetService: WidgetService,
|
private widgetService: WidgetService,
|
||||||
private resources: ResourcesService,
|
private resources: ResourcesService,
|
||||||
private timeService: TimeService,
|
private timeService: TimeService,
|
||||||
@ -1160,8 +1165,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
|||||||
if (isDefined(customHtml) && customHtml.length > 0) {
|
if (isDefined(customHtml) && customHtml.length > 0) {
|
||||||
htmlTemplate = customHtml;
|
htmlTemplate = customHtml;
|
||||||
}
|
}
|
||||||
this.loadCustomActionResources(actionNamespace, customCss, customResources).subscribe(
|
this.loadCustomActionResources(actionNamespace, customCss, customResources, descriptor).subscribe({
|
||||||
() => {
|
next: () => {
|
||||||
if (isDefined(customPrettyFunction) && customPrettyFunction.length > 0) {
|
if (isDefined(customPrettyFunction) && customPrettyFunction.length > 0) {
|
||||||
try {
|
try {
|
||||||
if (!additionalParams) {
|
if (!additionalParams) {
|
||||||
@ -1169,16 +1174,17 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
|
|||||||
}
|
}
|
||||||
const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId',
|
const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId',
|
||||||
'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction);
|
'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction);
|
||||||
|
this.widgetContext.customDialog.setAdditionalModules(descriptor.customModules);
|
||||||
customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel);
|
customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(errorMessages: string[]) => {
|
error: (errorMessages: string[]) => {
|
||||||
this.processResourcesLoadErrors(errorMessages);
|
this.processResourcesLoadErrors(errorMessages);
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
break;
|
break;
|
||||||
case WidgetActionType.mobileAction:
|
case WidgetActionType.mobileAction:
|
||||||
const mobileAction = descriptor.mobileAction;
|
const mobileAction = descriptor.mobileAction;
|
||||||
@ -1473,32 +1479,68 @@ 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) {
|
if (isDefined(customCss) && customCss.length > 0) {
|
||||||
this.cssParser.cssPreviewNamespace = actionNamespace;
|
this.cssParser.cssPreviewNamespace = actionNamespace;
|
||||||
this.cssParser.createStyleElement(actionNamespace, customCss, 'nonamespace');
|
this.cssParser.createStyleElement(actionNamespace, customCss, 'nonamespace');
|
||||||
}
|
}
|
||||||
const resourceTasks: Observable<string>[] = [];
|
|
||||||
if (isDefined(customResources) && customResources.length > 0) {
|
if (isDefined(customResources) && customResources.length > 0) {
|
||||||
customResources.forEach((resource) => {
|
customResources.forEach(resource => {
|
||||||
resourceTasks.push(
|
if (resource.isModule) {
|
||||||
this.resources.loadResource(resource.url).pipe(
|
modulesTasks.push(
|
||||||
catchError(e => of(`Failed to load custom action resource: '${resource.url}'`))
|
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}'`))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
resourceTasks.push(modulesObservable.pipe(
|
||||||
|
map((resolvedModules) => {
|
||||||
|
if (typeof resolvedModules === 'string') {
|
||||||
|
return resolvedModules;
|
||||||
|
} else {
|
||||||
|
actionDescriptor.customModules = resolvedModules;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
return forkJoin(resourceTasks).pipe(
|
return forkJoin(resourceTasks).pipe(
|
||||||
switchMap(msgs => {
|
switchMap(msgs => {
|
||||||
let errors: string[];
|
const errors = msgs.filter(msg => msg && msg.length > 0);
|
||||||
if (msgs && msgs.length) {
|
if (errors.length > 0) {
|
||||||
errors = msgs.filter(msg => msg && msg.length > 0);
|
return throwError(() => errors);
|
||||||
}
|
|
||||||
if (errors && errors.length) {
|
|
||||||
return throwError(errors);
|
|
||||||
} else {
|
} else {
|
||||||
return of(null);
|
return of(null);
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
return of(null);
|
return of(null);
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import {
|
|||||||
} from '@shared/models/query/query.models';
|
} from '@shared/models/query/query.models';
|
||||||
import { PopoverPlacement } from '@shared/components/popover.models';
|
import { PopoverPlacement } from '@shared/components/popover.models';
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
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 { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
||||||
@ -560,6 +560,7 @@ export interface CustomActionDescriptor {
|
|||||||
customResources?: Array<WidgetResource>;
|
customResources?: Array<WidgetResource>;
|
||||||
customHtml?: string;
|
customHtml?: string;
|
||||||
customCss?: string;
|
customCss?: string;
|
||||||
|
customModules?: Type<any>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WidgetActionDescriptor extends CustomActionDescriptor {
|
export interface WidgetActionDescriptor extends CustomActionDescriptor {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user