refactor(ivy): merge directives into LViewData (#26316)

PR Close #26316
This commit is contained in:
Kara Erickson 2018-10-08 16:04:46 -07:00 committed by Jason Aden
parent b0879046b7
commit 7ea5161d4d
33 changed files with 464 additions and 328 deletions

View File

@ -588,7 +588,7 @@ describe('compiler compliance', () => {
selectors: [["", "hostBindingDir", ""]], selectors: [["", "hostBindingDir", ""]],
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); }, factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) { hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵloadDirective(dirIndex).dirId)); $r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
}, },
hostVars: 1, hostVars: 1,
features: [$r3$.ɵPublicFeature] features: [$r3$.ɵPublicFeature]
@ -632,7 +632,7 @@ describe('compiler compliance', () => {
selectors: [["host-binding-comp"]], selectors: [["host-binding-comp"]],
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); }, factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
hostBindings: function HostBindingComp_HostBindings(dirIndex, elIndex) { hostBindings: function HostBindingComp_HostBindings(dirIndex, elIndex) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵloadDirective(dirIndex).id))); $r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
}, },
hostVars: 3, hostVars: 3,
features: [$r3$.ɵPublicFeature], features: [$r3$.ɵPublicFeature],
@ -1343,7 +1343,7 @@ describe('compiler compliance', () => {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false)); $r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$));
@ -1403,7 +1403,7 @@ describe('compiler compliance', () => {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false)); $r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false));
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));
@ -1452,7 +1452,7 @@ describe('compiler compliance', () => {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false, ElementRef)); $r3$.ɵregisterContentQuery($r3$.ɵquery(null, ["myRef1", "myRef2", "myRef3"], false, ElementRef));
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));

View File

@ -463,17 +463,14 @@ describe('ngtsc behavioral tests', () => {
env.driveMain(); env.driveMain();
const jsContents = env.getContents('test.js'); const jsContents = env.getContents('test.js');
expect(jsContents) expect(jsContents)
.toContain( .toContain(`i0.ɵelementProperty(elIndex, "attr.hello", i0.ɵbind(i0.ɵload(dirIndex).foo));`);
`i0.ɵelementProperty(elIndex, "attr.hello", i0.ɵbind(i0.ɵloadDirective(dirIndex).foo));`); expect(jsContents)
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
expect(jsContents) expect(jsContents)
.toContain( .toContain(
`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵloadDirective(dirIndex).bar));`); 'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵload(dirIndex).someClass))');
expect(jsContents) expect(jsContents).toContain('i0.ɵload(dirIndex).onClick($event)');
.toContain( expect(jsContents).toContain('i0.ɵload(dirIndex).onChange(i0.ɵload(dirIndex).arg)');
'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵloadDirective(dirIndex).someClass))');
expect(jsContents).toContain('i0.ɵloadDirective(dirIndex).onClick($event)');
expect(jsContents)
.toContain('i0.ɵloadDirective(dirIndex).onChange(i0.ɵloadDirective(dirIndex).arg)');
}); });
it('should correctly recognize local symbols', () => { it('should correctly recognize local symbols', () => {

View File

@ -96,7 +96,6 @@ export class Identifiers {
static pipeBindV: o.ExternalReference = {name: 'ɵpipeBindV', moduleName: CORE}; static pipeBindV: o.ExternalReference = {name: 'ɵpipeBindV', moduleName: CORE};
static load: o.ExternalReference = {name: 'ɵload', moduleName: CORE}; static load: o.ExternalReference = {name: 'ɵload', moduleName: CORE};
static loadDirective: o.ExternalReference = {name: 'ɵloadDirective', moduleName: CORE};
static loadQueryList: o.ExternalReference = {name: 'ɵloadQueryList', moduleName: CORE}; static loadQueryList: o.ExternalReference = {name: 'ɵloadQueryList', moduleName: CORE};
static pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE}; static pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE};

View File

@ -473,10 +473,9 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre
// var $tmp$: any; // var $tmp$: any;
const temporary = temporaryAllocator(statements, TEMPORARY_NAME); const temporary = temporaryAllocator(statements, TEMPORARY_NAME);
// const $instance$ = $r3$.ɵloadDirective(dirIndex); // const $instance$ = $r3$.ɵload(dirIndex);
statements.push( statements.push(directiveInstanceVar.set(o.importExpr(R3.load).callFn([o.variable('dirIndex')]))
directiveInstanceVar.set(o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')])) .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
meta.queries.forEach((query: R3QueryMetadata, idx: number) => { meta.queries.forEach((query: R3QueryMetadata, idx: number) => {
const loadQLArg = o.variable('queryStartIndex'); const loadQLArg = o.variable('queryStartIndex');
@ -580,7 +579,7 @@ function createHostBindingsFunction(
// Calculate the host property bindings // Calculate the host property bindings
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan); const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
const bindingContext = o.importExpr(R3.loadDirective).callFn([o.variable('dirIndex')]); const bindingContext = o.importExpr(R3.load).callFn([o.variable('dirIndex')]);
if (bindings) { if (bindings) {
const valueConverter = new ValueConverter( const valueConverter = new ValueConverter(
constantPool, constantPool,

View File

@ -47,7 +47,6 @@ export {
embeddedViewStart as ɵembeddedViewStart, embeddedViewStart as ɵembeddedViewStart,
query as ɵquery, query as ɵquery,
registerContentQuery as ɵregisterContentQuery, registerContentQuery as ɵregisterContentQuery,
loadDirective as ɵloadDirective,
projection as ɵprojection, projection as ɵprojection,
bind as ɵbind, bind as ɵbind,
interpolation1 as ɵinterpolation1, interpolation1 as ɵinterpolation1,

View File

@ -128,8 +128,6 @@ NOTE:
## `EXPANDO` ## `EXPANDO`
*TODO*: This section is to be implemented.
`EXPANDO` contains information on data which size is not known at compile time. `EXPANDO` contains information on data which size is not known at compile time.
Examples include: Examples include:
- `Component`/`Directives` since we don't know at compile time which directives will match. - `Component`/`Directives` since we don't know at compile time which directives will match.
@ -203,7 +201,7 @@ The `EXPANDO` section needs additional information for information stored in `TV
| Index | `TView.expandoInstructions` | Meaning | Index | `TView.expandoInstructions` | Meaning
| ----: | ---------------------------: | ------- | ----: | ---------------------------: | -------
| 0 | -10 | Negative numbers signifies pointers to elements. In this case 10 (`<child>`) | 0 | -10 | Negative numbers signify pointers to elements. In this case 10 (`<child>`)
| 1 | 2 | Injector size. Number of values to skip to get to Host Bindings. | 1 | 2 | Injector size. Number of values to skip to get to Host Bindings.
| 2 | Child.ngComponentDef.hostBindings | The function to call. (Only when `hostVars` is not `0`) | 2 | Child.ngComponentDef.hostBindings | The function to call. (Only when `hostVars` is not `0`)
| 3 | Child.ngComponentDef.hostVars | Number of host bindings to process. (Only when `hostVars` is not `0`) | 3 | Child.ngComponentDef.hostVars | Number of host bindings to process. (Only when `hostVars` is not `0`)
@ -215,9 +213,9 @@ The reason for this layout is to make the host binding update efficient using th
let currentDirectiveIndex = -1; let currentDirectiveIndex = -1;
let currentElementIndex = -1; let currentElementIndex = -1;
// This is global state which is used internally by hostBindings to know where the offset is // This is global state which is used internally by hostBindings to know where the offset is
let bindingRootIndex = tView.expandoStart; let bindingRootIndex = tView.expandoStartIndex;
for(var i = 0; i < tview.expandoInstructions.length; i++) { for(var i = 0; i < tView.expandoInstructions.length; i++) {
let instruction = tview.expandoInstructions[i]; let instruction = tView.expandoInstructions[i];
if (typeof instruction === 'number') { if (typeof instruction === 'number') {
// Numbers are used to update the indices. // Numbers are used to update the indices.
if (instruction < 0) { if (instruction < 0) {

View File

@ -16,11 +16,10 @@ import {assertComponentType, assertDefined} from './assert';
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery'; import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
import {getComponentDef} from './definition'; import {getComponentDef} from './definition';
import {queueInitHooks, queueLifecycleHooks} from './hooks'; import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {PlayerHandler} from './interfaces/player'; import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, prefillHostVars, setHostBindings} from './instructions';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition'; import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node'; import {LElementNode, TNodeFlags} from './interfaces/node';
import {PlayerHandler} from './interfaces/player';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view'; import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {getRootView, stringify} from './util'; import {getRootView, stringify} from './util';
@ -151,15 +150,16 @@ export function renderComponent<T>(
export function createRootComponent<T>( export function createRootComponent<T>(
elementNode: LElementNode, componentDef: ComponentDef<T>, rootView: LViewData, elementNode: LElementNode, componentDef: ComponentDef<T>, rootView: LViewData,
rootContext: RootContext, hostFeatures: HostFeature[] | null): any { rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
// Create directive instance with factory() and store at index 0 in directives array // Create directive instance with factory() and store at next index in viewData
const component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode); const component =
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, elementNode);
if (componentDef.hostBindings) queueHostBindingForCheck(0, componentDef.hostVars);
rootContext.components.push(component); rootContext.components.push(component);
(elementNode.data as LViewData)[CONTEXT] = component; (elementNode.data as LViewData)[CONTEXT] = component;
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef)); hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
setHostBindings(rootView[TVIEW].hostBindings); if (rootView[TVIEW].firstTemplatePass) prefillHostVars(componentDef.hostVars);
setHostBindings();
return component; return component;
} }
@ -190,11 +190,10 @@ export function createRootContext(
*/ */
export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void { export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void {
const rootTView = readPatchedLViewData(component) ![TVIEW]; const rootTView = readPatchedLViewData(component) ![TVIEW];
const dirIndex = rootTView.data.length - 1;
// Root component is always created at dir index 0 queueInitHooks(dirIndex, def.onInit, def.doCheck, rootTView);
queueInitHooks(0, def.onInit, def.doCheck, rootTView); queueLifecycleHooks(dirIndex << TNodeFlags.DirectiveStartingIndexShift | 1, rootTView);
// Directive starting index 0, directive count 1 -> directive flags: 1
queueLifecycleHooks(1, rootTView);
} }
/** /**

View File

@ -139,13 +139,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
// Create element node at index 0 in data array // Create element node at index 0 in data array
elementNode = hostElement(componentTag, hostNode, this.componentDef); elementNode = hostElement(componentTag, hostNode, this.componentDef);
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
component = createRootComponent(
elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]);
tElementNode = getTNode(0) as TElementNode; tElementNode = getTNode(0) as TElementNode;
// Transform the arrays of native nodes into a LNode structure that can be consumed by the // Transform the arrays of native nodes into a LNode structure that can be consumed by the
@ -168,6 +161,12 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
} }
} }
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
component = createRootComponent(
elementNode, this.componentDef, rootView, rootContext, [LifecycleHooksFeature]);
// Execute the template in creation mode only, and then turn off the CreationMode flag // Execute the template in creation mode only, and then turn off the CreationMode flag
const componentView = elementNode.data as LViewData; const componentView = elementNode.data as LViewData;
renderEmbeddedTemplate(componentView, componentView[TVIEW], component, RenderFlags.Create); renderEmbeddedTemplate(componentView, componentView[TVIEW], component, RenderFlags.Create);

View File

@ -10,7 +10,7 @@ import './ng_dev_mode';
import {assertEqual} from './assert'; import {assertEqual} from './assert';
import {LElementNode, TNode, TNodeFlags} from './interfaces/node'; import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
import {RElement} from './interfaces/renderer'; import {RElement} from './interfaces/renderer';
import {CONTEXT, DIRECTIVES, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view'; import {CONTEXT, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
/** /**
* This property will be monkey-patched on elements, components and directives * This property will be monkey-patched on elements, components and directives
@ -306,21 +306,17 @@ function findViaDirective(lViewData: LViewData, directiveInstance: {}): number {
// element bound to the directive being search lives somewhere // element bound to the directive being search lives somewhere
// in the view data. We loop through the nodes and check their // in the view data. We loop through the nodes and check their
// list of directives for the instance. // list of directives for the instance.
const directivesAcrossView = lViewData[DIRECTIVES];
let tNode = lViewData[TVIEW].firstChild; let tNode = lViewData[TVIEW].firstChild;
if (directivesAcrossView != null) { while (tNode) {
while (tNode) { const directiveIndexStart = getDirectiveStartIndex(tNode);
const directiveIndexStart = getDirectiveStartIndex(tNode); const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart); for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) { if (lViewData[i] === directiveInstance) {
if (directivesAcrossView[i] === directiveInstance) { return tNode.index;
return tNode.index;
}
} }
tNode = traverseNextElement(tNode);
} }
tNode = traverseNextElement(tNode);
} }
return -1; return -1;
} }
@ -341,24 +337,20 @@ function getLNodeFromViewData(lViewData: LViewData, lElementIndex: number): LEle
} }
/** /**
* Returns a list of directives extracted from the given view. Does not contain * Returns a list of directives extracted from the given view based on the
* the component. * provided list of directive index values.
* *
* @param nodeIndex Index of node to search * @param nodeIndex The node index
* @param lViewData The target view data * @param lViewData The target view data
* @param includeComponents Whether or not to include components in returned directives * @param includeComponents Whether or not to include components in returned directives
*/ */
export function discoverDirectives( export function discoverDirectives(
nodeIndex: number, lViewData: LViewData, includeComponents: boolean): any[]|null { nodeIndex: number, lViewData: LViewData, includeComponents: boolean): any[]|null {
const directivesAcrossView = lViewData[DIRECTIVES]; const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
if (directivesAcrossView != null) { let directiveStartIndex = getDirectiveStartIndex(tNode);
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode; const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
let directiveStartIndex = getDirectiveStartIndex(tNode); if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex); return lViewData.slice(directiveStartIndex, directiveEndIndex);
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
return directivesAcrossView.slice(directiveStartIndex, directiveEndIndex);
}
return null;
} }
/** /**
@ -375,7 +367,7 @@ export function discoverLocalRefs(lViewData: LViewData, lNodeIndex: number): {[k
const directiveIndex = tNode.localNames[i + 1] as number; const directiveIndex = tNode.localNames[i + 1] as number;
result[localRefName] = directiveIndex === -1 ? result[localRefName] = directiveIndex === -1 ?
getLNodeFromViewData(lViewData, lNodeIndex) !.native : getLNodeFromViewData(lViewData, lNodeIndex) !.native :
lViewData[DIRECTIVES] ![directiveIndex]; lViewData[directiveIndex];
} }
return result; return result;
} }

View File

@ -70,7 +70,7 @@ class Render3DebugContext implements DebugContext {
// TODO(vicb): add view providers when supported // TODO(vicb): add view providers when supported
get providerTokens(): any[] { get providerTokens(): any[] {
// TODO(vicb): why/when // TODO(vicb): why/when
const directiveDefs = this.view[TVIEW].directives; const directiveDefs = this.view[TVIEW].data;
if (this.nodeIndex === null || directiveDefs == null) { if (this.nodeIndex === null || directiveDefs == null) {
return []; return [];
} }

View File

@ -20,10 +20,10 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
import {NG_ELEMENT_ID} from './fields'; import {NG_ELEMENT_ID} from './fields';
import {_getViewData, assertPreviousIsParent, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions'; import {_getViewData, assertPreviousIsParent, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions';
import {DirectiveDef} from './interfaces/definition'; import {DirectiveDef} from './interfaces/definition';
import {INJECTOR_SIZE, InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector'; import {InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node'; import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {isProceduralRenderer} from './interfaces/renderer'; import {isProceduralRenderer} from './interfaces/renderer';
import {DECLARATION_VIEW, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, PARENT, RENDERER, TData, TVIEW, TView} from './interfaces/view'; import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, PARENT, RENDERER, TData, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert'; import {assertNodeOfPossibleTypes} from './node_assert';
/** /**
@ -104,12 +104,10 @@ export function getOrCreateNodeInjectorForNode(
const tView = hostView[TVIEW]; const tView = hostView[TVIEW];
if (tView.firstTemplatePass) { if (tView.firstTemplatePass) {
// TODO(kara): Store node injector with host bindings for that node (see VIEW_DATA.md)
tNode.injectorIndex = hostView.length; tNode.injectorIndex = hostView.length;
setUpBloom(tView.data, tNode); // foundation for node bloom setUpBloom(tView.data, tNode); // foundation for node bloom
setUpBloom(hostView, null); // foundation for cumulative bloom setUpBloom(hostView, null); // foundation for cumulative bloom
setUpBloom(tView.blueprint, null); setUpBloom(tView.blueprint, null);
tView.hostBindingStartIndex += INJECTOR_SIZE;
} }
const parentLoc = getParentInjectorLocation(tNode, hostView); const parentLoc = getParentInjectorLocation(tNode, hostView);
@ -244,7 +242,9 @@ export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T; export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
export function directiveInject<T>( export function directiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null { token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), _getViewData(), token, flags); const hostTNode =
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
return getOrCreateInjectable<T>(hostTNode, _getViewData(), token, flags);
} }
export function injectRenderer2(): Renderer2 { export function injectRenderer2(): Renderer2 {
@ -320,8 +320,8 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
* @returns the value from the injector or `null` when not found * @returns the value from the injector or `null` when not found
*/ */
export function getOrCreateInjectable<T>( export function getOrCreateInjectable<T>(
startInjectorIndex: number, hostView: LViewData, token: Type<T>| InjectionToken<T>, hostTNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData,
flags: InjectFlags = InjectFlags.Default): T|null { token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default): T|null {
const bloomHash = bloomHashBitOrFactory(token); const bloomHash = bloomHashBitOrFactory(token);
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
// so just call the factory function to create it. // so just call the factory function to create it.
@ -330,13 +330,24 @@ export function getOrCreateInjectable<T>(
// If the token has a bloom hash, then it is a directive that is public to the injection system // If the token has a bloom hash, then it is a directive that is public to the injection system
// (diPublic) otherwise fall back to the module injector. // (diPublic) otherwise fall back to the module injector.
if (bloomHash != null) { if (bloomHash != null) {
const startInjectorIndex = getInjectorIndex(hostTNode, hostView);
let injectorIndex = startInjectorIndex; let injectorIndex = startInjectorIndex;
let injectorView = hostView; let injectorView = hostView;
let parentLocation: number = -1;
if (flags & InjectFlags.SkipSelf) { // If we should skip this injector or if an injector doesn't exist on this node (e.g. all
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR]; // directives on this node are private), start by searching the parent injector.
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask; if (flags & InjectFlags.SkipSelf || injectorIndex === -1) {
injectorView = getParentInjectorView(parentLocation, injectorView); parentLocation = injectorIndex === -1 ? getParentInjectorLocation(hostTNode, hostView) :
injectorView[injectorIndex + PARENT_INJECTOR];
if (shouldNotSearchParent(flags, parentLocation)) {
injectorIndex = -1;
} else {
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
}
} }
while (injectorIndex !== -1) { while (injectorIndex !== -1) {
@ -348,9 +359,8 @@ export function getOrCreateInjectable<T>(
break; break;
} }
if (flags & InjectFlags.Self || parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
flags & InjectFlags.Host && if (shouldNotSearchParent(flags, parentLocation)) {
!sameHostView(injectorView[injectorIndex + PARENT_INJECTOR])) {
injectorIndex = -1; injectorIndex = -1;
break; break;
} }
@ -359,7 +369,6 @@ export function getOrCreateInjectable<T>(
// up to find the specific injector. If the ancestor bloom filter does not have the bit, we // up to find the specific injector. If the ancestor bloom filter does not have the bit, we
// can abort. // can abort.
if (injectorHasToken(bloomHash, injectorIndex, injectorView)) { if (injectorHasToken(bloomHash, injectorIndex, injectorView)) {
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask; injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView); injectorView = getParentInjectorView(parentLocation, injectorView);
} else { } else {
@ -390,7 +399,6 @@ export function getOrCreateInjectable<T>(
// The def wasn't found anywhere on this node, so it was a false positive. // The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching. // Traverse up the tree and continue searching.
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask; injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView); injectorView = getParentInjectorView(parentLocation, injectorView);
} }
@ -411,7 +419,7 @@ function searchMatchesQueuedForCreation<T>(token: any, hostTView: TView): T|null
for (let i = 0; i < matches.length; i += 2) { for (let i = 0; i < matches.length; i += 2) {
const def = matches[i] as DirectiveDef<any>; const def = matches[i] as DirectiveDef<any>;
if (def.type === token) { if (def.type === token) {
return resolveDirective(def, i + 1, matches, hostTView); return resolveDirective(def, i + 1, matches);
} }
} }
} }
@ -427,14 +435,14 @@ function searchDirectivesOnInjector<T>(
if (count !== 0) { if (count !== 0) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift; const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
const defs = injectorView[TVIEW].directives !; const defs = injectorView[TVIEW].data;
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic), // Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance. // and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDef<any>; const directiveDef = defs[i] as DirectiveDef<any>;
if (directiveDef.type === token && directiveDef.diPublic) { if (directiveDef.type === token && directiveDef.diPublic) {
return injectorView[DIRECTIVES] ![i]; return injectorView[i];
} }
} }
} }
@ -486,15 +494,10 @@ export function injectorHasToken(
return !!(value & mask); return !!(value & mask);
} }
/** Returns true if flags prevent parent injector from being searched for tokens */
/** function shouldNotSearchParent(flags: InjectFlags, parentLocation: number): boolean|number {
* Checks whether the current injector and its parent are in the same host view. return flags & InjectFlags.Self ||
* (flags & InjectFlags.Host && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) > 0);
* This is necessary to support @Host() decorators. If @Host() is set, we should stop searching once
* the injector and its parent view don't match because it means we'd cross the view boundary.
*/
function sameHostView(parentLocation: number): boolean {
return !!parentLocation && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) === 0;
} }
export class NodeInjector implements Injector { export class NodeInjector implements Injector {
@ -512,7 +515,7 @@ export class NodeInjector implements Injector {
} }
setEnvironment(this._tNode, this._hostView); setEnvironment(this._tNode, this._hostView);
return getOrCreateInjectable(this._injectorIndex, this._hostView, token); return getOrCreateInjectable(this._tNode, this._hostView, token);
} }
} }
export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null { export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {

View File

@ -9,7 +9,7 @@
import {assertEqual} from './assert'; import {assertEqual} from './assert';
import {DirectiveDef} from './interfaces/definition'; import {DirectiveDef} from './interfaces/definition';
import {TNodeFlags} from './interfaces/node'; import {TNodeFlags} from './interfaces/node';
import {DIRECTIVES, FLAGS, HookData, LViewData, LViewFlags, TView} from './interfaces/view'; import {FLAGS, HookData, LViewData, LViewFlags, TView} from './interfaces/view';
/** /**
@ -20,7 +20,7 @@ import {DIRECTIVES, FLAGS, HookData, LViewData, LViewFlags, TView} from './inter
* directive index), then saved in the even indices of the initHooks array. The odd indices * directive index), then saved in the even indices of the initHooks array. The odd indices
* hold the hook functions themselves. * hold the hook functions themselves.
* *
* @param index The index of the directive in LViewData[DIRECTIVES] * @param index The index of the directive in LViewData
* @param hooks The static hooks map on the directive def * @param hooks The static hooks map on the directive def
* @param tView The current TView * @param tView The current TView
*/ */
@ -52,7 +52,7 @@ export function queueLifecycleHooks(flags: number, tView: TView): void {
// directiveCreate) so we can preserve the current hook order. Content, view, and destroy // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
// hooks for projected components and directives must be called *before* their hosts. // hooks for projected components and directives must be called *before* their hosts.
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
const def: DirectiveDef<any> = tView.directives ![i]; const def = tView.data[i] as DirectiveDef<any>;
queueContentHooks(def, tView, i); queueContentHooks(def, tView, i);
queueViewHooks(def, tView, i); queueViewHooks(def, tView, i);
queueDestroyHooks(def, tView, i); queueDestroyHooks(def, tView, i);
@ -99,7 +99,7 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
export function executeInitHooks( export function executeInitHooks(
currentView: LViewData, tView: TView, creationMode: boolean): void { currentView: LViewData, tView: TView, creationMode: boolean): void {
if (currentView[FLAGS] & LViewFlags.RunInit) { if (currentView[FLAGS] & LViewFlags.RunInit) {
executeHooks(currentView[DIRECTIVES] !, tView.initHooks, tView.checkHooks, creationMode); executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
currentView[FLAGS] &= ~LViewFlags.RunInit; currentView[FLAGS] &= ~LViewFlags.RunInit;
} }
} }
@ -110,7 +110,7 @@ export function executeInitHooks(
* @param currentView The current view * @param currentView The current view
*/ */
export function executeHooks( export function executeHooks(
data: any[], allHooks: HookData | null, checkHooks: HookData | null, data: LViewData, allHooks: HookData | null, checkHooks: HookData | null,
creationMode: boolean): void { creationMode: boolean): void {
const hooksToCall = creationMode ? allHooks : checkHooks; const hooksToCall = creationMode ? allHooks : checkHooks;
if (hooksToCall) { if (hooksToCall) {
@ -125,8 +125,8 @@ export function executeHooks(
* @param currentView The current view * @param currentView The current view
* @param arr The array in which the hooks are found * @param arr The array in which the hooks are found
*/ */
export function callHooks(data: any[], arr: HookData): void { export function callHooks(currentView: any[], arr: HookData): void {
for (let i = 0; i < arr.length; i += 2) { for (let i = 0; i < arr.length; i += 2) {
(arr[i + 1] as() => void).call(data[arr[i] as number]); (arr[i + 1] as() => void).call(currentView[arr[i] as number]);
} }
} }

View File

@ -61,7 +61,6 @@ export {
listener, listener,
store, store,
load, load,
loadDirective,
namespaceHTML, namespaceHTML,
namespaceMathML, namespaceMathML,

View File

@ -18,12 +18,13 @@ import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComp
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks'; import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container'; import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition'; import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {INJECTOR_SIZE} from './interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node'; import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query'; import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling'; import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view'; import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation'; import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
@ -42,15 +43,6 @@ const _CLEAN_PROMISE = Promise.resolve(null);
*/ */
export type SanitizerFn = (value: any) => string; export type SanitizerFn = (value: any) => string;
/**
* TView.data needs to fill the same number of slots as the LViewData header
* so the indices of nodes are consistent between LViewData and TView.data.
*
* It's much faster to keep a blueprint of the pre-filled array and slice it
* than it is to create a new array and fill it each time a TView is created.
*/
const HEADER_FILLER = new Array(HEADER_OFFSET).fill(null);
/** /**
* Token set in currentMatches while dependencies are being resolved. * Token set in currentMatches while dependencies are being resolved.
* *
@ -230,14 +222,6 @@ export function _getViewData(): LViewData {
*/ */
let contextViewData: LViewData = null !; let contextViewData: LViewData = null !;
/**
* An array of directive instances in the current view.
*
* These must be stored separately from LNodes because their presence is
* unknown at compile-time and thus space cannot be reserved in data[].
*/
let directives: any[]|null;
function getCleanup(view: LViewData): any[] { function getCleanup(view: LViewData): any[] {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) // top level variables should not be exported for performance reasons (PERF_NOTES.md)
return view[CLEANUP] || (view[CLEANUP] = []); return view[CLEANUP] || (view[CLEANUP] = []);
@ -259,7 +243,7 @@ let firstTemplatePass = true;
/** /**
* The root index from which pure function instructions should calculate their binding * The root index from which pure function instructions should calculate their binding
* indices. In component views, this is TView.bindingStartIndex. In a host binding * indices. In component views, this is TView.bindingStartIndex. In a host binding
* context, this is the TView.hostBindingStartIndex + any hostVars before the given dir. * context, this is the TView.expandoStartIndex + any dirs/hostVars before the given dir.
*/ */
let bindingRootIndex: number = -1; let bindingRootIndex: number = -1;
@ -268,6 +252,9 @@ export function getBindingRoot() {
return bindingRootIndex; return bindingRootIndex;
} }
// Root component will always have an element index of 0 and an injector size of 1
const ROOT_EXPANDO_INSTRUCTIONS = [0, 1];
const enum BindingDirection { const enum BindingDirection {
Input, Input,
Output, Output,
@ -288,7 +275,6 @@ const enum BindingDirection {
export function enterView( export function enterView(
newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData { newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData {
const oldView: LViewData = viewData; const oldView: LViewData = viewData;
directives = newView && newView[DIRECTIVES];
tView = newView && newView[TVIEW]; tView = newView && newView[TVIEW];
creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode; creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
@ -317,7 +303,7 @@ export function enterView(
export function leaveView(newView: LViewData, creationOnly?: boolean): void { export function leaveView(newView: LViewData, creationOnly?: boolean): void {
if (!creationOnly) { if (!creationOnly) {
if (!checkNoChangesMode) { if (!checkNoChangesMode) {
executeHooks(directives !, tView.viewHooks, tView.viewCheckHooks, creationMode); executeHooks(viewData, tView.viewHooks, tView.viewCheckHooks, creationMode);
} }
// Views are clean and in update mode after being checked, so these bits are cleared // Views are clean and in update mode after being checked, so these bits are cleared
viewData[FLAGS] &= ~(LViewFlags.CreationMode | LViewFlags.Dirty); viewData[FLAGS] &= ~(LViewFlags.CreationMode | LViewFlags.Dirty);
@ -334,7 +320,7 @@ export function leaveView(newView: LViewData, creationOnly?: boolean): void {
* Note: view hooks are triggered later when leaving the view. * Note: view hooks are triggered later when leaving the view.
*/ */
function refreshDescendantViews() { function refreshDescendantViews() {
setHostBindings(tView.hostBindings); setHostBindings();
const parentFirstTemplatePass = firstTemplatePass; const parentFirstTemplatePass = firstTemplatePass;
// This needs to be set before children are processed to support recursive components // This needs to be set before children are processed to support recursive components
@ -349,7 +335,7 @@ function refreshDescendantViews() {
refreshContentQueries(tView); refreshContentQueries(tView);
if (!checkNoChangesMode) { if (!checkNoChangesMode) {
executeHooks(directives !, tView.contentHooks, tView.contentCheckHooks, creationMode); executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
} }
refreshChildComponents(tView.components, parentFirstTemplatePass); refreshChildComponents(tView.components, parentFirstTemplatePass);
@ -357,21 +343,38 @@ function refreshDescendantViews() {
/** Sets the host bindings for the current view. */ /** Sets the host bindings for the current view. */
export function setHostBindings(bindings: number[] | null): void { export function setHostBindings(): void {
if (bindings != null) { if (tView.expandoInstructions) {
bindingRootIndex = viewData[BINDING_INDEX] = tView.hostBindingStartIndex; bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
const defs = tView.directives !; let currentDirectiveIndex = -1;
for (let i = 0; i < bindings.length; i += 2) { let currentElementIndex = -1;
const dirIndex = bindings[i]; for (let i = 0; i < tView.expandoInstructions.length; i++) {
const def = defs[dirIndex] as DirectiveDef<any>; const instruction = tView.expandoInstructions[i];
if (firstTemplatePass) { if (typeof instruction === 'number') {
for (let i = 0; i < def.hostVars; i++) { if (instruction <= 0) {
tView.blueprint.push(NO_CHANGE); // Negative numbers mean that we are starting new EXPANDO block and need to update
viewData.push(NO_CHANGE); // the current element and directive index.
currentElementIndex = -instruction;
if (typeof viewData[bindingRootIndex] === 'number') {
// We've hit an injector. It may or may not exist depending on whether
// there is a public directive on this node.
bindingRootIndex += INJECTOR_SIZE;
}
currentDirectiveIndex = bindingRootIndex;
} else {
// This is either the injector size (so the binding root can skip over directives
// and get to the first set of host bindings on this node) or the host var count
// (to get to the next set of host bindings on this node).
bindingRootIndex += instruction;
} }
} else {
// If it's not a number, it's a host binding function that needs to be executed.
viewData[BINDING_INDEX] = bindingRootIndex;
// We must subtract the header offset because the load() instruction
// expects a raw, unadjusted index.
instruction(currentDirectiveIndex - HEADER_OFFSET, currentElementIndex);
currentDirectiveIndex++;
} }
def.hostBindings !(dirIndex, bindings[i + 1]);
bindingRootIndex = viewData[BINDING_INDEX] = bindingRootIndex + def.hostVars;
} }
} }
} }
@ -381,9 +384,10 @@ function refreshContentQueries(tView: TView): void {
if (tView.contentQueries != null) { if (tView.contentQueries != null) {
for (let i = 0; i < tView.contentQueries.length; i += 2) { for (let i = 0; i < tView.contentQueries.length; i += 2) {
const directiveDefIdx = tView.contentQueries[i]; const directiveDefIdx = tView.contentQueries[i];
const directiveDef = tView.directives ![directiveDefIdx]; const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>;
directiveDef.contentQueriesRefresh !(directiveDefIdx, tView.contentQueries[i + 1]); directiveDef.contentQueriesRefresh !(
directiveDefIdx - HEADER_OFFSET, tView.contentQueries[i + 1]);
} }
} }
} }
@ -401,7 +405,7 @@ function refreshChildComponents(
export function executeInitAndContentHooks(): void { export function executeInitAndContentHooks(): void {
if (!checkNoChangesMode) { if (!checkNoChangesMode) {
executeInitHooks(viewData, tView, creationMode); executeInitHooks(viewData, tView, creationMode);
executeHooks(directives !, tView.contentHooks, tView.contentCheckHooks, creationMode); executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
} }
} }
@ -538,7 +542,7 @@ export function createNodeAtIndex(
export function adjustBlueprintForNewNode(view: LViewData) { export function adjustBlueprintForNewNode(view: LViewData) {
const tView = view[TVIEW]; const tView = view[TVIEW];
if (tView.firstTemplatePass) { if (tView.firstTemplatePass) {
tView.hostBindingStartIndex++; tView.expandoStartIndex++;
tView.blueprint.push(null); tView.blueprint.push(null);
view.push(null); view.push(null);
} }
@ -658,10 +662,14 @@ export function renderEmbeddedTemplate<T>(
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
refreshDescendantViews(); refreshDescendantViews();
} else { } else {
// This must be set to false immediately after the first creation run because in an
// ngFor loop, all the views will be created together before update mode runs and turns
// off firstTemplatePass. If we don't set it here, instances will perform directive
// matching, etc again and again.
viewToRender[TVIEW].firstTemplatePass = firstTemplatePass = false; viewToRender[TVIEW].firstTemplatePass = firstTemplatePass = false;
} }
} finally { } finally {
// renderEmbeddedTemplate() is called twice in fact, once for creation only and then once for // renderEmbeddedTemplate() is called twice, once for creation only and then once for
// update. When for creation only, leaveView() must not trigger view hooks, nor clean flags. // update. When for creation only, leaveView() must not trigger view hooks, nor clean flags.
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create; const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
leaveView(oldView !, isCreationOnly); leaveView(oldView !, isCreationOnly);
@ -702,7 +710,7 @@ export function renderComponentOrTemplate<T>(
// Element was stored at 0 in data and directive was stored at 0 in directives // Element was stored at 0 in data and directive was stored at 0 in directives
// in renderComponent() // in renderComponent()
setHostBindings(tView.hostBindings); setHostBindings();
componentRefresh(HEADER_OFFSET, false); componentRefresh(HEADER_OFFSET, false);
} }
} finally { } finally {
@ -905,15 +913,45 @@ function cacheMatchingDirectivesForNode(
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in tsickle. // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in tsickle.
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null; const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
const matches = tView.currentMatches = findDirectiveMatches(tNode); const matches = tView.currentMatches = findDirectiveMatches(tNode);
generateExpandoBlock(tNode, matches);
let totalHostVars = 0;
if (matches) { if (matches) {
for (let i = 0; i < matches.length; i += 2) { for (let i = 0; i < matches.length; i += 2) {
const def = matches[i] as DirectiveDef<any>; const def = matches[i] as DirectiveDef<any>;
const valueIndex = i + 1; const valueIndex = i + 1;
resolveDirective(def, valueIndex, matches, tView); resolveDirective(def, valueIndex, matches);
totalHostVars += def.hostVars;
saveNameToExportMap(matches[valueIndex] as number, def, exportsMap); saveNameToExportMap(matches[valueIndex] as number, def, exportsMap);
} }
} }
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap); if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
prefillHostVars(totalHostVars);
}
/**
* Generates a new block in TView.expandoInstructions for this node.
*
* Each expando block starts with the element index (turned negative so we can distinguish
* it from the hostVar count) and the directive count. See more in VIEW_DATA.md.
*/
function generateExpandoBlock(tNode: TNode, matches: CurrentMatchesList | null): void {
const directiveCount = matches ? matches.length / 2 : 0;
const elementIndex = -(tNode.index - HEADER_OFFSET);
(tView.expandoInstructions || (tView.expandoInstructions = [
])).push(elementIndex, directiveCount);
}
/**
* On the first template pass, we need to reserve space for host binding values
* after directives are matched (so all directives are saved, then bindings).
* Because we are updating the blueprint, we only need to do this once.
*/
export function prefillHostVars(totalHostVars: number): void {
for (let i = 0; i < totalHostVars; i++) {
viewData.push(NO_CHANGE);
tView.blueprint.push(NO_CHANGE);
tView.data.push(null);
}
} }
/** Matches the current node against all available selectors. */ /** Matches the current node against all available selectors. */
@ -925,17 +963,16 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
const def = registry[i]; const def = registry[i];
if (isNodeMatchingSelectorList(tNode, def.selectors !)) { if (isNodeMatchingSelectorList(tNode, def.selectors !)) {
matches || (matches = []); matches || (matches = []);
if (def.diPublic) def.diPublic(def);
if ((def as ComponentDef<any>).template) { if ((def as ComponentDef<any>).template) {
if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode); if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode);
addComponentLogic(def as ComponentDef<any>); addComponentLogic(def as ComponentDef<any>);
tNode.flags = TNodeFlags.isComponent;
// The component is always stored first with directives after. // The component is always stored first with directives after.
matches.unshift(def, null); matches.unshift(def, null);
} else { } else {
matches.push(def, null); matches.push(def, null);
} }
if (def.diPublic) def.diPublic(def);
} }
} }
} }
@ -943,12 +980,11 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
} }
export function resolveDirective( export function resolveDirective(
def: DirectiveDef<any>, valueIndex: number, matches: CurrentMatchesList, tView: TView): any { def: DirectiveDef<any>, valueIndex: number, matches: CurrentMatchesList): any {
if (matches[valueIndex] === null) { if (matches[valueIndex] === null) {
matches[valueIndex] = CIRCULAR; matches[valueIndex] = CIRCULAR;
const instance = def.factory(); const instance = def.factory();
(tView.directives || (tView.directives = [])).push(def); return directiveCreate(matches[valueIndex] = viewData.length, instance, def);
return directiveCreate(matches[valueIndex] = tView.directives !.length - 1, instance, def);
} else if (matches[valueIndex] === CIRCULAR) { } else if (matches[valueIndex] === CIRCULAR) {
// If we revisit this directive before it's resolved, we know it's circular // If we revisit this directive before it's resolved, we know it's circular
throwCyclicDependencyError(def.type); throwCyclicDependencyError(def.type);
@ -965,13 +1001,11 @@ function queueComponentIndexForCheck(): void {
/** Stores index of directive and host element so it will be queued for binding refresh during CD. /** Stores index of directive and host element so it will be queued for binding refresh during CD.
*/ */
export function queueHostBindingForCheck(dirIndex: number, hostVars: number): void { export function queueHostBindingForCheck(
// Must subtract the header offset because hostBindings functions are generated with dirIndex: number, def: DirectiveDef<any>| ComponentDef<any>): void {
// instructions that expect element indices that are NOT adjusted (e.g. elementProperty).
ngDevMode && ngDevMode &&
assertEqual(firstTemplatePass, true, 'Should only be called in first template pass.'); assertEqual(firstTemplatePass, true, 'Should only be called in first template pass.');
(tView.hostBindings || (tView.hostBindings = [ tView.expandoInstructions !.push(def.hostBindings !, def.hostVars);
])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET);
} }
/** /**
@ -990,10 +1024,9 @@ function instantiateDirectivesDirectly() {
if (count > 0) { if (count > 0) {
const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift; const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
const tDirectives = tView.directives !;
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
const def: DirectiveDef<any> = tDirectives[i]; const def = tView.data[i] as DirectiveDef<any>| ComponentDef<any>;
// Component view must be set on node before the factory is created so // Component view must be set on node before the factory is created so
// ChangeDetectorRefs have a way to store component view on creation. // ChangeDetectorRefs have a way to store component view on creation.
@ -1046,7 +1079,7 @@ function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
let localIndex = previousOrParentTNode.index + 1; let localIndex = previousOrParentTNode.index + 1;
for (let i = 0; i < localNames.length; i += 2) { for (let i = 0; i < localNames.length; i += 2) {
const index = localNames[i + 1] as number; const index = localNames[i + 1] as number;
const value = index === -1 ? localRefExtractor(tNode, viewData) : directives ![index]; const value = index === -1 ? localRefExtractor(tNode, viewData) : viewData[index];
viewData[localIndex++] = value; viewData[localIndex++] = value;
} }
} }
@ -1108,8 +1141,9 @@ export function createTView(
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LViewData data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LViewData
childIndex: -1, // Children set in addToViewTree(), if any childIndex: -1, // Children set in addToViewTree(), if any
bindingStartIndex: bindingStartIndex, bindingStartIndex: bindingStartIndex,
hostBindingStartIndex: initialViewLength, expandoStartIndex: initialViewLength,
directives: null, directives: null,
expandoInstructions: null,
firstTemplatePass: true, firstTemplatePass: true,
initHooks: null, initHooks: null,
checkHooks: null, checkHooks: null,
@ -1120,7 +1154,6 @@ export function createTView(
destroyHooks: null, destroyHooks: null,
pipeDestroyHooks: null, pipeDestroyHooks: null,
cleanup: null, cleanup: null,
hostBindings: null,
contentQueries: null, contentQueries: null,
components: null, components: null,
directiveRegistry: typeof directives === 'function' ? directives() : directives, directiveRegistry: typeof directives === 'function' ? directives() : directives,
@ -1224,9 +1257,10 @@ export function hostElement(
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer)); null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
if (firstTemplatePass) { if (firstTemplatePass) {
tNode.flags = TNodeFlags.isComponent; tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
if (def.diPublic) def.diPublic(def); if (def.diPublic) def.diPublic(def);
tView.directives = [def]; tNode.flags =
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
} }
return viewData[HEADER_OFFSET]; return viewData[HEADER_OFFSET];
} }
@ -1289,8 +1323,8 @@ export function listener(
*/ */
function createOutput(outputs: PropertyAliasValue, listener: Function): void { function createOutput(outputs: PropertyAliasValue, listener: Function): void {
for (let i = 0; i < outputs.length; i += 2) { for (let i = 0; i < outputs.length; i += 2) {
ngDevMode && assertDataInRange(outputs[i] as number, directives !); ngDevMode && assertDataInRange(outputs[i] as number, viewData);
const subscription = directives ![outputs[i] as number][outputs[i + 1]].subscribe(listener); const subscription = viewData[outputs[i] as number][outputs[i + 1]].subscribe(listener);
storeCleanupWithContext(viewData, subscription, subscription.unsubscribe); storeCleanupWithContext(viewData, subscription, subscription.unsubscribe);
} }
} }
@ -1498,8 +1532,8 @@ export function createTNode(
*/ */
function setInputsForProperty(inputs: PropertyAliasValue, value: any): void { function setInputsForProperty(inputs: PropertyAliasValue, value: any): void {
for (let i = 0; i < inputs.length; i += 2) { for (let i = 0; i < inputs.length; i += 2) {
ngDevMode && assertDataInRange(inputs[i] as number, directives !); ngDevMode && assertDataInRange(inputs[i] as number, viewData);
directives ![inputs[i] as number][inputs[i + 1]] = value; viewData[inputs[i] as number][inputs[i + 1]] = value;
} }
} }
@ -1519,7 +1553,7 @@ function generatePropertyAliases(
const start = tNodeFlags >> TNodeFlags.DirectiveStartingIndexShift; const start = tNodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
const isInput = direction === BindingDirection.Input; const isInput = direction === BindingDirection.Input;
const defs = tView.directives !; const defs = tView.data;
for (let i = start; i < end; i++) { for (let i = start; i < end; i++) {
const directiveDef = defs[i] as DirectiveDef<any>; const directiveDef = defs[i] as DirectiveDef<any>;
@ -1775,8 +1809,6 @@ export function directiveCreate<T>(
// Init hooks are queued now so ngOnInit is called in host components before // Init hooks are queued now so ngOnInit is called in host components before
// any projected components. // any projected components.
queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView); queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView);
if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars);
} }
ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode'); ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
@ -1810,7 +1842,11 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
(hostNode as{data: LViewData}).data = componentView; (hostNode as{data: LViewData}).data = componentView;
(componentView as LViewData)[HOST_NODE] = previousOrParentTNode as TElementNode; (componentView as LViewData)[HOST_NODE] = previousOrParentTNode as TElementNode;
if (firstTemplatePass) queueComponentIndexForCheck(); if (firstTemplatePass) {
queueComponentIndexForCheck();
previousOrParentTNode.flags =
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
}
} }
/** /**
@ -1832,14 +1868,11 @@ export function baseDirectiveCreate<T>(
attachPatchData(hostNode.native, viewData); attachPatchData(hostNode.native, viewData);
} }
if (directives == null) viewData[DIRECTIVES] = directives = []; viewData[index] = directive;
ngDevMode && assertDataNext(index, directives);
directives[index] = directive;
if (firstTemplatePass) { if (firstTemplatePass) {
const flags = previousOrParentTNode.flags; const flags = previousOrParentTNode.flags;
if ((flags & TNodeFlags.DirectiveCountMask) === 0) { if (flags === 0) {
// When the first directive is created: // When the first directive is created:
// - save the index, // - save the index,
// - set the number of directives to 1 // - set the number of directives to 1
@ -1852,6 +1885,10 @@ export function baseDirectiveCreate<T>(
'Reached the max number of directives'); 'Reached the max number of directives');
previousOrParentTNode.flags++; previousOrParentTNode.flags++;
} }
tView.data.push(directiveDef);
tView.blueprint.push(null);
if (directiveDef.hostBindings) queueHostBindingForCheck(index, directiveDef);
} else { } else {
const diPublic = directiveDef !.diPublic; const diPublic = directiveDef !.diPublic;
if (diPublic) diPublic(directiveDef !); if (diPublic) diPublic(directiveDef !);
@ -2828,13 +2865,6 @@ function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
return currentView; return currentView;
} }
/** Retrieves a value from the `directives` array. */
export function loadDirective<T>(index: number): T {
ngDevMode && assertDefined(directives, 'Directives array should be defined if reading a dir.');
ngDevMode && assertDataInRange(index, directives !);
return directives ![index];
}
export function loadQueryList<T>(queryListIdx: number): QueryList<T> { export function loadQueryList<T>(queryListIdx: number): QueryList<T> {
ngDevMode && assertDefined( ngDevMode && assertDefined(
viewData[CONTENT_QUERIES], viewData[CONTENT_QUERIES],
@ -2918,7 +2948,7 @@ export function registerContentQuery<Q>(queryList: QueryList<Q>): void {
const savedContentQueriesLength = const savedContentQueriesLength =
(viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList); (viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList);
if (firstTemplatePass) { if (firstTemplatePass) {
const currentDirectiveIndex = directives !.length - 1; const currentDirectiveIndex = viewData.length - 1;
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []); const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
const lastSavedDirectiveIndex = const lastSavedDirectiveIndex =
tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 2] : -1; tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 2] : -1;

View File

@ -145,7 +145,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
hostVars: number; hostVars: number;
/** Refreshes host bindings on the associated directive. */ /** Refreshes host bindings on the associated directive. */
hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null; hostBindings: HostBindingsFunction|null;
/** /**
* Static attributes to set on host element. * Static attributes to set on host element.
@ -330,6 +330,8 @@ export type DirectiveTypeList =
(DirectiveDef<any>| ComponentDef<any>| (DirectiveDef<any>| ComponentDef<any>|
Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[]; Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[];
export type HostBindingsFunction = (directiveIndex: number, elementIndex: number) => void;
/** /**
* Type used for PipeDefs on component definition. * Type used for PipeDefs on component definition.
* *

View File

@ -12,13 +12,13 @@ import {Sanitizer} from '../../sanitization/security';
import {PlayerHandler} from '../interfaces/player'; import {PlayerHandler} from '../interfaces/player';
import {LContainer} from './container'; import {LContainer} from './container';
import {ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef, PipeDefList} from './definition'; import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
import {LElementNode, LViewNode, TElementNode, TNode, TViewNode} from './node'; import {TElementNode, TNode, TViewNode} from './node';
import {LQueries} from './query'; import {LQueries} from './query';
import {Renderer3} from './renderer'; import {Renderer3} from './renderer';
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */ /** Size of LViewData's header. Necessary to adjust for it when setting slots. */
export const HEADER_OFFSET = 17; export const HEADER_OFFSET = 16;
// Below are constants for LViewData indices to help us look up LViewData members // Below are constants for LViewData indices to help us look up LViewData members
// without having to remember the specific indices. // without having to remember the specific indices.
@ -30,16 +30,15 @@ export const QUERIES = 3;
export const FLAGS = 4; export const FLAGS = 4;
export const HOST_NODE = 5; export const HOST_NODE = 5;
export const BINDING_INDEX = 6; export const BINDING_INDEX = 6;
export const DIRECTIVES = 7; export const CLEANUP = 7;
export const CLEANUP = 8; export const CONTEXT = 8;
export const CONTEXT = 9; export const INJECTOR = 9;
export const INJECTOR = 10; export const RENDERER = 10;
export const RENDERER = 11; export const SANITIZER = 11;
export const SANITIZER = 12; export const TAIL = 12;
export const TAIL = 13; export const CONTAINER_INDEX = 13;
export const CONTAINER_INDEX = 14; export const CONTENT_QUERIES = 14;
export const CONTENT_QUERIES = 15; export const DECLARATION_VIEW = 15;
export const DECLARATION_VIEW = 16;
// This interface replaces the real LViewData interface if it is an arg or a // This interface replaces the real LViewData interface if it is an arg or a
// return value of a public instruction. This ensures we don't need to expose // return value of a public instruction. This ensures we don't need to expose
@ -115,15 +114,6 @@ export interface LViewData extends Array<any> {
*/ */
[BINDING_INDEX]: number; [BINDING_INDEX]: number;
/**
* An array of directive instances in the current view.
*
* These must be stored separately from LNodes because their presence is
* unknown at compile-time and thus space cannot be reserved in data[].
*/
// TODO: flatten into LViewData[]
[DIRECTIVES]: any[]|null;
/** /**
* When a view is destroyed, listeners need to be released and outputs need to be * When a view is destroyed, listeners need to be released and outputs need to be
* unsubscribed. This context array stores both listener functions wrapped with * unsubscribed. This context array stores both listener functions wrapped with
@ -307,11 +297,16 @@ export interface TView {
bindingStartIndex: number; bindingStartIndex: number;
/** /**
* The index at which the data array begins to store host bindings for components * The index where the "expando" section of `LViewData` begins. The expando
* or directives in its template. Saving this value ensures that we can set the * section contains injectors, directive instances, and host binding values.
* binding root and binding index correctly before checking host bindings. * Unlike the "consts" and "vars" sections of `LViewData`, the length of this
* section cannot be calculated at compile-time because directives are matched
* at runtime to preserve locality.
*
* We store this start index so we know where to start checking host bindings
* in `setHostBindings`.
*/ */
hostBindingStartIndex: number; expandoStartIndex: number;
/** /**
* Index of the host node of the first LView or LContainer beneath this LView in * Index of the host node of the first LView or LContainer beneath this LView in
@ -357,6 +352,13 @@ export interface TView {
*/ */
directives: DirectiveDefList|null; directives: DirectiveDefList|null;
/**
* Set of instructions used to process host bindings efficiently.
*
* See VIEW_DATA.md for more information.
*/
expandoInstructions: (number|HostBindingsFunction)[]|null;
/** /**
* Full registry of directives and components that may be found in this view. * Full registry of directives and components that may be found in this view.
* *
@ -479,18 +481,6 @@ export interface TView {
*/ */
components: number[]|null; components: number[]|null;
/**
* A list of indices for child directives that have host bindings.
*
* Even indices: Directive indices
* Odd indices: Element indices
*
* Element indices are NOT adjusted for LViewData header offset because
* they will be fed into instructions that expect the raw index (e.g. elementProperty)
*/
hostBindings: number[]|null;
/** /**
* A list of indices for child directives that have content queries. * A list of indices for child directives that have content queries.
* *
@ -558,7 +548,7 @@ export type HookData = (number | (() => void))[];
* *
* Injector bloom filters are also stored here. * Injector bloom filters are also stored here.
*/ */
export type TData = (TNode | PipeDef<any>| number | null)[]; export type TData = (TNode | PipeDef<any>| DirectiveDef<any>| ComponentDef<any>| number | null)[];
/** Type for TView.currentMatches */ /** Type for TView.currentMatches */
export type CurrentMatchesList = [DirectiveDef<any>, (string | number | null)]; export type CurrentMatchesList = [DirectiveDef<any>, (string | number | null)];

View File

@ -41,7 +41,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵnextContext': r3.nextContext, 'ɵnextContext': r3.nextContext,
'ɵcontainerRefreshStart': r3.containerRefreshStart, 'ɵcontainerRefreshStart': r3.containerRefreshStart,
'ɵcontainerRefreshEnd': r3.containerRefreshEnd, 'ɵcontainerRefreshEnd': r3.containerRefreshEnd,
'ɵloadDirective': r3.loadDirective,
'ɵloadQueryList': r3.loadQueryList, 'ɵloadQueryList': r3.loadQueryList,
'ɵnamespaceHTML': r3.namespaceHTML, 'ɵnamespaceHTML': r3.namespaceHTML,
'ɵnamespaceMathML': r3.namespaceMathML, 'ɵnamespaceMathML': r3.namespaceMathML,

View File

@ -13,7 +13,7 @@ import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unuse
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node'; import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
import {assertNodeType} from './node_assert'; import {assertNodeType} from './node_assert';
import {getLNode, stringify} from './util'; import {getLNode, stringify} from './util';
@ -499,7 +499,7 @@ function executeOnDestroys(view: LViewData): void {
const tView = view[TVIEW]; const tView = view[TVIEW];
let destroyHooks: HookData|null; let destroyHooks: HookData|null;
if (tView != null && (destroyHooks = tView.destroyHooks) != null) { if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
callHooks(view[DIRECTIVES] !, destroyHooks); callHooks(view, destroyHooks);
} }
} }

View File

@ -11,17 +11,19 @@ import {bindingUpdated, bindingUpdated2, bindingUpdated4, updateBinding, getBind
/** /**
* Bindings for pure functions are stored after regular bindings. * Bindings for pure functions are stored after regular bindings.
* *
* |--------consts--------|----------------vars----------------|------ hostVars (dir1) ------| * |------consts------|---------vars---------| |----- hostVars (dir1) ------|
* --------------------------------------------------------------------------------------------- * ------------------------------------------------------------------------------------------
* | nodes / refs / pipes | bindings | pure function bindings | host bindings | host slots | * | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
* --------------------------------------------------------------------------------------------- * ------------------------------------------------------------------------------------------
* ^ ^ * ^ ^
* TView.bindingStartIndex TView.hostBindingStartIndex * TView.bindingStartIndex TView.expandoStartIndex
* *
* Pure function instructions are given an offset from the binding root. Adding the offset to the * Pure function instructions are given an offset from the binding root. Adding the offset to the
* binding root gives the first index where the bindings are stored. In component views, the binding * binding root gives the first index where the bindings are stored. In component views, the binding
* root is the bindingStartIndex. In host bindings, the binding root is the hostBindingStartIndex + * root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
* any hostVars in directives evaluated before it. * any directive instances + any hostVars in directives evaluated before it.
*
* See VIEW_DATA.md for more information about host binding resolution.
*/ */
/** /**

View File

@ -24,7 +24,7 @@ import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfac
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
import {DIRECTIVES, LViewData, TVIEW} from './interfaces/view'; import {LViewData, TVIEW} from './interfaces/view';
import {flatten, isContentQueryHost} from './util'; import {flatten, isContentQueryHost} from './util';
import {createElementRef, createTemplateRef} from './view_engine_compatibility'; import {createElementRef, createTemplateRef} from './view_engine_compatibility';
@ -251,7 +251,7 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
*/ */
function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type<any>): number| function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type<any>): number|
null { null {
const defs = currentView[TVIEW].directives; const defs = currentView[TVIEW].data;
if (defs) { if (defs) {
const flags = tNode.flags; const flags = tNode.flags;
const count = flags & TNodeFlags.DirectiveCountMask; const count = flags & TNodeFlags.DirectiveCountMask;
@ -275,7 +275,7 @@ function queryRead(tNode: TNode, currentView: LViewData, read: any): any {
} else { } else {
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>); const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
if (matchingIdx !== null) { if (matchingIdx !== null) {
return currentView[DIRECTIVES] ![matchingIdx]; return currentView[matchingIdx];
} }
} }
return null; return null;
@ -314,7 +314,7 @@ function add(
result = queryRead(tNode, currentView, predicate.read); result = queryRead(tNode, currentView, predicate.read);
} else { } else {
if (directiveIdx > -1) { if (directiveIdx > -1) {
result = currentView[DIRECTIVES] ![directiveIdx]; result = currentView[directiveIdx];
} else { } else {
// if read token and / or strategy is not specified, // if read token and / or strategy is not specified,
// detect it using appropriate tNode type // detect it using appropriate tNode type

View File

@ -22,4 +22,4 @@ import {createTemplateRef} from './view_engine_compatibility';
*/ */
export function templateRefExtractor(tNode: TNode, currentView: LViewData) { export function templateRefExtractor(tNode: TNode, currentView: LViewData) {
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView); return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
} }

View File

@ -13,7 +13,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEn
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions'; import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
import {TViewNode} from './interfaces/node'; import {TViewNode} from './interfaces/node';
import {DIRECTIVES, FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view'; import {FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view';
import {destroyLView} from './node_manipulation'; import {destroyLView} from './node_manipulation';
@ -256,7 +256,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; } attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
private _lookUpContext(): T { private _lookUpContext(): T {
return this._context = this._view[PARENT] ![DIRECTIVES] ![this._componentIndex] as T; return this._context = this._view[PARENT] ![this._componentIndex] as T;
} }
} }

View File

@ -38,9 +38,6 @@
{ {
"name": "DECLARATION_VIEW" "name": "DECLARATION_VIEW"
}, },
{
"name": "DIRECTIVES"
},
{ {
"name": "DefaultIterableDiffer" "name": "DefaultIterableDiffer"
}, },
@ -164,6 +161,9 @@
{ {
"name": "RENDER_PARENT" "name": "RENDER_PARENT"
}, },
{
"name": "ROOT_EXPANDO_INSTRUCTIONS"
},
{ {
"name": "RecordViewTuple" "name": "RecordViewTuple"
}, },
@ -527,6 +527,9 @@
{ {
"name": "firstTemplatePass" "name": "firstTemplatePass"
}, },
{
"name": "generateExpandoBlock"
},
{ {
"name": "generateInitialInputs" "name": "generateInitialInputs"
}, },
@ -833,6 +836,9 @@
{ {
"name": "pointers" "name": "pointers"
}, },
{
"name": "prefillHostVars"
},
{ {
"name": "prepareInitialFlag" "name": "prepareInitialFlag"
}, },
@ -905,9 +911,6 @@
{ {
"name": "resolveDirective" "name": "resolveDirective"
}, },
{
"name": "sameHostView"
},
{ {
"name": "saveNameToExportMap" "name": "saveNameToExportMap"
}, },
@ -968,6 +971,9 @@
{ {
"name": "setValue" "name": "setValue"
}, },
{
"name": "shouldNotSearchParent"
},
{ {
"name": "storeCleanupFn" "name": "storeCleanupFn"
}, },

View File

@ -23,9 +23,6 @@
{ {
"name": "DECLARATION_VIEW" "name": "DECLARATION_VIEW"
}, },
{
"name": "DIRECTIVES"
},
{ {
"name": "EMPTY$1" "name": "EMPTY$1"
}, },
@ -95,6 +92,9 @@
{ {
"name": "RENDER_PARENT" "name": "RENDER_PARENT"
}, },
{
"name": "ROOT_EXPANDO_INSTRUCTIONS"
},
{ {
"name": "SANITIZER" "name": "SANITIZER"
}, },
@ -302,6 +302,9 @@
{ {
"name": "nextNgElementId" "name": "nextNgElementId"
}, },
{
"name": "prefillHostVars"
},
{ {
"name": "queueHostBindingForCheck" "name": "queueHostBindingForCheck"
}, },

View File

@ -29,9 +29,6 @@
{ {
"name": "DECLARATION_VIEW" "name": "DECLARATION_VIEW"
}, },
{
"name": "DIRECTIVES"
},
{ {
"name": "DefaultIterableDiffer" "name": "DefaultIterableDiffer"
}, },
@ -155,6 +152,9 @@
{ {
"name": "RENDER_PARENT" "name": "RENDER_PARENT"
}, },
{
"name": "ROOT_EXPANDO_INSTRUCTIONS"
},
{ {
"name": "RecordViewTuple" "name": "RecordViewTuple"
}, },
@ -578,6 +578,9 @@
{ {
"name": "firstTemplatePass" "name": "firstTemplatePass"
}, },
{
"name": "generateExpandoBlock"
},
{ {
"name": "generateInitialInputs" "name": "generateInitialInputs"
}, },
@ -854,6 +857,9 @@
{ {
"name": "pointers" "name": "pointers"
}, },
{
"name": "prefillHostVars"
},
{ {
"name": "prepareInitialFlag" "name": "prepareInitialFlag"
}, },
@ -932,9 +938,6 @@
{ {
"name": "restoreView" "name": "restoreView"
}, },
{
"name": "sameHostView"
},
{ {
"name": "saveNameToExportMap" "name": "saveNameToExportMap"
}, },
@ -992,6 +995,9 @@
{ {
"name": "setValue" "name": "setValue"
}, },
{
"name": "shouldNotSearchParent"
},
{ {
"name": "storeCleanupFn" "name": "storeCleanupFn"
}, },

View File

@ -191,9 +191,6 @@
{ {
"name": "DIGIT_CHAR" "name": "DIGIT_CHAR"
}, },
{
"name": "DIRECTIVES"
},
{ {
"name": "DOCUMENT" "name": "DOCUMENT"
}, },
@ -692,6 +689,9 @@
{ {
"name": "ROOT_CONTEXT" "name": "ROOT_CONTEXT"
}, },
{
"name": "ROOT_EXPANDO_INSTRUCTIONS"
},
{ {
"name": "RecordViewTuple" "name": "RecordViewTuple"
}, },
@ -1577,6 +1577,9 @@
{ {
"name": "fromPromise" "name": "fromPromise"
}, },
{
"name": "generateExpandoBlock"
},
{ {
"name": "generateInitialInputs" "name": "generateInitialInputs"
}, },
@ -2165,6 +2168,9 @@
{ {
"name": "pointers" "name": "pointers"
}, },
{
"name": "prefillHostVars"
},
{ {
"name": "prepareInitialFlag" "name": "prepareInitialFlag"
}, },
@ -2273,9 +2279,6 @@
{ {
"name": "rxSubscriber" "name": "rxSubscriber"
}, },
{
"name": "sameHostView"
},
{ {
"name": "sanitizeSrcset" "name": "sanitizeSrcset"
}, },
@ -2357,6 +2360,9 @@
{ {
"name": "shimHostAttribute" "name": "shimHostAttribute"
}, },
{
"name": "shouldNotSearchParent"
},
{ {
"name": "staticError" "name": "staticError"
}, },

View File

@ -105,7 +105,7 @@ describe('components & directives', () => {
hostVars: 1, hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
elIndex, 'id', $r3$.ɵbind($r3$.ɵloadDirective<HostBindingDir>(dirIndex).dirId)); elIndex, 'id', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).dirId));
} }
}); });
// /NORMATIVE // /NORMATIVE
@ -256,8 +256,7 @@ describe('components & directives', () => {
hostVars: 1, hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) { hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵelementAttribute( $r3$.ɵelementAttribute(
elIndex, 'aria-label', elIndex, 'aria-label', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).label));
$r3$.ɵbind($r3$.ɵloadDirective<HostBindingDir>(dirIndex).label));
} }
}); });
// /NORMATIVE // /NORMATIVE
@ -752,7 +751,7 @@ describe('components & directives', () => {
selectors: [['my-app']], selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); }, factory: function MyApp_Factory() { return new MyApp(); },
consts: 1, consts: 1,
vars: 10, vars: 11,
template: function MyApp_Template(rf: $RenderFlags$, c: $any$) { template: function MyApp_Template(rf: $RenderFlags$, c: $any$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'my-comp'); $r3$.ɵelement(0, 'my-comp');

View File

@ -121,7 +121,7 @@ describe('queries', () => {
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh( contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(
dirIndex: $number$, queryStartIndex: $number$) { dirIndex: $number$, queryStartIndex: $number$) {
let $tmp$: any; let $tmp$: any;
const $instance$ = $r3$.ɵloadDirective<ContentQueryComponent>(dirIndex); const $instance$ = $r3$.ɵload<ContentQueryComponent>(dirIndex);
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex)) && $r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex)) &&
($instance$.someDir = $tmp$.first); ($instance$.someDir = $tmp$.first);
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex + 1)) && $r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex + 1)) &&

View File

@ -10,10 +10,10 @@ import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Injector, O
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition'; import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateInjectable, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di'; import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
import {PublicFeature, defineDirective, directiveInject, elementProperty, injectRenderer2, load, templateRefExtractor} from '../../src/render3/index'; import {PublicFeature, defineDirective, directiveInject, elementProperty, injectRenderer2, load, templateRefExtractor} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd, _getViewData, getTNode} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node'; import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
@ -677,8 +677,7 @@ describe('di', () => {
factory: () => hostBindingDir = new HostBindingDir(), factory: () => hostBindingDir = new HostBindingDir(),
hostVars: 1, hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => { hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty( elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
} }
}); });
} }
@ -1298,7 +1297,8 @@ describe('di', () => {
projectionDef(); projectionDef();
projection(0); projection(0);
} }
} },
features: [PublicFeature]
}); });
} }
@ -1323,7 +1323,8 @@ describe('di', () => {
type: DirectiveSameInstance, type: DirectiveSameInstance,
selectors: [['', 'dirSame', '']], selectors: [['', 'dirSame', '']],
factory: () => dirSameInstance = factory: () => dirSameInstance =
new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any)) new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any)),
features: [PublicFeature]
}); });
} }
@ -1419,7 +1420,8 @@ describe('di', () => {
textBinding(3, bind(tmp.value)); textBinding(3, bind(tmp.value));
} }
}, },
directives: directives directives: directives,
features: [PublicFeature]
}); });
} }
@ -1563,6 +1565,7 @@ describe('di', () => {
}); });
describe('@Attribute', () => { describe('@Attribute', () => {
let myDirectiveInstance !: MyDirective | null;
class MyDirective { class MyDirective {
exists = 'wrong' as string | undefined; exists = 'wrong' as string | undefined;
@ -1577,10 +1580,13 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: MyDirective, type: MyDirective,
selectors: [['', 'myDirective', '']], selectors: [['', 'myDirective', '']],
factory: () => new MyDirective(injectAttribute('exist'), injectAttribute('myDirective')) factory: () => myDirectiveInstance =
new MyDirective(injectAttribute('exist'), injectAttribute('myDirective'))
}); });
} }
beforeEach(() => myDirectiveInstance = null);
it('should inject attribute', () => { it('should inject attribute', () => {
let exist = 'wrong' as string | undefined; let exist = 'wrong' as string | undefined;
let nonExist = 'wrong' as string | undefined; let nonExist = 'wrong' as string | undefined;
@ -1843,7 +1849,7 @@ describe('di', () => {
(parentTNode as{parent: any}).parent = undefined; (parentTNode as{parent: any}).parent = undefined;
const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904) const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904)
expect(injector).not.toBe(null); expect(injector).not.toEqual(-1);
} finally { } finally {
leaveView(oldView); leaveView(oldView);
} }

View File

@ -11,10 +11,10 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RendererStyleFlags2, RendererType2} from '../../src/render/api'; import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index'; import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions';
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition'; import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, CONTEXT, DIRECTIVES} from '../../src/render3/interfaces/view'; import {HEADER_OFFSET, CONTEXT} from '../../src/render3/interfaces/view';
import {sanitizeUrl} from '../../src/sanitization/sanitization'; import {sanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
@ -449,8 +449,7 @@ describe('render3 integration test', () => {
hostBindings: function(directiveIndex: number, elementIndex: number): void { hostBindings: function(directiveIndex: number, elementIndex: number): void {
// host bindings // host bindings
elementProperty( elementProperty(
elementIndex, 'title', elementIndex, 'title', bind(load<TodoComponentHostBinding>(directiveIndex).title));
bind(loadDirective<TodoComponentHostBinding>(directiveIndex).title));
} }
}); });
} }
@ -750,7 +749,7 @@ describe('render3 integration test', () => {
type: TestDirective, type: TestDirective,
selectors: [['', 'testDirective', '']], selectors: [['', 'testDirective', '']],
factory: factory:
() => new TestDirective( () => testDirective = new TestDirective(
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)), directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
}); });
} }
@ -777,9 +776,6 @@ describe('render3 integration test', () => {
template( template(
0, embeddedTemplate, 2, 0, null, [AttributeMarker.SelectOnly, 'testDirective']); 0, embeddedTemplate, 2, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
} }
if (rf & RenderFlags.Update) {
testDirective = loadDirective<TestDirective>(0);
}
}, 1, 0, [TestDirective]); }, 1, 0, [TestDirective]);
const fixture = new ComponentFixture(TestCmpt); const fixture = new ComponentFixture(TestCmpt);
@ -845,6 +841,7 @@ describe('render3 integration test', () => {
}); });
it('should render inside another ng-container at the root of a delayed view', () => { it('should render inside another ng-container at the root of a delayed view', () => {
let testDirective: TestDirective;
class TestDirective { class TestDirective {
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {} constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
@ -857,12 +854,11 @@ describe('render3 integration test', () => {
type: TestDirective, type: TestDirective,
selectors: [['', 'testDirective', '']], selectors: [['', 'testDirective', '']],
factory: factory:
() => new TestDirective( () => testDirective = new TestDirective(
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)), directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
}); });
} }
let testDirective: TestDirective;
function embeddedTemplate(rf: RenderFlags, ctx: any) { function embeddedTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
@ -895,9 +891,6 @@ describe('render3 integration test', () => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
template(0, embeddedTemplate, 4, 0, null, [AttributeMarker.SelectOnly, 'testDirective']); template(0, embeddedTemplate, 4, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
} }
if (rf & RenderFlags.Update) {
testDirective = loadDirective<TestDirective>(0);
}
}, 1, 0, [TestDirective]); }, 1, 0, [TestDirective]);
function App() { element(0, 'test-cmpt'); } function App() { element(0, 'test-cmpt'); }
@ -926,7 +919,7 @@ describe('render3 integration test', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: Directive, type: Directive,
selectors: [['', 'dir', '']], selectors: [['', 'dir', '']],
factory: () => new Directive(directiveInject(ElementRef)), factory: () => directive = new Directive(directiveInject(ElementRef)),
}); });
} }
@ -940,7 +933,6 @@ describe('render3 integration test', () => {
{ {
elementContainerStart(1, [AttributeMarker.SelectOnly, 'dir']); elementContainerStart(1, [AttributeMarker.SelectOnly, 'dir']);
elementContainerEnd(); elementContainerEnd();
directive = loadDirective<Directive>(0);
} }
elementEnd(); elementEnd();
} }
@ -1277,8 +1269,7 @@ describe('render3 integration test', () => {
}, },
hostVars: 1, hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) { hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) {
elementAttribute( elementAttribute(elIndex, 'aria-label', bind(load<HostBindingDir>(dirIndex).label));
elIndex, 'aria-label', bind(loadDirective<HostBindingDir>(dirIndex).label));
} }
}); });
} }
@ -2112,11 +2103,10 @@ describe('render3 integration test', () => {
const context = getContext(hostElm) !; const context = getContext(hostElm) !;
const elementNode = context.lViewData[context.nodeIndex]; const elementNode = context.lViewData[context.nodeIndex];
const elmData = elementNode.data !; const elmData = elementNode.data !;
const dirs = elmData[DIRECTIVES];
expect(dirs).toContain(myDir1Instance); expect(elmData).toContain(myDir1Instance);
expect(dirs).toContain(myDir2Instance); expect(elmData).toContain(myDir2Instance);
expect(dirs).toContain(myDir3Instance); expect(elmData).toContain(myDir3Instance);
expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); expect(Array.isArray((myDir1Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();
expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy(); expect(Array.isArray((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME])).toBeTruthy();

View File

@ -8,12 +8,13 @@
import {EventEmitter} from '@angular/core'; import {EventEmitter} from '@angular/core';
import {AttributeMarker, PublicFeature, defineComponent, defineDirective} from '../../src/render3/index'; import {AttributeMarker, PublicFeature, defineComponent, template, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, loadDirective, reference, text, textBinding} from '../../src/render3/instructions'; import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, load, reference, text, textBinding} from '../../src/render3/instructions';
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';
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; import {ComponentFixture, TemplateFixture, createComponent, renderToHtml, createDirective} from './render_util';
import {NgForOf} from './common_with_def';
describe('elementProperty', () => { describe('elementProperty', () => {
@ -109,8 +110,7 @@ describe('elementProperty', () => {
factory: () => directiveInstance = new Directive, factory: () => directiveInstance = new Directive,
hostVars: 1, hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => { hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty( elementProperty(elementIndex, 'className', bind(load<Directive>(directiveIndex).klass));
elementIndex, 'className', bind(loadDirective<Directive>(directiveIndex).klass));
} }
}); });
} }
@ -138,7 +138,7 @@ describe('elementProperty', () => {
vars: 0, vars: 0,
hostVars: 1, hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
const instance = loadDirective(dirIndex) as HostBindingComp; const instance = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'id', bind(instance.id)); elementProperty(elIndex, 'id', bind(instance.id));
}, },
template: (rf: RenderFlags, ctx: HostBindingComp) => {} template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -153,6 +153,71 @@ describe('elementProperty', () => {
expect(fixture.hostElement.id).toBe('other-id'); expect(fixture.hostElement.id).toBe('other-id');
}); });
it('should support host bindings on multiple nodes', () => {
let hostBindingDir !: HostBindingDir;
class HostBindingDir {
// @HostBinding()
id = 'foo';
static ngDirectiveDef = defineDirective({
type: HostBindingDir,
selectors: [['', 'hostBindingDir', '']],
factory: () => hostBindingDir = new HostBindingDir(),
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
});
}
const SomeDir = createDirective('someDir');
class HostBindingComp {
// @HostBinding()
title = 'my-title';
static ngComponentDef = defineComponent({
type: HostBindingComp,
selectors: [['host-binding-comp']],
factory: () => new HostBindingComp(),
consts: 0,
vars: 0,
hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => {
const ctx = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'title', bind(ctx.title));
},
template: (rf: RenderFlags, ctx: HostBindingComp) => {},
features: [PublicFeature]
});
}
/**
* <div hostBindingDir></div>
* <div someDir></div>
* <host-binding-comp></host-binding-comp>
*/
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
element(0, 'div', ['hostBindingDir', '']);
element(1, 'div', ['someDir', '']);
element(2, 'host-binding-comp');
}
}, 3, 0, [HostBindingDir, SomeDir, HostBindingComp]);
const fixture = new ComponentFixture(App);
const hostBindingDiv = fixture.hostElement.querySelector('div') as HTMLElement;
const hostBindingComp = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
expect(hostBindingDiv.id).toEqual('foo');
expect(hostBindingComp.title).toEqual('my-title');
hostBindingDir.id = 'bar';
fixture.update();
expect(hostBindingDiv.id).toEqual('bar');
});
it('should support host bindings on second template pass', () => { it('should support host bindings on second template pass', () => {
class HostBindingDir { class HostBindingDir {
// @HostBinding() // @HostBinding()
@ -164,8 +229,7 @@ describe('elementProperty', () => {
factory: () => new HostBindingDir(), factory: () => new HostBindingDir(),
hostVars: 1, hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => { hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty( elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
}, },
features: [PublicFeature] features: [PublicFeature]
}); });
@ -195,6 +259,55 @@ describe('elementProperty', () => {
expect(divs[1].id).toEqual('foo'); expect(divs[1].id).toEqual('foo');
}); });
it('should support host bindings in for loop', () => {
class HostBindingDir {
// @HostBinding()
id = 'foo';
static ngDirectiveDef = defineDirective({
type: HostBindingDir,
selectors: [['', 'hostBindingDir', '']],
factory: () => new HostBindingDir(),
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
});
}
function NgForTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div');
{ element(1, 'p', ['hostBindingDir', '']); }
elementEnd();
}
}
/**
* <div *ngFor="let row of rows">
* <p hostBindingDir></p>
* </div>
*/
const App = createComponent('parent', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
template(0, NgForTemplate, 2, 0, null, ['ngForOf', '']);
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'ngForOf', bind(ctx.rows));
}
}, 1, 1, [HostBindingDir, NgForOf]);
const fixture = new ComponentFixture(App);
fixture.component.rows = [1, 2, 3];
fixture.update();
const paragraphs = fixture.hostElement.querySelectorAll('p');
expect(paragraphs[0].id).toEqual('foo');
expect(paragraphs[1].id).toEqual('foo');
expect(paragraphs[2].id).toEqual('foo');
});
it('should support component with host bindings and array literals', () => { it('should support component with host bindings and array literals', () => {
const ff = (v: any) => ['Nancy', v, 'Ned']; const ff = (v: any) => ['Nancy', v, 'Ned'];
@ -210,7 +323,7 @@ describe('elementProperty', () => {
vars: 0, vars: 0,
hostVars: 1, hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
const ctx = loadDirective(dirIndex) as HostBindingComp; const ctx = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'id', bind(ctx.id)); elementProperty(elIndex, 'id', bind(ctx.id));
}, },
template: (rf: RenderFlags, ctx: HostBindingComp) => {} template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -283,7 +396,7 @@ describe('elementProperty', () => {
vars: 0, vars: 0,
hostVars: 8, hostVars: 8,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
const ctx = loadDirective(dirIndex) as HostBindingComp; const ctx = load(dirIndex) as HostBindingComp;
// LViewData: [..., id, dir, title, ctx.id, pf1, ctx.title, ctx.otherTitle, pf2] // LViewData: [..., id, dir, title, ctx.id, pf1, ctx.title, ctx.otherTitle, pf2]
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id))); elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
elementProperty(elIndex, 'dir', bind(ctx.dir)); elementProperty(elIndex, 'dir', bind(ctx.dir));
@ -359,7 +472,7 @@ describe('elementProperty', () => {
hostVars: 3, hostVars: 3,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
// LViewData: [..., id, ctx.id, pf1] // LViewData: [..., id, ctx.id, pf1]
const ctx = loadDirective(dirIndex) as HostBindingComp; const ctx = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id))); elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
}, },
template: (rf: RenderFlags, ctx: HostBindingComp) => {} template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -387,7 +500,7 @@ describe('elementProperty', () => {
hostVars: 3, hostVars: 3,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
// LViewData [..., title, ctx.title, pf1] // LViewData [..., title, ctx.title, pf1]
const ctx = loadDirective(dirIndex) as HostBindingDir; const ctx = load(dirIndex) as HostBindingDir;
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title))); elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
} }
}); });
@ -448,7 +561,7 @@ describe('elementProperty', () => {
hostVars: 6, hostVars: 6,
hostBindings: (dirIndex: number, elIndex: number) => { hostBindings: (dirIndex: number, elIndex: number) => {
// LViewData: [..., id, title, ctx.id, pf1, ctx.title, pf1] // LViewData: [..., id, title, ctx.id, pf1, ctx.title, pf1]
const ctx = loadDirective(dirIndex) as HostBindingComp; const ctx = load(dirIndex) as HostBindingComp;
elementProperty( elementProperty(
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green')); elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
elementProperty( elementProperty(

View File

@ -14,7 +14,7 @@ import {directiveInject} from '../../src/render3/di';
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index'; import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadElement, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadElement, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {query, queryRefresh} from '../../src/render3/query'; import {query, queryRefresh} from '../../src/render3/query';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound'; import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
@ -1914,7 +1914,7 @@ describe('query', () => {
contentQueries: () => { registerContentQuery(query(null, ['foo'], true)); }, contentQueries: () => { registerContentQuery(query(null, ['foo'], true)); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
withContentInstance = loadDirective<WithContentDirective>(dirIndex); withContentInstance = load<WithContentDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(withContentInstance.foos = tmp); (withContentInstance.foos = tmp);
} }
@ -1935,7 +1935,7 @@ describe('query', () => {
contentQueries: () => { registerContentQuery(query(null, ['foo'], false)); }, contentQueries: () => { registerContentQuery(query(null, ['foo'], false)); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
shallowCompInstance = loadDirective<ShallowComp>(dirIndex); shallowCompInstance = load<ShallowComp>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(shallowCompInstance.foos = tmp); (shallowCompInstance.foos = tmp);
} }
@ -2116,7 +2116,7 @@ describe('query', () => {
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
const instance = loadDirective<QueryDirective>(dirIndex); const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.fooBars = tmp); (instance.fooBars = tmp);
}, },
@ -2180,7 +2180,7 @@ describe('query', () => {
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
const instance = loadDirective<QueryDirective>(dirIndex); const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.fooBars = tmp); (instance.fooBars = tmp);
}, },
@ -2233,7 +2233,7 @@ describe('query', () => {
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
const instance = loadDirective<ShallowQueryDirective>(dirIndex); const instance = load<ShallowQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.foos = tmp); (instance.foos = tmp);
}, },
@ -2253,7 +2253,7 @@ describe('query', () => {
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any; let tmp: any;
const instance = loadDirective<DeepQueryDirective>(dirIndex); const instance = load<DeepQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.foos = tmp); (instance.foos = tmp);
}, },

View File

@ -9,9 +9,9 @@
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core'; import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
import {ViewEncapsulation} from '../../src/metadata'; import {ViewEncapsulation} from '../../src/metadata';
import {directiveInject} from '../../src/render3/di'; import {directiveInject} from '../../src/render3/di';
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver} from '../../src/render3/index'; import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, 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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound'; import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
import {NgModuleFactory} from '../../src/render3/ng_module_ref'; import {NgModuleFactory} from '../../src/render3/ng_module_ref';
@ -1728,7 +1728,7 @@ describe('ViewContainerRef', () => {
hostVars: 1, hostVars: 1,
attributes: ['id', 'attribute'], attributes: ['id', 'attribute'],
hostBindings: function(dirIndex, elIndex) { hostBindings: function(dirIndex, elIndex) {
const cmptInstance = loadDirective<HostBindingCmpt>(dirIndex); const cmptInstance = load<HostBindingCmpt>(dirIndex);
elementProperty(elIndex, 'title', bind(cmptInstance.title)); elementProperty(elIndex, 'title', bind(cmptInstance.title));
}, },
}); });