feat(ivy): introduce "allocHostVars" instruction as a replacement for "hostVars" field (FW-692) (#27299)
PR Close #27299
This commit is contained in:

committed by
Igor Minar

parent
0df914e1e9
commit
a088b8c203
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Inject, InjectionToken} from '../../src/core';
|
||||
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, load} from '../../src/render3/index';
|
||||
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, load} from '../../src/render3/index';
|
||||
|
||||
import {ComponentFixture, createComponent} from './render_util';
|
||||
|
||||
@ -309,11 +309,13 @@ describe('InheritDefinitionFeature', () => {
|
||||
type: SuperDirective,
|
||||
selectors: [['', 'superDir', '']],
|
||||
hostBindings: (rf: RenderFlags, ctx: SuperDirective, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
},
|
||||
hostVars: 1,
|
||||
factory: () => new SuperDirective(),
|
||||
});
|
||||
}
|
||||
@ -325,11 +327,13 @@ describe('InheritDefinitionFeature', () => {
|
||||
type: SubDirective,
|
||||
selectors: [['', 'subDir', '']],
|
||||
hostBindings: (rf: RenderFlags, ctx: SubDirective, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
},
|
||||
hostVars: 1,
|
||||
factory: () => subDir = new SubDirective(),
|
||||
features: [InheritDefinitionFeature]
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ import {defineComponent} from '../../src/render3/definition';
|
||||
import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
|
||||
import {ProvidersFeature, defineDirective, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLView, createTView, directiveInject, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, injectAttribute, interpolation2, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
|
||||
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLView, createTView, directiveInject, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, injectAttribute, interpolation2, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
|
||||
import {isProceduralRenderer, RElement} from '../../src/render3/interfaces/renderer';
|
||||
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
|
||||
import {getNativeByIndex} from '../../src/render3/util';
|
||||
@ -654,8 +654,10 @@ describe('di', () => {
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {ElementRef, EventEmitter} from '@angular/core';
|
||||
|
||||
import {AttributeMarker, defineComponent, template, defineDirective, ProvidersFeature, NgOnChangesFeature, QueryList} from '../../src/render3/index';
|
||||
import {bind, directiveInject, element, elementEnd, elementProperty, elementStart, load, text, textBinding, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
|
||||
import {allocHostVars, bind, directiveInject, element, elementEnd, elementProperty, elementStart, listener, load, text, textBinding, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
|
||||
import {query, queryRefresh} from '../../src/render3/query';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
|
||||
@ -49,8 +49,10 @@ describe('host bindings', () => {
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
@ -68,8 +70,10 @@ describe('host bindings', () => {
|
||||
factory: () => new HostBindingComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
@ -89,8 +93,10 @@ describe('host bindings', () => {
|
||||
type: Directive,
|
||||
selectors: [['', 'dir', '']],
|
||||
factory: () => directiveInstance = new Directive,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'className', bind(ctx.klass));
|
||||
}
|
||||
@ -139,8 +145,10 @@ describe('host bindings', () => {
|
||||
() => new CompWithProviders(directiveInject(ServiceOne), directiveInject(ServiceTwo)),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: CompWithProviders, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
@ -173,8 +181,10 @@ describe('host bindings', () => {
|
||||
factory: () => new HostTitleComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostTitleComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
@ -207,6 +217,66 @@ describe('host bindings', () => {
|
||||
expect(hostBindingDiv.id).toEqual('bar');
|
||||
});
|
||||
|
||||
it('should support consecutive components with host bindings', () => {
|
||||
let comps: HostBindingComp[] = [];
|
||||
|
||||
class HostBindingComp {
|
||||
// @HostBinding()
|
||||
id = 'blue';
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: HostBindingComp,
|
||||
selectors: [['host-binding-comp']],
|
||||
factory: () => {
|
||||
const comp = new HostBindingComp();
|
||||
comps.push(comp);
|
||||
return comp;
|
||||
},
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <host-binding-comp></host-binding-comp>
|
||||
* <host-binding-comp></host-binding-comp>
|
||||
* */
|
||||
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'host-binding-comp');
|
||||
element(1, 'host-binding-comp');
|
||||
}
|
||||
}, 2, 0, [HostBindingComp]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
const hostBindingEls =
|
||||
fixture.hostElement.querySelectorAll('host-binding-comp') as NodeListOf<HTMLElement>;
|
||||
|
||||
expect(hostBindingEls.length).toBe(2);
|
||||
|
||||
comps[0].id = 'red';
|
||||
fixture.update();
|
||||
expect(hostBindingEls[0].id).toBe('red');
|
||||
|
||||
// second element should not be affected
|
||||
expect(hostBindingEls[1].id).toBe('blue');
|
||||
|
||||
comps[1].id = 'red';
|
||||
fixture.update();
|
||||
|
||||
// now second element should take updated value
|
||||
expect(hostBindingEls[1].id).toBe('red');
|
||||
});
|
||||
|
||||
it('should support dirs with host bindings on the same node as dirs without host bindings',
|
||||
() => {
|
||||
const SomeDir = createDirective('someDir');
|
||||
@ -253,9 +323,11 @@ describe('host bindings', () => {
|
||||
template: (rf: RenderFlags, ctx: InitHookComp) => {},
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
features: [NgOnChangesFeature],
|
||||
hostBindings: (rf: RenderFlags, ctx: InitHookComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.value));
|
||||
}
|
||||
@ -420,9 +492,11 @@ describe('host bindings', () => {
|
||||
factory: () => hostBindingComp = new HostBindingComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 8,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LView: [..., id, dir, title, ctx.id, pf1, ctx.title, ctx.otherTitle, pf2]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(8);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
|
||||
elementProperty(elIndex, 'dir', bind(ctx.dir));
|
||||
@ -496,9 +570,11 @@ describe('host bindings', () => {
|
||||
factory: () => hostBindingComp = new HostBindingComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 3,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LView: [..., id, ctx.id, pf1]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(3);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
|
||||
}
|
||||
@ -525,9 +601,11 @@ describe('host bindings', () => {
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 3,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingDir, elIndex: number) => {
|
||||
// LView [..., title, ctx.title, pf1]
|
||||
// LView: [..., title, ctx.title, pf1]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(3);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
|
||||
}
|
||||
@ -559,6 +637,69 @@ describe('host bindings', () => {
|
||||
expect(hostElement.id).toBe('red,green');
|
||||
});
|
||||
|
||||
it('should support directives with and without allocHostVars on the same component', () => {
|
||||
let events: string[] = [];
|
||||
|
||||
const ff1 = (v: any) => [v, 'other title'];
|
||||
|
||||
/**
|
||||
* @Directive({
|
||||
* ...
|
||||
* host: {
|
||||
* '[title]': '[title, 'other title']'
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*/
|
||||
class HostBindingDir {
|
||||
title = 'my title';
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostDir', '']],
|
||||
factory: () => new HostBindingDir(),
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingDir, elIndex: number) => {
|
||||
// LViewData [..., title, ctx.title, pf1]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(3);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class HostListenerDir {
|
||||
/* @HostListener('click') */
|
||||
onClick() { events.push('click!'); }
|
||||
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: HostListenerDir,
|
||||
selectors: [['', 'hostListenerDir', '']],
|
||||
factory: function HostListenerDir_Factory() { return new HostListenerDir(); },
|
||||
hostBindings: function HostListenerDir_HostBindings(
|
||||
rf: RenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
listener('click', function() { return ctx.onClick(); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// <button hostListenerDir hostDir>Click</button>
|
||||
const fixture = new TemplateFixture(() => {
|
||||
elementStart(0, 'button', ['hostListenerDir', '', 'hostDir', '']);
|
||||
text(1, 'Click');
|
||||
elementEnd();
|
||||
}, () => {}, 2, 0, [HostListenerDir, HostBindingDir]);
|
||||
|
||||
const button = fixture.hostElement.querySelector('button') !;
|
||||
button.click();
|
||||
expect(events).toEqual(['click!']);
|
||||
expect(button.title).toEqual('my title,other title');
|
||||
});
|
||||
|
||||
it('should support ternary expressions in host bindings', () => {
|
||||
let hostBindingComp !: HostBindingComp;
|
||||
|
||||
@ -587,9 +728,11 @@ describe('host bindings', () => {
|
||||
factory: () => hostBindingComp = new HostBindingComp(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 6,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LView: [..., id, title, ctx.id, pf1, ctx.title, pf1]
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(6);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(
|
||||
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
|
||||
@ -677,8 +820,10 @@ describe('host bindings', () => {
|
||||
factory: () => new HostBindingWithContentChildren(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingWithContentChildren, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.foos.length));
|
||||
}
|
||||
@ -734,8 +879,10 @@ describe('host bindings', () => {
|
||||
factory: () => new HostBindingWithContentHooks(),
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingWithContentHooks, elIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.myValue));
|
||||
}
|
||||
@ -755,5 +902,4 @@ describe('host bindings', () => {
|
||||
const hostBindingEl = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
|
||||
expect(hostBindingEl.id).toEqual('after-content');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
|
||||
import {AttributeMarker, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template, elementStylingMap, directiveInject} from '../../src/render3/instructions';
|
||||
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template, elementStylingMap, directiveInject} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer';
|
||||
import {NO_CHANGE} from '../../src/render3/tokens';
|
||||
@ -446,8 +446,10 @@ describe('render3 integration test', () => {
|
||||
}
|
||||
},
|
||||
factory: () => cmptInstance = new TodoComponentHostBinding,
|
||||
hostVars: 1,
|
||||
hostBindings: function(rf: RenderFlags, ctx: any, elementIndex: number): void {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
// host bindings
|
||||
elementProperty(elementIndex, 'title', bind(ctx.title));
|
||||
@ -1379,9 +1381,11 @@ describe('render3 integration test', () => {
|
||||
factory: function HostBindingDir_Factory() {
|
||||
return hostBindingDir = new HostBindingDir();
|
||||
},
|
||||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(
|
||||
rf: RenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementAttribute(elIndex, 'aria-label', bind(ctx.label));
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, El
|
||||
import {ViewEncapsulation} from '../../src/metadata';
|
||||
import {AttributeMarker, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load, query, queryRefresh} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
|
||||
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RElement} from '../../src/render3/interfaces/renderer';
|
||||
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
|
||||
@ -1836,9 +1836,11 @@ describe('ViewContainerRef', () => {
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, cmp: HostBindingCmpt) => {},
|
||||
hostVars: 1,
|
||||
attributes: ['id', 'attribute'],
|
||||
hostBindings: function(rf: RenderFlags, ctx: HostBindingCmpt, elIndex: number) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
allocHostVars(1);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
|
Reference in New Issue
Block a user