diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 672d61c2a4..b96eaa3fd6 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -67,59 +67,57 @@ type OnChangesExpando = OnChanges & { [key: string]: any; }; -export function NgOnChangesFeature(type: Type): (definition: DirectiveDef) => void { - return function(definition: DirectiveDef): void { - const inputs = definition.inputs; - const proto = 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}); +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); + 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) { + // 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]; - if (simpleChanges != null) { - this.ngOnChanges(simpleChanges); - this[PRIVATE_PREFIX] = null; + let isFirstChange = simpleChanges === undefined; + if (simpleChanges == null) { + simpleChanges = this[PRIVATE_PREFIX] = {}; } - delegateHook && delegateHook.apply(this); - }; - } - }; + 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); + }; + } } diff --git a/packages/core/test/render3/common_with_def.ts b/packages/core/test/render3/common_with_def.ts index 2b40b4fe94..37e3708094 100644 --- a/packages/core/test/render3/common_with_def.ts +++ b/packages/core/test/render3/common_with_def.ts @@ -19,7 +19,7 @@ NgForOf.ngDirectiveDef = defineDirective({ factory: () => new NgForOfDef( injectViewContainerRef(), injectTemplateRef(), inject(IterableDiffers, InjectFlags.Default, defaultIterableDiffers)), - features: [NgOnChangesFeature(NgForOf)], + features: [NgOnChangesFeature], inputs: { ngForOf: 'ngForOf', ngForTrackBy: 'ngForTrackBy', diff --git a/packages/core/test/render3/compiler_canonical_spec.ts b/packages/core/test/render3/compiler_canonical_spec.ts index 462bc03343..6bb2c9f289 100644 --- a/packages/core/test/render3/compiler_canonical_spec.ts +++ b/packages/core/test/render3/compiler_canonical_spec.ts @@ -426,7 +426,7 @@ describe('compiler specification', () => { factory: () => new LifecycleComp(), template: function(ctx: any, cm: boolean) {}, inputs: {nameMin: 'name'}, - features: [r3.NgOnChangesFeature(LifecycleComp)] + features: [r3.NgOnChangesFeature] }); } diff --git a/packages/core/test/render3/define_spec.ts b/packages/core/test/render3/define_spec.ts index 8aa5a12174..b26e91f531 100644 --- a/packages/core/test/render3/define_spec.ts +++ b/packages/core/test/render3/define_spec.ts @@ -30,7 +30,7 @@ describe('define', () => { static ngDirectiveDef = defineDirective({ type: MyDirective, factory: () => new MyDirective(), - features: [NgOnChangesFeature(MyDirective)], + features: [NgOnChangesFeature], inputs: {valA: 'valA', valB: 'valB'} }); }