Commit ddb3ee61e74c52bda6eef1ec5e6a8f0e2cf88f65

Authored by Igor Kulikov
1 parent 674512dd

UI: Optimizations - AOT + JIT

@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 "main": "src/main.ts", 22 "main": "src/main.ts",
23 "polyfills": "src/polyfills.ts", 23 "polyfills": "src/polyfills.ts",
24 "tsConfig": "src/tsconfig.app.json", 24 "tsConfig": "src/tsconfig.app.json",
  25 + "aot": true,
25 "assets": [ 26 "assets": [
26 "src/thingsboard.ico", 27 "src/thingsboard.ico",
27 "src/assets", 28 "src/assets",
@@ -161,6 +162,7 @@ @@ -161,6 +162,7 @@
161 "serve": { 162 "serve": {
162 "builder": "@angular-builders/custom-webpack:dev-server", 163 "builder": "@angular-builders/custom-webpack:dev-server",
163 "options": { 164 "options": {
  165 + "aot": true,
164 "browserTarget": "thingsboard:build", 166 "browserTarget": "thingsboard:build",
165 "proxyConfig": "proxy.conf.js" 167 "proxyConfig": "proxy.conf.js"
166 }, 168 },
@@ -14,8 +14,10 @@ @@ -14,8 +14,10 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 const CompressionPlugin = require("compression-webpack-plugin"); 16 const CompressionPlugin = require("compression-webpack-plugin");
  17 +const TerserPlugin = require("terser-webpack-plugin");
17 const webpack = require("webpack"); 18 const webpack = require("webpack");
18 const dirTree = require("directory-tree"); 19 const dirTree = require("directory-tree");
  20 +const AngularCompilerPlugin = require('@ngtools/webpack');
19 21
20 var langs = []; 22 var langs = [];
21 23
@@ -25,12 +27,14 @@ dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => { @@ -25,12 +27,14 @@ dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => {
25 langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5)); 27 langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5));
26 }); 28 });
27 29
28 -module.exports = {  
29 - plugins: [ 30 +module.exports = (config, options) => {
  31 + config.plugins.push(
30 new webpack.DefinePlugin({ 32 new webpack.DefinePlugin({
31 TB_VERSION: JSON.stringify(require("./package.json").version), 33 TB_VERSION: JSON.stringify(require("./package.json").version),
32 SUPPORTED_LANGS: JSON.stringify(langs), 34 SUPPORTED_LANGS: JSON.stringify(langs),
33 - }), 35 + })
  36 + );
  37 + config.plugins.push(
34 new CompressionPlugin({ 38 new CompressionPlugin({
35 filename: "[path][base].gz[query]", 39 filename: "[path][base].gz[query]",
36 algorithm: "gzip", 40 algorithm: "gzip",
@@ -38,6 +42,21 @@ module.exports = { @@ -38,6 +42,21 @@ module.exports = {
38 threshold: 10240, 42 threshold: 10240,
39 minRatio: 0.8, 43 minRatio: 0.8,
40 deleteOriginalAssets: false, 44 deleteOriginalAssets: false,
41 - }),  
42 - ], 45 + })
  46 + );
  47 +
  48 + if (config.mode === 'production') {
  49 + const index = config.plugins.findIndex(p => p instanceof AngularCompilerPlugin.AngularCompilerPlugin);
  50 + const angularCompilerOptions = config.plugins[index]._options;
  51 + angularCompilerOptions.emitClassMetadata = true;
  52 + angularCompilerOptions.emitNgModuleScope = true;
  53 + config.plugins.splice(index, 1);
  54 + config.plugins.push(new AngularCompilerPlugin.AngularCompilerPlugin(angularCompilerOptions));
  55 + const terserPluginOptions = config.optimization.minimizer[1].options;
  56 + delete terserPluginOptions.terserOptions.compress.global_defs.ngJitMode;
  57 + terserPluginOptions.terserOptions.compress.side_effects = false;
  58 + config.optimization.minimizer.splice(1, 1);
  59 + config.optimization.minimizer.push(new TerserPlugin(terserPluginOptions));
  60 + }
  61 + return config;
43 }; 62 };
@@ -100,6 +100,7 @@ @@ -100,6 +100,7 @@
100 "@angular/cli": "^10.1.5", 100 "@angular/cli": "^10.1.5",
101 "@angular/compiler-cli": "^10.1.5", 101 "@angular/compiler-cli": "^10.1.5",
102 "@angular/language-service": "^10.1.5", 102 "@angular/language-service": "^10.1.5",
  103 + "@ngtools/webpack": "10.1.5",
103 "@types/canvas-gauges": "^2.1.2", 104 "@types/canvas-gauges": "^2.1.2",
104 "@types/flot": "^0.0.31", 105 "@types/flot": "^0.0.31",
105 "@types/jasmine": "^3.5.12", 106 "@types/jasmine": "^3.5.12",
@@ -446,8 +446,12 @@ export class AuthService { @@ -446,8 +446,12 @@ export class AuthService {
446 const refreshTokenValid = AuthService.isTokenValid('refresh_token'); 446 const refreshTokenValid = AuthService.isTokenValid('refresh_token');
447 this.setUserFromJwtToken(null, null, false); 447 this.setUserFromJwtToken(null, null, false);
448 if (!refreshTokenValid) { 448 if (!refreshTokenValid) {
449 - this.refreshTokenSubject.error(new Error(this.translate.instant('access.refresh-token-expired')));  
450 - this.refreshTokenSubject = null; 449 + this.translate.get('access.refresh-token-expired').subscribe(
  450 + (translation) => {
  451 + this.refreshTokenSubject.error(new Error(translation));
  452 + this.refreshTokenSubject = null;
  453 + }
  454 + );
451 } else { 455 } else {
452 const refreshTokenRequest = { 456 const refreshTokenRequest = {
453 refreshToken 457 refreshToken
@@ -59,37 +59,40 @@ export class DynamicComponentFactoryService { @@ -59,37 +59,40 @@ export class DynamicComponentFactoryService {
59 template: string, 59 template: string,
60 modules?: Type<any>[]): Observable<ComponentFactory<T>> { 60 modules?: Type<any>[]): Observable<ComponentFactory<T>> {
61 const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>(); 61 const dymamicComponentFactorySubject = new ReplaySubject<ComponentFactory<T>>();
62 - const comp = this.createDynamicComponent(componentType, template);  
63 - let moduleImports: Type<any>[] = [CommonModule];  
64 - if (modules) {  
65 - moduleImports = [...moduleImports, ...modules];  
66 - }  
67 - // noinspection AngularInvalidImportedOrDeclaredSymbol  
68 - @NgModule({  
69 - declarations: [comp],  
70 - imports: moduleImports  
71 - })  
72 - class DynamicComponentInstanceModule extends DynamicComponentModule {}  
73 - try {  
74 - this.compiler.compileModuleAsync(DynamicComponentInstanceModule).then(  
75 - (module) => {  
76 - const moduleRef = module.create(this.injector);  
77 - const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);  
78 - this.dynamicComponentModulesMap.set(factory, {  
79 - moduleRef,  
80 - moduleType: module.moduleType  
81 - });  
82 - dymamicComponentFactorySubject.next(factory);  
83 - dymamicComponentFactorySubject.complete(); 62 + import('@angular/compiler').then(
  63 + () => {
  64 + const comp = this.createDynamicComponent(componentType, template);
  65 + let moduleImports: Type<any>[] = [CommonModule];
  66 + if (modules) {
  67 + moduleImports = [...moduleImports, ...modules];
84 } 68 }
85 - ).catch(  
86 - (e) => { 69 + // noinspection AngularInvalidImportedOrDeclaredSymbol
  70 + const dynamicComponentInstanceModule = NgModule({
  71 + declarations: [comp],
  72 + imports: moduleImports
  73 + })(class DynamicComponentInstanceModule extends DynamicComponentModule {});
  74 + try {
  75 + this.compiler.compileModuleAsync(dynamicComponentInstanceModule).then(
  76 + (module) => {
  77 + const moduleRef = module.create(this.injector);
  78 + const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp);
  79 + this.dynamicComponentModulesMap.set(factory, {
  80 + moduleRef,
  81 + moduleType: module.moduleType
  82 + });
  83 + dymamicComponentFactorySubject.next(factory);
  84 + dymamicComponentFactorySubject.complete();
  85 + }
  86 + ).catch(
  87 + (e) => {
  88 + dymamicComponentFactorySubject.error(e);
  89 + }
  90 + );
  91 + } catch (e) {
87 dymamicComponentFactorySubject.error(e); 92 dymamicComponentFactorySubject.error(e);
88 } 93 }
89 - );  
90 - } catch (e) {  
91 - dymamicComponentFactorySubject.error(e);  
92 - } 94 + }
  95 + );
93 return dymamicComponentFactorySubject.asObservable(); 96 return dymamicComponentFactorySubject.asObservable();
94 } 97 }
95 98
@@ -78,28 +78,31 @@ export class ResourcesService { @@ -78,28 +78,31 @@ export class ResourcesService {
78 (module) => { 78 (module) => {
79 const modules = this.extractNgModules(module); 79 const modules = this.extractNgModules(module);
80 if (modules.length) { 80 if (modules.length) {
81 - const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];  
82 - for (const m of modules) {  
83 - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));  
84 - }  
85 - forkJoin(tasks).subscribe((compiled) => {  
86 - try {  
87 - const componentFactories: ComponentFactory<any>[] = [];  
88 - for (const c of compiled) {  
89 - c.ngModuleFactory.create(this.injector);  
90 - componentFactories.push(...c.componentFactories); 81 + import('@angular/compiler').then(
  82 + () => {
  83 + const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
  84 + for (const m of modules) {
  85 + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
91 } 86 }
92 - this.loadedFactories[url].next(componentFactories);  
93 - this.loadedFactories[url].complete();  
94 - } catch (e) {  
95 - this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));  
96 - delete this.loadedFactories[url];  
97 - }  
98 - },  
99 - (e) => {  
100 - this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));  
101 - delete this.loadedFactories[url];  
102 - }); 87 + forkJoin(tasks).subscribe((compiled) => {
  88 + try {
  89 + const componentFactories: ComponentFactory<any>[] = [];
  90 + for (const c of compiled) {
  91 + c.ngModuleFactory.create(this.injector);
  92 + componentFactories.push(...c.componentFactories);
  93 + }
  94 + this.loadedFactories[url].next(componentFactories);
  95 + this.loadedFactories[url].complete();
  96 + } catch (e) {
  97 + this.loadedFactories[url].error(new Error(`Unable to init module from url: ${url}`));
  98 + delete this.loadedFactories[url];
  99 + }
  100 + },
  101 + (e) => {
  102 + this.loadedFactories[url].error(new Error(`Unable to compile module from url: ${url}`));
  103 + delete this.loadedFactories[url];
  104 + }); }
  105 + );
103 } else { 106 } else {
104 this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`)); 107 this.loadedFactories[url].error(new Error(`Module '${url}' doesn't have default export!`));
105 delete this.loadedFactories[url]; 108 delete this.loadedFactories[url];
@@ -133,26 +136,30 @@ export class ResourcesService { @@ -133,26 +136,30 @@ export class ResourcesService {
133 } catch (e) { 136 } catch (e) {
134 } 137 }
135 if (modules && modules.length) { 138 if (modules && modules.length) {
136 - const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];  
137 - for (const m of modules) {  
138 - tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));  
139 - }  
140 - forkJoin(tasks).subscribe((compiled) => {  
141 - try {  
142 - for (const c of compiled) {  
143 - c.ngModuleFactory.create(this.injector);  
144 - }  
145 - this.loadedModules[url].next(modules);  
146 - this.loadedModules[url].complete();  
147 - } catch (e) {  
148 - this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));  
149 - delete this.loadedModules[url]; 139 + import('@angular/compiler').then(
  140 + () => {
  141 + const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
  142 + for (const m of modules) {
  143 + tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
150 } 144 }
151 - },  
152 - (e) => {  
153 - this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));  
154 - delete this.loadedModules[url];  
155 - }); 145 + forkJoin(tasks).subscribe((compiled) => {
  146 + try {
  147 + for (const c of compiled) {
  148 + c.ngModuleFactory.create(this.injector);
  149 + }
  150 + this.loadedModules[url].next(modules);
  151 + this.loadedModules[url].complete();
  152 + } catch (e) {
  153 + this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
  154 + delete this.loadedModules[url];
  155 + }
  156 + },
  157 + (e) => {
  158 + this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
  159 + delete this.loadedModules[url];
  160 + });
  161 + }
  162 + );
156 } else { 163 } else {
157 this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`)); 164 this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`));
158 delete this.loadedModules[url]; 165 delete this.loadedModules[url];
@@ -77,7 +77,7 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On @@ -77,7 +77,7 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
77 this.complexFilterPredicate = predicate; 77 this.complexFilterPredicate = predicate;
78 } 78 }
79 79
80 - private openComplexFilterDialog() { 80 + public openComplexFilterDialog() {
81 this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData, 81 this.dialog.open<ComplexFilterPredicateDialogComponent, ComplexFilterPredicateDialogData,
82 ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, { 82 ComplexFilterPredicateInfo>(ComplexFilterPredicateDialogComponent, {
83 disableClose: true, 83 disableClose: true,
@@ -59,7 +59,7 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit { @@ -59,7 +59,7 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit {
59 59
60 requiredClass = false; 60 requiredClass = false;
61 61
62 - private filterText: string; 62 + public filterText: string;
63 63
64 private propagateChange = (v: any) => { }; 64 private propagateChange = (v: any) => { };
65 65
@@ -38,7 +38,7 @@ export interface AlarmRuleConditionDialogData { @@ -38,7 +38,7 @@ export interface AlarmRuleConditionDialogData {
38 selector: 'tb-alarm-rule-condition-dialog', 38 selector: 'tb-alarm-rule-condition-dialog',
39 templateUrl: './alarm-rule-condition-dialog.component.html', 39 templateUrl: './alarm-rule-condition-dialog.component.html',
40 providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}], 40 providers: [{provide: ErrorStateMatcher, useExisting: AlarmRuleConditionDialogComponent}],
41 - styleUrls: ['/alarm-rule-condition-dialog.component.scss'] 41 + styleUrls: ['./alarm-rule-condition-dialog.component.scss']
42 }) 42 })
43 export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition> 43 export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRuleConditionDialogComponent, AlarmCondition>
44 implements OnInit, ErrorStateMatcher { 44 implements OnInit, ErrorStateMatcher {
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 /// 15 ///
16 16
17 import { MatDialogRef } from '@angular/material/dialog'; 17 import { MatDialogRef } from '@angular/material/dialog';
18 -import { Inject, InjectionToken } from '@angular/core'; 18 +import { Directive, Inject, InjectionToken } from '@angular/core';
19 import { Store } from '@ngrx/store'; 19 import { Store } from '@ngrx/store';
20 import { AppState } from '@core/core.state'; 20 import { AppState } from '@core/core.state';
21 import { Router } from '@angular/router'; 21 import { Router } from '@angular/router';
@@ -30,6 +30,7 @@ export interface CustomDialogData { @@ -30,6 +30,7 @@ export interface CustomDialogData {
30 [key: string]: any; 30 [key: string]: any;
31 } 31 }
32 32
  33 +@Directive()
33 export class CustomDialogComponent extends PageComponent { 34 export class CustomDialogComponent extends PageComponent {
34 35
35 [key: string]: any; 36 [key: string]: any;
@@ -154,7 +154,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -154,7 +154,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
154 private alarmsTitlePattern: string; 154 private alarmsTitlePattern: string;
155 155
156 private displayDetails = true; 156 private displayDetails = true;
157 - private allowAcknowledgment = true; 157 + public allowAcknowledgment = true;
158 private allowClear = true; 158 private allowClear = true;
159 159
160 private defaultPageSize = 10; 160 private defaultPageSize = 10;
@@ -186,11 +186,16 @@ export type TripAnimationSettings = { @@ -186,11 +186,16 @@ export type TripAnimationSettings = {
186 usePointAsAnchor: boolean; 186 usePointAsAnchor: boolean;
187 normalizationStep: number; 187 normalizationStep: number;
188 showPolygon: boolean; 188 showPolygon: boolean;
  189 + showLabel: boolean;
  190 + showTooltip: boolean;
189 latKeyName: string; 191 latKeyName: string;
190 lngKeyName: string; 192 lngKeyName: string;
191 rotationAngle: number; 193 rotationAngle: number;
192 label: string; 194 label: string;
193 tooltipPattern: string; 195 tooltipPattern: string;
  196 + tooltipColor: string;
  197 + tooltipOpacity: number;
  198 + tooltipFontColor: string;
194 useTooltipFunction: boolean; 199 useTooltipFunction: boolean;
195 useLabelFunction: boolean; 200 useLabelFunction: boolean;
196 pointAsAnchorFunction: GenericFunction; 201 pointAsAnchorFunction: GenericFunction;
@@ -102,11 +102,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni @@ -102,11 +102,11 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni
102 ctx: WidgetContext; 102 ctx: WidgetContext;
103 103
104 private formResize$: ResizeObserver; 104 private formResize$: ResizeObserver;
105 - private settings: MultipleInputWidgetSettings; 105 + public settings: MultipleInputWidgetSettings;
106 private widgetConfig: WidgetConfig; 106 private widgetConfig: WidgetConfig;
107 private subscription: IWidgetSubscription; 107 private subscription: IWidgetSubscription;
108 private datasources: Array<Datasource>; 108 private datasources: Array<Datasource>;
109 - private sources: Array<MultipleInputWidgetSource> = []; 109 + public sources: Array<MultipleInputWidgetSource> = [];
110 110
111 isVerticalAlignment: boolean; 111 isVerticalAlignment: boolean;
112 inputWidthSettings: string; 112 inputWidthSettings: string;
@@ -126,7 +126,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -126,7 +126,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
126 private defaultPageSize = 10; 126 private defaultPageSize = 10;
127 private defaultSortOrder = '-0'; 127 private defaultSortOrder = '-0';
128 private hideEmptyLines = false; 128 private hideEmptyLines = false;
129 - private showTimestamp = true; 129 + public showTimestamp = true;
130 private dateFormatFilter: string; 130 private dateFormatFilter: string;
131 131
132 private searchAction: WidgetAction = { 132 private searchAction: WidgetAction = {
@@ -36,12 +36,14 @@ import { JsonFormProps } from './react/json-form.models'; @@ -36,12 +36,14 @@ import { JsonFormProps } from './react/json-form.models';
36 import inspector from 'schema-inspector'; 36 import inspector from 'schema-inspector';
37 import * as tinycolor_ from 'tinycolor2'; 37 import * as tinycolor_ from 'tinycolor2';
38 import { DialogService } from '@app/core/services/dialog.service'; 38 import { DialogService } from '@app/core/services/dialog.service';
39 -import * as React from 'react';  
40 -import * as ReactDOM from 'react-dom';  
41 -import ReactSchemaForm from './react/json-form-react'; 39 +// import * as React from 'react';
  40 +// import * as ReactDOM from 'react-dom';
  41 +// import ReactSchemaForm from './react/json-form-react';
42 import JsonFormUtils from './react/json-form-utils'; 42 import JsonFormUtils from './react/json-form-utils';
43 import { JsonFormComponentData } from './json-form-component.models'; 43 import { JsonFormComponentData } from './json-form-component.models';
44 import { GroupInfo } from '@shared/models/widget.models'; 44 import { GroupInfo } from '@shared/models/widget.models';
  45 +import { Observable } from 'rxjs/internal/Observable';
  46 +import { forkJoin, from } from 'rxjs';
45 47
46 const tinycolor = tinycolor_; 48 const tinycolor = tinycolor_;
47 49
@@ -252,11 +254,35 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato @@ -252,11 +254,35 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
252 if (destroy) { 254 if (destroy) {
253 this.destroyReactSchemaForm(); 255 this.destroyReactSchemaForm();
254 } 256 }
255 - ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement); 257 +
  258 + // import ReactSchemaForm from './react/json-form-react';
  259 + const reactSchemaFormObservables: Observable<any>[] = [];
  260 + reactSchemaFormObservables.push(from(import('react')));
  261 + reactSchemaFormObservables.push(from(import('react-dom')));
  262 + reactSchemaFormObservables.push(from(import('./react/json-form-react')));
  263 + forkJoin(reactSchemaFormObservables).subscribe(
  264 + (modules) => {
  265 + const react = modules[0];
  266 + const reactDom = modules[1];
  267 + const jsonFormReact = modules[2].default;
  268 + reactDom.render(react.createElement(jsonFormReact, this.formProps), this.reactRootElmRef.nativeElement);
  269 + }
  270 + );
  271 + /* import('./react/json-form-react').then(
  272 + (mod) => {
  273 + ReactDOM.render(React.createElement(mod.default, this.formProps), this.reactRootElmRef.nativeElement);
  274 + }
  275 + );*/
  276 + // ReactDOM.render(React.createElement(ReactSchemaForm, this.formProps), this.reactRootElmRef.nativeElement);
256 } 277 }
257 278
258 private destroyReactSchemaForm() { 279 private destroyReactSchemaForm() {
259 - ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement); 280 + import('react-dom').then(
  281 + (reactDom) => {
  282 + reactDom.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
  283 + }
  284 + );
  285 + // ReactDOM.unmountComponentAtNode(this.reactRootElmRef.nativeElement);
260 } 286 }
261 287
262 private validateModel(): boolean { 288 private validateModel(): boolean {
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; 17 import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
18 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 18 import { coerceBooleanProperty } from '@angular/cdk/coercion';
19 -import Raphael, { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael'; 19 +import { RaphaelElement, RaphaelPaper, RaphaelSet } from 'raphael';
20 import * as tinycolor_ from 'tinycolor2'; 20 import * as tinycolor_ from 'tinycolor2';
21 21
22 const tinycolor = tinycolor_; 22 const tinycolor = tinycolor_;
@@ -90,10 +90,14 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges { @@ -90,10 +90,14 @@ export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
90 if (this.paper) { 90 if (this.paper) {
91 this.paper.remove(); 91 this.paper.remove();
92 } 92 }
93 - this.paper = Raphael($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);  
94 - const center = this.canvasSize / 2;  
95 - this.circleElement = this.paper.circle(center, center, this.radius);  
96 - this.draw(); 93 + import('raphael').then(
  94 + (raphael) => {
  95 + this.paper = raphael.default($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);
  96 + const center = this.canvasSize / 2;
  97 + this.circleElement = this.paper.circle(center, center, this.radius);
  98 + this.draw();
  99 + }
  100 + );
97 } 101 }
98 102
99 private draw() { 103 private draw() {