Browse Source

UI: widget extension module support in custom actions

pull/8438/head
Chantsova Ekaterina 4 years ago
parent
commit
415c95907b
  1. 6
      ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html
  2. 9
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts
  3. 53
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  4. 3
      ui-ngx/src/app/shared/models/widget.models.ts

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

@ -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"
@ -90,7 +94,7 @@
<tb-js-func <tb-js-func
[(ngModel)]="action.customFunction" [(ngModel)]="action.customFunction"
(ngModelChange)="notifyActionUpdated()" (ngModelChange)="notifyActionUpdated()"
[functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel']" [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', 'customModules']"
[disableUndefinedCheck]="true" [disableUndefinedCheck]="true"
[validationArgs]="[]" [validationArgs]="[]"
[editorCompleter]="customPrettyActionEditorCompleter" [editorCompleter]="customPrettyActionEditorCompleter"

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

@ -29,6 +29,7 @@ import {
} from '@home/components/widget/dialog/custom-dialog-container.component'; } from '@home/components/widget/dialog/custom-dialog-container.component';
import { SHARED_MODULE_TOKEN } from '@shared/components/tokens'; import { SHARED_MODULE_TOKEN } from '@shared/components/tokens';
import { HOME_COMPONENTS_MODULE_TOKEN, SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { HOME_COMPONENTS_MODULE_TOKEN, SHARED_HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
import { isObject } from '@core/utils';
@Injectable() @Injectable()
export class CustomDialogService { export class CustomDialogService {
@ -46,10 +47,12 @@ export class CustomDialogService {
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 (isObject(data) && data.hasOwnProperty('customModules')) {
modules.push(data.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,

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

@ -29,8 +29,10 @@ import {
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
Optional,
Renderer2, Renderer2,
SimpleChanges, SimpleChanges,
Type,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
ViewEncapsulation ViewEncapsulation
@ -90,7 +92,7 @@ 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 { ResourcesService } from '@core/services/resources.service';
import { catchError, map, switchMap } from 'rxjs/operators'; import { catchError, map, mergeMap, 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';
import { DeviceService } from '@app/core/http/device.service'; import { DeviceService } from '@app/core/http/device.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,7 +1165,7 @@ 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(
() => { () => {
if (isDefined(customPrettyFunction) && customPrettyFunction.length > 0) { if (isDefined(customPrettyFunction) && customPrettyFunction.length > 0) {
try { try {
@ -1168,8 +1173,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
additionalParams = {}; additionalParams = {};
} }
const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId', const customActionPrettyFunction = new Function('$event', 'widgetContext', 'entityId',
'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', customPrettyFunction); 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel', 'customModules', customPrettyFunction);
customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel); customActionPrettyFunction($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel, descriptor.customModules);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@ -1473,20 +1478,54 @@ 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> {
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>[] = []; const resourceTasks: Observable<string>[] = [];
const modulesTasks: Observable<Type<any>[] | string>[] = [];
if (isDefined(customResources) && customResources.length > 0) { if (isDefined(customResources) && customResources.length > 0) {
customResources.forEach((resource) => { customResources.filter(r => r.isModule).forEach(
(resource) => {
modulesTasks.push(
this.resources.loadModules(resource.url, this.modulesMap).pipe(
catchError((e: Error) => of(e?.message ? e.message : `Failed to load custom action resource module: '${resource.url}'`))
)
);
}
);
customResources.filter(r => !r.isModule).forEach((resource) => {
resourceTasks.push( resourceTasks.push(
this.resources.loadResource(resource.url).pipe( this.resources.loadResource(resource.url).pipe(
catchError(e => of(`Failed to load custom action resource: '${resource.url}'`)) catchError(e => of(`Failed to load custom action resource: '${resource.url}'`))
) )
); );
}); });
if (modulesTasks.length) {
let 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 {
return (res as Type<any>[][]).flat();
}
})
);
resourceTasks.push(modulesObservable.pipe(
mergeMap((resolvedModules) => {
if (typeof resolvedModules === 'string') {
return of(resolvedModules);
} else {
actionDescriptor.customModules = resolvedModules;
return of(null);
}
})));
}
return forkJoin(resourceTasks).pipe( return forkJoin(resourceTasks).pipe(
switchMap(msgs => { switchMap(msgs => {
let errors: string[]; let errors: string[];
@ -1499,7 +1538,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
return of(null); return of(null);
} }
} }
)); ));
} else { } else {
return of(null); return of(null);
} }

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

@ -26,7 +26,7 @@ import * as moment_ from 'moment';
import { EntityDataPageLink, EntityFilter, KeyFilter } from '@shared/models/query/query.models'; import { EntityDataPageLink, EntityFilter, KeyFilter } 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';
@ -550,6 +550,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…
Cancel
Save