feat(ivy): introduce "allocHostVars" instruction as a replacement for "hostVars" field (FW-692) (#27299)

PR Close #27299
This commit is contained in:
Andrew Kushnir
2018-11-27 12:05:26 -08:00
committed by Igor Minar
parent 0df914e1e9
commit a088b8c203
25 changed files with 329 additions and 144 deletions

View File

@ -842,6 +842,9 @@
{
"name": "invertObject"
},
{
"name": "invokeDirectivesHostBindings"
},
{
"name": "isClassBased"
},
@ -968,9 +971,6 @@
{
"name": "noSideEffects"
},
{
"name": "noop"
},
{
"name": "onChangesWrapper"
},
@ -983,9 +983,6 @@
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
{
"name": "prepareInitialFlag"
},
@ -1001,9 +998,6 @@
{
"name": "queueDestroyHooks"
},
{
"name": "queueHostBindingForCheck"
},
{
"name": "queueInitHooks"
},
@ -1091,6 +1085,9 @@
{
"name": "setContextPlayersDirty"
},
{
"name": "setCurrentDirectiveDef"
},
{
"name": "setDirty"
},

View File

@ -377,24 +377,15 @@
{
"name": "noSideEffects"
},
{
"name": "noop"
},
{
"name": "onChangesWrapper"
},
{
"name": "postProcessBaseDirective"
},
{
"name": "prefillHostVars"
},
{
"name": "queueComponentIndexForCheck"
},
{
"name": "queueHostBindingForCheck"
},
{
"name": "readElementValue"
},
@ -431,6 +422,9 @@
{
"name": "setBindingRoot"
},
{
"name": "setCurrentDirectiveDef"
},
{
"name": "setFirstTemplatePass"
},

View File

@ -1142,9 +1142,6 @@
{
"name": "noSideEffects"
},
{
"name": "noop"
},
{
"name": "noop$1"
},
@ -1184,9 +1181,6 @@
{
"name": "postProcessBaseDirective"
},
{
"name": "prefillHostVars"
},
{
"name": "projectionNodeStack"
},
@ -1208,9 +1202,6 @@
{
"name": "queueDestroyHooks"
},
{
"name": "queueHostBindingForCheck"
},
{
"name": "queueInitHooks"
},
@ -1292,6 +1283,9 @@
{
"name": "setCheckNoChangesMode"
},
{
"name": "setCurrentDirectiveDef"
},
{
"name": "setCurrentInjector"
},

View File

@ -872,6 +872,9 @@
{
"name": "invertObject"
},
{
"name": "invokeDirectivesHostBindings"
},
{
"name": "isComponent"
},
@ -986,9 +989,6 @@
{
"name": "noSideEffects"
},
{
"name": "noop"
},
{
"name": "onChangesWrapper"
},
@ -1001,9 +1001,6 @@
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
{
"name": "prepareInitialFlag"
},
@ -1019,9 +1016,6 @@
{
"name": "queueDestroyHooks"
},
{
"name": "queueHostBindingForCheck"
},
{
"name": "queueInitHooks"
},
@ -1112,6 +1106,9 @@
{
"name": "setContextPlayersDirty"
},
{
"name": "setCurrentDirectiveDef"
},
{
"name": "setDirty"
},

View File

@ -2105,6 +2105,9 @@
{
"name": "invertObject"
},
{
"name": "invokeDirectivesHostBindings"
},
{
"name": "isArray"
},
@ -2333,9 +2336,6 @@
{
"name": "noSideEffects"
},
{
"name": "noop"
},
{
"name": "noop$1"
},
@ -2399,9 +2399,6 @@
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
{
"name": "prepareInitialFlag"
},
@ -2426,9 +2423,6 @@
{
"name": "queueDestroyHooks"
},
{
"name": "queueHostBindingForCheck"
},
{
"name": "queueInitHooks"
},
@ -2546,6 +2540,9 @@
{
"name": "setContextPlayersDirty"
},
{
"name": "setCurrentDirectiveDef"
},
{
"name": "setCurrentInjector"
},

View File

@ -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]
});

View File

@ -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));
}

View File

@ -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');
});
});

View File

@ -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));
}

View File

@ -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));
}