/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {SimpleChange} from '../change_detection/change_detection_util'; import {PipeTransform} from '../change_detection/pipe_transform'; import {OnChanges, SimpleChanges} from '../metadata/lifecycle_hooks'; import {RendererType2} from '../render/api'; import {Type} from '../type'; import {resolveRendererType2} from '../view/util'; import {diPublic} from './di'; import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs, PipeDef, PipeType} from './interfaces/definition'; /** * Create a component definition object. * * * # Example * ``` * class MyDirective { * // Generated by Angular Template Compiler * // [Symbol] syntax will not be supported by TypeScript until v2.7 * static ngComponentDef = defineComponent({ * ... * }); * } * ``` */ export function defineComponent(componentDefinition: ComponentDefArgs): ComponentDef { const type = componentDefinition.type; const def = >{ type: type, diPublic: null, n: componentDefinition.factory, tag: (componentDefinition as ComponentDefArgs).tag || null !, template: (componentDefinition as ComponentDefArgs).template || null !, h: componentDefinition.hostBindings || noop, inputs: invertObject(componentDefinition.inputs), outputs: invertObject(componentDefinition.outputs), methods: invertObject(componentDefinition.methods), rendererType: resolveRendererType2(componentDefinition.rendererType) || null, exportAs: componentDefinition.exportAs, onInit: type.prototype.ngOnInit || null, doCheck: type.prototype.ngDoCheck || null, afterContentInit: type.prototype.ngAfterContentInit || null, afterContentChecked: type.prototype.ngAfterContentChecked || null, afterViewInit: type.prototype.ngAfterViewInit || null, afterViewChecked: type.prototype.ngAfterViewChecked || null, onDestroy: type.prototype.ngOnDestroy || null }; const feature = componentDefinition.features; feature && feature.forEach((fn) => fn(def)); return def; } const PRIVATE_PREFIX = '__ngOnChanges_'; type OnChangesExpando = OnChanges & { __ngOnChanges_: SimpleChanges|null|undefined; [key: string]: any; }; export function NgOnChangesFeature(definition: DirectiveDef): void { const inputs = definition.inputs; const proto = definition.type.prototype; // Place where we will store SimpleChanges if there is a change Object.defineProperty(proto, PRIVATE_PREFIX, {value: undefined, writable: true}); for (let pubKey in inputs) { const minKey = inputs[pubKey]; const privateMinKey = PRIVATE_PREFIX + minKey; // Create a place where the actual value will be stored and make it non-enumerable Object.defineProperty(proto, privateMinKey, {value: undefined, writable: true}); const existingDesc = Object.getOwnPropertyDescriptor(proto, minKey); // create a getter and setter for property Object.defineProperty(proto, minKey, { get: function(this: OnChangesExpando) { return (existingDesc && existingDesc.get) ? existingDesc.get.call(this) : this[privateMinKey]; }, set: function(this: OnChangesExpando, value: any) { let simpleChanges = this[PRIVATE_PREFIX]; let isFirstChange = simpleChanges === undefined; if (simpleChanges == null) { simpleChanges = this[PRIVATE_PREFIX] = {}; } simpleChanges[pubKey] = new SimpleChange(this[privateMinKey], value, isFirstChange); (existingDesc && existingDesc.set) ? existingDesc.set.call(this, value) : this[privateMinKey] = value; } }); } // If an onInit hook is defined, it will need to wrap the ngOnChanges call // so the call order is changes-init-check in creation mode. In subsequent // change detection runs, only the check wrapper will be called. if (definition.onInit != null) { definition.onInit = onChangesWrapper(definition.onInit); } definition.doCheck = onChangesWrapper(definition.doCheck); function onChangesWrapper(delegateHook: (() => void) | null) { return function(this: OnChangesExpando) { let simpleChanges = this[PRIVATE_PREFIX]; if (simpleChanges != null) { this.ngOnChanges(simpleChanges); this[PRIVATE_PREFIX] = null; } delegateHook && delegateHook.apply(this); }; } } export function PublicFeature(definition: DirectiveDef) { definition.diPublic = diPublic; } const EMPTY = {}; function noop() {} /** Swaps the keys and values of an object. */ function invertObject(obj: any): any { if (obj == null) return EMPTY; const newObj: any = {}; for (let minifiedKey in obj) { newObj[obj[minifiedKey]] = minifiedKey; } return newObj; } /** * Create a directive definition object. * * # Example * ``` * class MyDirective { * // Generated by Angular Template Compiler * // [Symbol] syntax will not be supported by TypeScript until v2.7 * static ngDirectiveDef = defineDirective({ * ... * }); * } * ``` */ export const defineDirective = defineComponent as(directiveDefinition: DirectiveDefArgs) => DirectiveDef; /** * Create a pipe definition object. * * # Example * ``` * class MyPipe implements PipeTransform { * // Generated by Angular Template Compiler * static ngPipeDef = definePipe({ * ... * }); * } * ``` * @param type Pipe class reference. Needed to extract pipe lifecycle hooks. * @param factory A factory for creating a pipe instance. * @param pure Whether the pipe is pure. */ export function definePipe( {type, factory, pure}: {type: Type, factory: () => PipeTransform, pure?: boolean}): PipeDef { throw new Error('TODO: implement!'); }