From dcca799dbbe92aaa1266b147939eb761039439d5 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Fri, 26 Jan 2018 13:38:21 -0800 Subject: [PATCH] fix(ivy): call onChanges before onInit (#21793) PR Close #21793 --- packages/core/src/render3/definition.ts | 16 +++++++++++--- .../test/render3/compiler_canonical_spec.ts | 22 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 528fb5078c..672d61c2a4 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -99,16 +99,26 @@ export function NgOnChangesFeature(type: Type): (definition: DirectiveDef< } }); } - definition.doCheck = (function(delegateDoCheck) { + + // 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; } - delegateDoCheck && delegateDoCheck.apply(this); + delegateHook && delegateHook.apply(this); }; - })(proto.ngDoCheck); + } }; } diff --git a/packages/core/test/render3/compiler_canonical_spec.ts b/packages/core/test/render3/compiler_canonical_spec.ts index 594e5f5f84..218283d303 100644 --- a/packages/core/test/render3/compiler_canonical_spec.ts +++ b/packages/core/test/render3/compiler_canonical_spec.ts @@ -270,6 +270,7 @@ describe('compiler specification', () => { describe('lifecycle hooks', () => { let events: string[] = []; + let simpleLayout: SimpleLayout; beforeEach(() => { events = []; }); @@ -277,6 +278,8 @@ describe('compiler specification', () => { class LifecycleComp { @Input() nameMin: string; + ngOnChanges() { events.push('changes' + this.nameMin); } + ngOnInit() { events.push('init' + this.nameMin); } ngDoCheck() { events.push('check' + this.nameMin); } @@ -293,7 +296,8 @@ describe('compiler specification', () => { tag: 'lifecycle-comp', factory: () => new LifecycleComp(), template: function(ctx: any, cm: boolean) {}, - inputs: {nameMin: 'name'} + inputs: {nameMin: 'name'}, + features: [r3.NgOnChangesFeature(LifecycleComp)] }); } @@ -311,7 +315,7 @@ describe('compiler specification', () => { static ngComponentDef = r3.defineComponent({ type: SimpleLayout, tag: 'simple-layout', - factory: () => new SimpleLayout(), + factory: () => simpleLayout = new SimpleLayout(), template: function(ctx: any, cm: boolean) { if (cm) { r3.E(0, LifecycleComp); @@ -333,8 +337,18 @@ describe('compiler specification', () => { expect(renderComp(SimpleLayout)) .toEqual(``); expect(events).toEqual([ - 'init1', 'check1', 'init2', 'check2', 'content init1', 'content check1', 'content init2', - 'content check2', 'view init1', 'view check1', 'view init2', 'view check2' + 'changes1', 'init1', 'check1', 'changes2', 'init2', 'check2', 'content init1', + 'content check1', 'content init2', 'content check2', 'view init1', 'view check1', + 'view init2', 'view check2' + ]); + + events = []; + simpleLayout.name1 = '-one'; + simpleLayout.name2 = '-two'; + r3.detectChanges(simpleLayout); + expect(events).toEqual([ + 'changes-one', 'check-one', 'changes-two', 'check-two', 'content check-one', + 'content check-two', 'view check-one', 'view check-two' ]); });