fix(ivy): host bindings should support content children and content hooks (#27065)
PR Close #27065
This commit is contained in:
parent
f80c6008af
commit
8b9249a670
|
@ -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);
|
||||||
|
|
|
@ -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');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue