diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e9b5879 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "vsicons.presets.angular": true, + "typescript.tsdk": "./node_modules/typescript/lib" +} diff --git a/src/app/af-spawn.service.ts b/src/app/af-spawn.service.ts index 26e82e3..c37bf37 100644 --- a/src/app/af-spawn.service.ts +++ b/src/app/af-spawn.service.ts @@ -1,91 +1,86 @@ import { - Injectable, ViewContainerRef, ComponentFactoryResolver, ApplicationRef, Injector, - ComponentFactory + Injectable, + ViewContainerRef, + ComponentFactoryResolver, + ApplicationRef, + Injector, + ComponentFactory, + ComponentRef } from '@angular/core'; -import {BehaviorSubject, Subscription} from "rxjs"; -import {getSymbolObservable} from "rxjs/symbol/observable"; -import {SpawnReference} from "./interfaces/SpawnReference"; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Subscription } from 'rxjs/Subscription'; +import { getSymbolObservable } from 'rxjs/symbol/observable'; +import { SpawnReference } from './interfaces/SpawnReference'; @Injectable() export class AFSpawnService { - constructor( - private cfr: ComponentFactoryResolver - , private appRef: ApplicationRef - , private injector: Injector + private cfr: ComponentFactoryResolver, + private appRef: ApplicationRef, + private injector: Injector ) { } - createComponent(type: any, vcr?: ViewContainerRef, context?: any): SpawnReference{ + createComponent(componentType: any, viewContainerRef?: ViewContainerRef, context?: any): SpawnReference { - // Resolve the factory for incoming component `type`. - let factory = this.cfr.resolveComponentFactory(type); + const factory: ComponentFactory = this.cfr.resolveComponentFactory(componentType); - // Create an instance of the component, and add it to the DOM - let componentRef; - if(vcr){ - componentRef = vcr.createComponent(factory); + let componentRef: ComponentRef; + if (viewContainerRef) { + componentRef = viewContainerRef.createComponent(factory); } else { componentRef = factory.create(this.injector); this.appRef.attachView(componentRef.hostView); - document.body.appendChild( (componentRef.hostView as any).rootNodes[0]); + document.body.appendChild((componentRef.hostView as any).rootNodes[0]); } - // Wire up the outputs, and get reference to un-wire outputs - let unsubs = this._wireOutputs(factory, componentRef, context); - - // Turn the provided inputs into an observable (if not already an observable) - let observableSymbol = getSymbolObservable(window); - let context$; - if(context && context[observableSymbol]){ + const subscriptions: Subscription[] = this.wireOutputs(factory, componentRef, context); + const observableSymbol: any = getSymbolObservable(window); + let context$: BehaviorSubject; + if (context && context[observableSymbol]) { context$ = context; } else { context$ = new BehaviorSubject(context); } - // Subscribe to the new observable for updated input values - unsubs.push( context$.subscribe(()=>{ - factory.inputs.forEach(i=>{ - if(context[i.propName] !== undefined){ - componentRef.instance[i.propName] = context[i.propName]; + subscriptions.push(context$.subscribe(() => { + factory.inputs.forEach(input => { + if (context[input.propName] !== undefined) { + componentRef.instance[input.propName] = context[input.propName]; } - }) - }) ); + }); + })); - // This function will be returned to the caller, to be called when their context is destroyed - let detach = ()=>{ - if (!vcr) { + const detach = () => { + if (!viewContainerRef) { this.appRef.detachView(componentRef.hostView); } componentRef.destroy(); - unsubs.map(u => { if (!u.closed) { u.unsubscribe(); } }); + subscriptions.map(subscription => { if (!subscription.closed) { subscription.unsubscribe(); } }); }; - // This function will be returned to the caller, to be called when there are new values for the inputs - let next = (data)=>{ - if(context$ == context){ - throw `When passing an observable as a context, you cannot call the \`.next\` function from the result. - If you wish to update the values in your context, send the data through the observable that you - passed in as the context.`; + const next = (data: any) => { + if (context$ === context) { + throw new Error(`When passing an observable as a context, you cannot call the \`.next\` function from the result. + If you wish to update the values in your context, send the data through the observable that you passed in as the context.`); } + context$.next(data); }; return { detach, - next, - } + next + }; } - // Internal function to add event emitters for each of the provide outputs - _wireOutputs(factory: ComponentFactory, componentRef: any, context: {[key:string]: any}): Array{ - let unsubs = []; - factory.outputs.forEach(o=>{ - if(context[o.propName] && context[o.propName] instanceof Function){ - unsubs.push(componentRef.instance[o.propName].subscribe(context[o.propName])); + private wireOutputs(factory: ComponentFactory, componentRef: ComponentRef, context: { [key: string]: any }): Array { + const subscriptions: Subscription[] = []; + factory.outputs.forEach(output => { + if (context[output.propName] && context[output.propName] instanceof Function) { + subscriptions.push(componentRef.instance[output.propName].subscribe(context[output.propName])); } }); - return unsubs; + return subscriptions; } - } diff --git a/tslint.json b/tslint.json index 9113f13..fdbdedc 100644 --- a/tslint.json +++ b/tslint.json @@ -1,39 +1,63 @@ -{ - "rulesDirectory": [ +{ + "rulesDirectory": [ "node_modules/codelyzer" ], - "rules": { - "callable-types": true, - "class-name": true, - "comment-format": [ + "rules": { + "directive-selector": [true, "attribute", "", "kebab-case"], + "component-selector": [true, "element", "", "kebab-case"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": false, + "no-attribute-parameter-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "no-forward-ref" :true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "pipe-naming": [true, "camelCase"], + "component-class-suffix": true, + "directive-class-suffix": true, + "import-destructuring-spacing": true, + "templates-use-public": true, + "no-access-missing-member": false, + "invoke-injectable": true, + "member-access": false, + "member-ordering": [ true, - "check-space" + { "order": "statics-first" } + ], + "no-any": false, + "no-inferrable-types": [true, "ignore-params"], + "no-internal-module": true, + "no-namespace": true, + "no-reference": true, + "no-var-requires": false, + "typedef": false, + "typedef-whitespace": [ + true, { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, { + "call-signature": "space", + "index-signature": "space", + "parameter": "space", + "property-declaration": "space", + "variable-declaration": "space" + } ], "curly": true, - "eofline": true, "forin": true, "import-blacklist": [true, "rxjs"], - "import-spacing": true, - "indent": [ - true, - "spaces" - ], - "interface-over-type-literal": true, "label-position": true, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - "static-before-instance", - "variables-before-functions" - ], "no-arg": true, "no-bitwise": true, + "no-conditional-assignment": false, "no-console": [ true, + "log", "debug", "info", "time", @@ -41,76 +65,66 @@ "trace" ], "no-construct": true, - "no-debugger": true, + "no-debugger": false, "no-duplicate-variable": true, - "no-empty": false, - "no-empty-interface": true, + "no-empty": true, "no-eval": true, - "no-inferrable-types": [true, "ignore-params"], + "no-invalid-this": true, + "no-null-keyword": false, "no-shadowed-variable": true, "no-string-literal": false, - "no-string-throw": true, "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, "no-unused-expression": true, "no-use-before-declare": true, "no-var-keyword": true, + "radix": true, + "switch-default": false, + "triple-equals": [true, "allow-null-check"], + "use-isnan": true, + "eofline": true, + "indent": [true, "spaces"], + "max-line-length": [true, 150], + "no-require-imports": false, + "no-trailing-whitespace": true, "object-literal-sort-keys": false, + "trailing-comma": [ + true, + { + "multiline": "never", + "singleline": "never" + } + ], + "align": false, + "class-name": true, + "comment-format": [true, "check-space"], + "interface-name": [true, "never-prefix"], + "jsdoc-format": true, + "no-angle-bracket-type-assertion": true, + "no-consecutive-blank-lines": false, "one-line": [ true, - "check-open-brace", "check-catch", "check-else", + "check-finally", + "check-open-brace", "check-whitespace" ], - "prefer-const": true, - "quotemark": [ - true, - "single" - ], - "radix": true, - "semicolon": [ - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ + "one-variable-per-declaration": true, + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "variable-name": [ true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } + "check-format", + "ban-keywords" ], - "typeof-compare": true, - "unified-signatures": true, - "variable-name": false, "whitespace": [ true, "check-branch", "check-decl", "check-operator", + "check-module", "check-separator", "check-type" - ], - - "directive-selector": [true, "attribute", "app", "camelCase"], - "component-selector": [true, "element", "app", "kebab-case"], - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true, - "no-access-missing-member": true, - "templates-use-public": true, - "invoke-injectable": true + ] } }