fix(ivy): host bindings should support content children and content hooks (#27065)

PR Close #27065
This commit is contained in:
Kara Erickson 2018-11-12 11:44:47 -08:00 committed by Andrew Kushnir
parent f80c6008af
commit 8b9249a670
2 changed files with 99 additions and 5 deletions

View File

@ -83,8 +83,6 @@ export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | nu
executeInitHooks(viewData, tView, creationMode); executeInitHooks(viewData, tView, creationMode);
} }
setHostBindings(tView, viewData);
refreshDynamicEmbeddedViews(viewData); refreshDynamicEmbeddedViews(viewData);
// Content query results must be refreshed before content hooks are called. // Content query results must be refreshed before content hooks are called.
@ -93,6 +91,8 @@ export function refreshDescendantViews(viewData: LViewData, rf: RenderFlags | nu
if (!checkNoChangesMode) { if (!checkNoChangesMode) {
executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode); executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
} }
setHostBindings(tView, viewData);
} }
refreshChildComponents(tView.components, parentFirstTemplatePass, rf); refreshChildComponents(tView.components, parentFirstTemplatePass, rf);

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {EventEmitter} from '@angular/core'; import {ElementRef, EventEmitter} from '@angular/core';
import {AttributeMarker, defineComponent, template, defineDirective, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index'; import {AttributeMarker, defineComponent, template, defineDirective, ProvidersFeature, NgOnChangesFeature, QueryList} from '../../src/render3/index';
import {bind, directiveInject, element, elementEnd, elementProperty, elementStart, load, text, textBinding} from '../../src/render3/instructions'; import {bind, directiveInject, element, elementEnd, elementProperty, elementStart, load, text, textBinding, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
import {query, queryRefresh} from '../../src/render3/query';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function'; import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
@ -21,6 +22,7 @@ describe('host bindings', () => {
let hostBindingDir: HostBindingDir|null; let hostBindingDir: HostBindingDir|null;
beforeEach(() => { beforeEach(() => {
nameComp = null;
nameComp = null; nameComp = null;
hostBindingDir = null; hostBindingDir = null;
}); });
@ -646,4 +648,96 @@ describe('host bindings', () => {
expect(fixture.html).toEqual(`<div hostattributedir="" role="listbox"></div>`); expect(fixture.html).toEqual(`<div hostattributedir="" role="listbox"></div>`);
}); });
it('should support content children in host bindings', () => {
/**
* host: {
* '[id]': 'foos.length'
* }
*/
class HostBindingWithContentChildren {
// @ContentChildren('foo')
foos !: QueryList<any>;
static ngComponentDef = defineComponent({
type: HostBindingWithContentChildren,
selectors: [['host-binding-comp']],
factory: () => new HostBindingWithContentChildren(),
consts: 0,
vars: 0,
hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => {
elementProperty(
elIndex, 'id', bind(load<HostBindingWithContentChildren>(dirIndex).foos.length));
},
contentQueries: (dirIndex) => { registerContentQuery(query(null, ['foo']), dirIndex); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = load<HostBindingWithContentChildren>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && (instance.foos = tmp);
},
template: (rf: RenderFlags, cmp: HostBindingWithContentChildren) => {}
});
}
/**
* <host-binding-comp>
* <div #foo></div>
* <div #foo></div>
* </host-binding-comp>
*/
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
elementStart(0, 'host-binding-comp');
{
element(1, 'div', null, ['foo', '']);
element(3, 'div', null, ['foo', '']);
}
elementEnd();
}
}, 5, 0, [HostBindingWithContentChildren]);
const fixture = new ComponentFixture(App);
const hostBindingEl = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
expect(hostBindingEl.id).toEqual('2');
});
it('should support host bindings dependent on content hooks', () => {
/**
* host: {
* '[id]': 'myValue'
* }
*/
class HostBindingWithContentHooks {
myValue = 'initial';
ngAfterContentInit() { this.myValue = 'after-content'; }
ngAfterViewInit() { this.myValue = 'after-view'; }
static ngComponentDef = defineComponent({
type: HostBindingWithContentHooks,
selectors: [['host-binding-comp']],
factory: () => new HostBindingWithContentHooks(),
consts: 0,
vars: 0,
hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => {
elementProperty(elIndex, 'id', bind(load<HostBindingWithContentHooks>(dirIndex).myValue));
},
template: (rf: RenderFlags, cmp: HostBindingWithContentHooks) => {}
});
}
/** <host-binding-comp></host-binding-comp> */
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
element(0, 'host-binding-comp');
}
}, 1, 0, [HostBindingWithContentHooks]);
const fixture = new ComponentFixture(App);
const hostBindingEl = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
expect(hostBindingEl.id).toEqual('after-content');
});
}); });