UI: Widget settings forms support for extensions
This commit is contained in:
parent
c44727851d
commit
33e5b033ec
@ -60,21 +60,23 @@ module.exports = (config, options) => {
|
||||
);
|
||||
|
||||
const index = config.plugins.findIndex(p => p instanceof ngWebpack.ivy.AngularWebpackPlugin || p instanceof ngWebpack.AngularWebpackPlugin);
|
||||
const angularWebpackPlugin = config.plugins[index];
|
||||
|
||||
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, keysTransformer);
|
||||
let angularWebpackPlugin = config.plugins[index];
|
||||
|
||||
if (config.mode === 'production') {
|
||||
const angularCompilerOptions = angularWebpackPlugin.pluginOptions;
|
||||
angularCompilerOptions.emitClassMetadata = true;
|
||||
angularCompilerOptions.emitNgModuleScope = true;
|
||||
config.plugins.splice(index, 1);
|
||||
config.plugins.push(new ngWebpack.ivy.AngularWebpackPlugin(angularCompilerOptions));
|
||||
angularWebpackPlugin = new ngWebpack.ivy.AngularWebpackPlugin(angularCompilerOptions);
|
||||
config.plugins.push(angularWebpackPlugin);
|
||||
const javascriptOptimizerOptions = config.optimization.minimizer[1].options;
|
||||
delete javascriptOptimizerOptions.define.ngJitMode;
|
||||
config.optimization.minimizer.splice(1, 1);
|
||||
config.optimization.minimizer.push(new JavaScriptOptimizerPlugin(javascriptOptimizerOptions));
|
||||
}
|
||||
|
||||
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, keysTransformer);
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
|
||||
@ -219,7 +219,7 @@ export class RuleChainService {
|
||||
map((res) => {
|
||||
if (nodeDefinition.configDirective && nodeDefinition.configDirective.length) {
|
||||
const selector = snakeCase(nodeDefinition.configDirective, '-');
|
||||
const componentFactory = res.find((factory) =>
|
||||
const componentFactory = res.factories.find((factory) =>
|
||||
factory.selector === selector);
|
||||
if (componentFactory) {
|
||||
this.ruleNodeConfigFactories[nodeDefinition.configDirective] = componentFactory;
|
||||
|
||||
@ -30,6 +30,11 @@ import { IModulesMap } from '@modules/common/modules-map.models';
|
||||
|
||||
declare const System;
|
||||
|
||||
export interface ModulesWithFactories {
|
||||
modules: Type<any>[];
|
||||
factories: ComponentFactory<any>[];
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@ -37,7 +42,7 @@ export class ResourcesService {
|
||||
|
||||
private loadedResources: { [url: string]: ReplaySubject<any> } = {};
|
||||
private loadedModules: { [url: string]: ReplaySubject<Type<any>[]> } = {};
|
||||
private loadedFactories: { [url: string]: ReplaySubject<ComponentFactory<any>[]> } = {};
|
||||
private loadedModulesAndFactories: { [url: string]: ReplaySubject<ModulesWithFactories> } = {};
|
||||
|
||||
private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0];
|
||||
|
||||
@ -64,13 +69,13 @@ export class ResourcesService {
|
||||
return this.loadResourceByType(fileType, url);
|
||||
}
|
||||
|
||||
public loadFactories(url: string, modulesMap: IModulesMap): Observable<ComponentFactory<any>[]> {
|
||||
if (this.loadedFactories[url]) {
|
||||
return this.loadedFactories[url].asObservable();
|
||||
public loadFactories(url: string, modulesMap: IModulesMap): Observable<ModulesWithFactories> {
|
||||
if (this.loadedModulesAndFactories[url]) {
|
||||
return this.loadedModulesAndFactories[url].asObservable();
|
||||
}
|
||||
modulesMap.init();
|
||||
const subject = new ReplaySubject<ComponentFactory<any>[]>();
|
||||
this.loadedFactories[url] = subject;
|
||||
const subject = new ReplaySubject<ModulesWithFactories>();
|
||||
this.loadedModulesAndFactories[url] = subject;
|
||||
import('@angular/compiler').then(
|
||||
() => {
|
||||
System.import(url).then(
|
||||
@ -88,25 +93,29 @@ export class ResourcesService {
|
||||
c.ngModuleFactory.create(this.injector);
|
||||
componentFactories.push(...c.componentFactories);
|
||||
}
|
||||
this.loadedFactories[url].next(componentFactories);
|
||||
this.loadedFactories[url].complete();
|
||||
const modulesWithFactories: ModulesWithFactories = {
|
||||
modules,
|
||||
factories: componentFactories
|
||||
};
|
||||
this.loadedModulesAndFactories[url].next(modulesWithFactories);
|
||||
this.loadedModulesAndFactories[url].complete();
|
||||
} catch (e) {
|
||||
this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
|
||||
delete this.loadedFactories[url];
|
||||
this.loadedModulesAndFactories[url].error(new Error(`Unable to init module from url: ${url}`));
|
||||
delete this.loadedModulesAndFactories[url];
|
||||
}
|
||||
},
|
||||
(e) => {
|
||||
this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
|
||||
delete this.loadedFactories[url];
|
||||
this.loadedModulesAndFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
|
||||
delete this.loadedModulesAndFactories[url];
|
||||
});
|
||||
} else {
|
||||
this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
|
||||
delete this.loadedFactories[url];
|
||||
this.loadedModulesAndFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
|
||||
delete this.loadedModulesAndFactories[url];
|
||||
}
|
||||
},
|
||||
(e) => {
|
||||
this.loadedFactories[url].error(new Error(`Unable to load module from url: ${url}`));
|
||||
delete this.loadedFactories[url];
|
||||
this.loadedModulesAndFactories[url].error(new Error(`Unable to load module from url: ${url}`));
|
||||
delete this.loadedModulesAndFactories[url];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ export class LedIndicatorWidgetSettingsComponent extends WidgetSettingsComponent
|
||||
functionScopeVariables = this.widgetService.getWidgetScopeVariables();
|
||||
|
||||
get targetDeviceAliasId(): string {
|
||||
const aliasIds = this.widget.config.targetDeviceAliasIds;
|
||||
const aliasIds = this.widget?.config?.targetDeviceAliasIds;
|
||||
if (aliasIds && aliasIds.length) {
|
||||
return aliasIds[0];
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export class RoundSwitchWidgetSettingsComponent extends WidgetSettingsComponent
|
||||
}
|
||||
|
||||
get targetDeviceAliasId(): string {
|
||||
const aliasIds = this.widget.config.targetDeviceAliasIds;
|
||||
const aliasIds = this.widget?.config?.targetDeviceAliasIds;
|
||||
if (aliasIds && aliasIds.length) {
|
||||
return aliasIds[0];
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export class SlideToggleWidgetSettingsComponent extends WidgetSettingsComponent
|
||||
}
|
||||
|
||||
get targetDeviceAliasId(): string {
|
||||
const aliasIds = this.widget.config.targetDeviceAliasIds;
|
||||
const aliasIds = this.widget?.config?.targetDeviceAliasIds;
|
||||
if (aliasIds && aliasIds.length) {
|
||||
return aliasIds[0];
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export class SwitchControlWidgetSettingsComponent extends WidgetSettingsComponen
|
||||
}
|
||||
|
||||
get targetDeviceAliasId(): string {
|
||||
const aliasIds = this.widget.config.targetDeviceAliasIds;
|
||||
const aliasIds = this.widget?.config?.targetDeviceAliasIds;
|
||||
if (aliasIds && aliasIds.length) {
|
||||
return aliasIds[0];
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Inject, Injectable, Optional, Type } from '@angular/core';
|
||||
import { ComponentFactory, Inject, Injectable, Optional, Type } from '@angular/core';
|
||||
import { DynamicComponentFactoryService } from '@core/services/dynamic-component-factory.service';
|
||||
import { WidgetService } from '@core/http/widget.service';
|
||||
import { forkJoin, from, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs';
|
||||
@ -28,7 +28,7 @@ import {
|
||||
} from '@home/models/widget-component.models';
|
||||
import cssjs from '@core/css/css';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { ResourcesService } from '@core/services/resources.service';
|
||||
import { ModulesWithFactories, ResourcesService } from '@core/services/resources.service';
|
||||
import { Widget, widgetActionSources, WidgetControllerDescriptor, WidgetType } from '@shared/models/widget.models';
|
||||
import { catchError, map, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { isFunction, isUndefined } from '@core/utils';
|
||||
@ -46,6 +46,7 @@ import * as tinycolor_ from 'tinycolor2';
|
||||
import moment from 'moment';
|
||||
import { IModulesMap } from '@modules/common/modules-map.models';
|
||||
import { HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens';
|
||||
import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module';
|
||||
|
||||
const tinycolor = tinycolor_;
|
||||
|
||||
@ -314,12 +315,12 @@ export class WidgetComponentService {
|
||||
this.cssParser.cssPreviewNamespace = widgetNamespace;
|
||||
this.cssParser.createStyleElement(widgetNamespace, widgetInfo.templateCss);
|
||||
const resourceTasks: Observable<string>[] = [];
|
||||
const modulesTasks: Observable<Type<any>[] | string>[] = [];
|
||||
const modulesTasks: Observable<ModulesWithFactories | string>[] = [];
|
||||
if (widgetInfo.resources.length > 0) {
|
||||
widgetInfo.resources.filter(r => r.isModule).forEach(
|
||||
(resource) => {
|
||||
modulesTasks.push(
|
||||
this.resources.loadModules(resource.url, this.modulesMap).pipe(
|
||||
this.resources.loadFactories(resource.url, this.modulesMap).pipe(
|
||||
catchError((e: Error) => of(e?.message ? e.message : `Failed to load widget resource module: '${resource.url}'`))
|
||||
)
|
||||
);
|
||||
@ -336,7 +337,7 @@ export class WidgetComponentService {
|
||||
}
|
||||
);
|
||||
|
||||
let modulesObservable: Observable<string | Type<any>[]>;
|
||||
let modulesObservable: Observable<string | ModulesWithFactories>;
|
||||
if (modulesTasks.length) {
|
||||
modulesObservable = forkJoin(modulesTasks).pipe(
|
||||
map(res => {
|
||||
@ -344,16 +345,20 @@ export class WidgetComponentService {
|
||||
if (msg) {
|
||||
return msg as string;
|
||||
} else {
|
||||
let resModules = (res as Type<any>[][]).flat();
|
||||
const modulesWithFactoriesList = res as ModulesWithFactories[];
|
||||
const resModulesWithFactories: ModulesWithFactories = {
|
||||
modules: modulesWithFactoriesList.map(mf => mf.modules).flat(),
|
||||
factories: modulesWithFactoriesList.map(mf => mf.factories).flat()
|
||||
};
|
||||
if (modules && modules.length) {
|
||||
resModules = resModules.concat(modules);
|
||||
resModulesWithFactories.modules.concat(modules);
|
||||
}
|
||||
return resModules;
|
||||
return resModulesWithFactories;
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
modulesObservable = modules && modules.length ? of(modules) : of([]);
|
||||
modulesObservable = modules && modules.length ? of({modules, factories: []}) : of({modules: [], factories: []});
|
||||
}
|
||||
|
||||
resourceTasks.push(
|
||||
@ -362,10 +367,11 @@ export class WidgetComponentService {
|
||||
if (typeof resolvedModules === 'string') {
|
||||
return of(resolvedModules);
|
||||
} else {
|
||||
this.registerWidgetSettingsForms(widgetInfo, resolvedModules.factories);
|
||||
return this.dynamicComponentFactoryService.createDynamicComponentFactory(
|
||||
class DynamicWidgetComponentInstance extends DynamicWidgetComponent {},
|
||||
widgetInfo.templateHtml,
|
||||
resolvedModules
|
||||
resolvedModules.modules
|
||||
).pipe(
|
||||
map((factory) => {
|
||||
widgetInfo.componentFactory = factory;
|
||||
@ -395,6 +401,25 @@ export class WidgetComponentService {
|
||||
));
|
||||
}
|
||||
|
||||
private registerWidgetSettingsForms(widgetInfo: WidgetInfo, factories: ComponentFactory<any>[]) {
|
||||
const directives: string[] = [];
|
||||
if (widgetInfo.settingsDirective && widgetInfo.settingsDirective.length) {
|
||||
directives.push(widgetInfo.settingsDirective);
|
||||
}
|
||||
if (widgetInfo.dataKeySettingsDirective && widgetInfo.dataKeySettingsDirective.length) {
|
||||
directives.push(widgetInfo.dataKeySettingsDirective);
|
||||
}
|
||||
if (widgetInfo.latestDataKeySettingsDirective && widgetInfo.latestDataKeySettingsDirective.length) {
|
||||
directives.push(widgetInfo.latestDataKeySettingsDirective);
|
||||
}
|
||||
if (directives.length) {
|
||||
factories.filter((factory) => directives.includes(factory.selector))
|
||||
.forEach((foundFactory) => {
|
||||
widgetSettingsComponentsMap[foundFactory.selector] = foundFactory.componentType;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private createWidgetControllerDescriptor(widgetInfo: WidgetInfo, name: string): WidgetControllerDescriptor {
|
||||
let widgetTypeFunctionBody = `return function _${name} (ctx) {\n` +
|
||||
' var self = this;\n' +
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user