fix(ivy): init hooks should be called before host bindings (#26802)

PR Close #26802
This commit is contained in:
Kara Erickson 2018-10-27 16:01:26 -07:00
parent df6a8b28de
commit ea0a99610d
2 changed files with 70 additions and 2 deletions

View File

@ -78,11 +78,13 @@ export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | nu
if (rf !== RenderFlags.Create) {
const creationMode = getCreationMode();
const checkNoChangesMode = getCheckNoChangesMode();
setHostBindings(tView, viewData);
if (!checkNoChangesMode) {
executeInitHooks(viewData, tView, creationMode);
}
setHostBindings(tView, viewData);
refreshDynamicEmbeddedViews(viewData);
// Content query results must be refreshed before content hooks are called.

View File

@ -8,7 +8,7 @@
import {EventEmitter} from '@angular/core';
import {AttributeMarker, defineComponent, template, defineDirective, ProvidersFeature} from '../../src/render3/index';
import {AttributeMarker, defineComponent, template, defineDirective, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index';
import {bind, directiveInject, element, elementEnd, elementProperty, elementStart, load, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
@ -218,6 +218,72 @@ describe('host bindings', () => {
expect(hostBindingDiv.id).toEqual('bar');
});
it('should support host bindings that rely on values from init hooks', () => {
class InitHookComp {
// @Input()
inputValue = '';
changesValue = '';
initValue = '';
checkValue = '';
ngOnChanges() { this.changesValue = 'changes'; }
ngOnInit() { this.initValue = 'init'; }
ngDoCheck() { this.checkValue = 'check'; }
get value() {
return `${this.inputValue}-${this.changesValue}-${this.initValue}-${this.checkValue}`;
}
static ngComponentDef = defineComponent({
type: InitHookComp,
selectors: [['init-hook-comp']],
factory: () => new InitHookComp(),
template: (rf: RenderFlags, ctx: InitHookComp) => {},
consts: 0,
vars: 0,
hostVars: 1,
features: [NgOnChangesFeature],
hostBindings: (dirIndex: number, elIndex: number) => {
const ctx = load(dirIndex) as InitHookComp;
elementProperty(elIndex, 'title', bind(ctx.value));
},
inputs: {inputValue: 'inputValue'}
});
}
/** <init-hook-comp [inputValue]="value"></init-hook-comp> */
class App {
value = 'input';
static ngComponentDef = defineComponent({
type: App,
selectors: [['app']],
factory: () => new App(),
template: (rf: RenderFlags, ctx: App) => {
if (rf & RenderFlags.Create) {
element(0, 'init-hook-comp');
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'inputValue', bind(ctx.value));
}
},
consts: 1,
vars: 1,
directives: [InitHookComp]
});
}
const fixture = new ComponentFixture(App);
const initHookComp = fixture.hostElement.querySelector('init-hook-comp') as HTMLElement;
expect(initHookComp.title).toEqual('input-changes-init-check');
fixture.component.value = 'input2';
fixture.update();
expect(initHookComp.title).toEqual('input2-changes-init-check');
});
it('should support host bindings on second template pass', () => {
/** <div hostBindingDir></div> */