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", ""]],
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
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,
features: [$r3$.ɵPublicFeature]
@ -632,7 +632,7 @@ describe('compiler compliance', () => {
selectors: [["host-binding-comp"]],
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
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,
features: [$r3$.ɵPublicFeature],
@ -1343,7 +1343,7 @@ describe('compiler compliance', () => {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex);
const instance = $r3$.ɵload(dirIndex);
var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
($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));
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex);
const instance = $r3$.ɵload(dirIndex);
var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
($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));
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵloadDirective(dirIndex);
const instance = $r3$.ɵload(dirIndex);
var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));

View File

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

View File

@ -96,7 +96,6 @@ export class Identifiers {
static pipeBindV: o.ExternalReference = {name: 'ɵpipeBindV', 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 pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE};

View File

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

View File

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

View File

@ -128,8 +128,6 @@ NOTE:
## `EXPANDO`
*TODO*: This section is to be implemented.
`EXPANDO` contains information on data which size is not known at compile time.
Examples include:
- `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
| ----: | ---------------------------: | -------
| 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.
| 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`)
@ -215,9 +213,9 @@ The reason for this layout is to make the host binding update efficient using th
let currentDirectiveIndex = -1;
let currentElementIndex = -1;
// This is global state which is used internally by hostBindings to know where the offset is
let bindingRootIndex = tView.expandoStart;
for(var i = 0; i < tview.expandoInstructions.length; i++) {
let instruction = tview.expandoInstructions[i];
let bindingRootIndex = tView.expandoStartIndex;
for(var i = 0; i < tView.expandoInstructions.length; i++) {
let instruction = tView.expandoInstructions[i];
if (typeof instruction === 'number') {
// Numbers are used to update the indices.
if (instruction < 0) {

View File

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

View File

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

View File

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

View File

@ -20,10 +20,10 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
import {NG_ELEMENT_ID} from './fields';
import {_getViewData, assertPreviousIsParent, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions';
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 {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';
/**
@ -104,12 +104,10 @@ export function getOrCreateNodeInjectorForNode(
const tView = hostView[TVIEW];
if (tView.firstTemplatePass) {
// TODO(kara): Store node injector with host bindings for that node (see VIEW_DATA.md)
tNode.injectorIndex = hostView.length;
setUpBloom(tView.data, tNode); // foundation for node bloom
setUpBloom(hostView, null); // foundation for cumulative bloom
setUpBloom(tView.blueprint, null);
tView.hostBindingStartIndex += INJECTOR_SIZE;
}
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.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 {
@ -320,8 +320,8 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
* @returns the value from the injector or `null` when not found
*/
export function getOrCreateInjectable<T>(
startInjectorIndex: number, hostView: LViewData, token: Type<T>| InjectionToken<T>,
flags: InjectFlags = InjectFlags.Default): T|null {
hostTNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData,
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default): T|null {
const bloomHash = bloomHashBitOrFactory(token);
// 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.
@ -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
// (diPublic) otherwise fall back to the module injector.
if (bloomHash != null) {
const startInjectorIndex = getInjectorIndex(hostTNode, hostView);
let injectorIndex = startInjectorIndex;
let injectorView = hostView;
let parentLocation: number = -1;
if (flags & InjectFlags.SkipSelf) {
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
// If we should skip this injector or if an injector doesn't exist on this node (e.g. all
// directives on this node are private), start by searching the parent injector.
if (flags & InjectFlags.SkipSelf || injectorIndex === -1) {
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) {
@ -348,9 +359,8 @@ export function getOrCreateInjectable<T>(
break;
}
if (flags & InjectFlags.Self ||
flags & InjectFlags.Host &&
!sameHostView(injectorView[injectorIndex + PARENT_INJECTOR])) {
parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
if (shouldNotSearchParent(flags, parentLocation)) {
injectorIndex = -1;
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
// can abort.
if (injectorHasToken(bloomHash, injectorIndex, injectorView)) {
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
} else {
@ -390,7 +399,6 @@ export function getOrCreateInjectable<T>(
// The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching.
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
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) {
const def = matches[i] as DirectiveDef<any>;
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) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const defs = injectorView[TVIEW].directives !;
const defs = injectorView[TVIEW].data;
for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDef<any>;
if (directiveDef.type === token && directiveDef.diPublic) {
return injectorView[DIRECTIVES] ![i];
return injectorView[i];
}
}
}
@ -486,15 +494,10 @@ export function injectorHasToken(
return !!(value & mask);
}
/**
* Checks whether the current injector and its parent are in the same host view.
*
* 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;
/** Returns true if flags prevent parent injector from being searched for tokens */
function shouldNotSearchParent(flags: InjectFlags, parentLocation: number): boolean|number {
return flags & InjectFlags.Self ||
(flags & InjectFlags.Host && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) > 0);
}
export class NodeInjector implements Injector {
@ -512,7 +515,7 @@ export class NodeInjector implements Injector {
}
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 {

View File

@ -9,7 +9,7 @@
import {assertEqual} from './assert';
import {DirectiveDef} from './interfaces/definition';
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
* 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 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
// hooks for projected components and directives must be called *before* their hosts.
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);
queueViewHooks(def, tView, i);
queueDestroyHooks(def, tView, i);
@ -99,7 +99,7 @@ function queueDestroyHooks(def: DirectiveDef<any>, tView: TView, i: number): voi
export function executeInitHooks(
currentView: LViewData, tView: TView, creationMode: boolean): void {
if (currentView[FLAGS] & LViewFlags.RunInit) {
executeHooks(currentView[DIRECTIVES] !, tView.initHooks, tView.checkHooks, creationMode);
executeHooks(currentView, tView.initHooks, tView.checkHooks, creationMode);
currentView[FLAGS] &= ~LViewFlags.RunInit;
}
}
@ -110,7 +110,7 @@ export function executeInitHooks(
* @param currentView The current view
*/
export function executeHooks(
data: any[], allHooks: HookData | null, checkHooks: HookData | null,
data: LViewData, allHooks: HookData | null, checkHooks: HookData | null,
creationMode: boolean): void {
const hooksToCall = creationMode ? allHooks : checkHooks;
if (hooksToCall) {
@ -125,8 +125,8 @@ export function executeHooks(
* @param currentView The current view
* @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) {
(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,
store,
load,
loadDirective,
namespaceHTML,
namespaceMathML,

View File

@ -18,12 +18,13 @@ import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComp
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
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 {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
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 {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
@ -42,15 +43,6 @@ const _CLEAN_PROMISE = Promise.resolve(null);
*/
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.
*
@ -230,14 +222,6 @@ export function _getViewData(): LViewData {
*/
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[] {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return view[CLEANUP] || (view[CLEANUP] = []);
@ -259,7 +243,7 @@ let firstTemplatePass = true;
/**
* The root index from which pure function instructions should calculate their 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;
@ -268,6 +252,9 @@ export function getBindingRoot() {
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 {
Input,
Output,
@ -288,7 +275,6 @@ const enum BindingDirection {
export function enterView(
newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData {
const oldView: LViewData = viewData;
directives = newView && newView[DIRECTIVES];
tView = newView && newView[TVIEW];
creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
@ -317,7 +303,7 @@ export function enterView(
export function leaveView(newView: LViewData, creationOnly?: boolean): void {
if (!creationOnly) {
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
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.
*/
function refreshDescendantViews() {
setHostBindings(tView.hostBindings);
setHostBindings();
const parentFirstTemplatePass = firstTemplatePass;
// This needs to be set before children are processed to support recursive components
@ -349,7 +335,7 @@ function refreshDescendantViews() {
refreshContentQueries(tView);
if (!checkNoChangesMode) {
executeHooks(directives !, tView.contentHooks, tView.contentCheckHooks, creationMode);
executeHooks(viewData, tView.contentHooks, tView.contentCheckHooks, creationMode);
}
refreshChildComponents(tView.components, parentFirstTemplatePass);
@ -357,21 +343,38 @@ function refreshDescendantViews() {
/** Sets the host bindings for the current view. */
export function setHostBindings(bindings: number[] | null): void {
if (bindings != null) {
bindingRootIndex = viewData[BINDING_INDEX] = tView.hostBindingStartIndex;
const defs = tView.directives !;
for (let i = 0; i < bindings.length; i += 2) {
const dirIndex = bindings[i];
const def = defs[dirIndex] as DirectiveDef<any>;
if (firstTemplatePass) {
for (let i = 0; i < def.hostVars; i++) {
tView.blueprint.push(NO_CHANGE);
viewData.push(NO_CHANGE);
export function setHostBindings(): void {
if (tView.expandoInstructions) {
bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
let currentDirectiveIndex = -1;
let currentElementIndex = -1;
for (let i = 0; i < tView.expandoInstructions.length; i++) {
const instruction = tView.expandoInstructions[i];
if (typeof instruction === 'number') {
if (instruction <= 0) {
// Negative numbers mean that we are starting new EXPANDO block and need to update
// 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) {
for (let i = 0; i < tView.contentQueries.length; i += 2) {
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 {
if (!checkNoChangesMode) {
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) {
const tView = view[TVIEW];
if (tView.firstTemplatePass) {
tView.hostBindingStartIndex++;
tView.expandoStartIndex++;
tView.blueprint.push(null);
view.push(null);
}
@ -658,10 +662,14 @@ export function renderEmbeddedTemplate<T>(
if (rf & RenderFlags.Update) {
refreshDescendantViews();
} 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;
}
} 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.
const isCreationOnly = (rf & RenderFlags.Create) === RenderFlags.Create;
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
// in renderComponent()
setHostBindings(tView.hostBindings);
setHostBindings();
componentRefresh(HEADER_OFFSET, false);
}
} finally {
@ -905,15 +913,45 @@ function cacheMatchingDirectivesForNode(
// 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 matches = tView.currentMatches = findDirectiveMatches(tNode);
generateExpandoBlock(tNode, matches);
let totalHostVars = 0;
if (matches) {
for (let i = 0; i < matches.length; i += 2) {
const def = matches[i] as DirectiveDef<any>;
const valueIndex = i + 1;
resolveDirective(def, valueIndex, matches, tView);
resolveDirective(def, valueIndex, matches);
totalHostVars += def.hostVars;
saveNameToExportMap(matches[valueIndex] as number, def, 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. */
@ -925,17 +963,16 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
const def = registry[i];
if (isNodeMatchingSelectorList(tNode, def.selectors !)) {
matches || (matches = []);
if (def.diPublic) def.diPublic(def);
if ((def as ComponentDef<any>).template) {
if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode);
addComponentLogic(def as ComponentDef<any>);
tNode.flags = TNodeFlags.isComponent;
// The component is always stored first with directives after.
matches.unshift(def, null);
} else {
matches.push(def, null);
}
if (def.diPublic) def.diPublic(def);
}
}
}
@ -943,12 +980,11 @@ function findDirectiveMatches(tNode: TNode): CurrentMatchesList|null {
}
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) {
matches[valueIndex] = CIRCULAR;
const instance = def.factory();
(tView.directives || (tView.directives = [])).push(def);
return directiveCreate(matches[valueIndex] = tView.directives !.length - 1, instance, def);
return directiveCreate(matches[valueIndex] = viewData.length, instance, def);
} else if (matches[valueIndex] === CIRCULAR) {
// If we revisit this directive before it's resolved, we know it's circular
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.
*/
export function queueHostBindingForCheck(dirIndex: number, hostVars: number): void {
// Must subtract the header offset because hostBindings functions are generated with
// instructions that expect element indices that are NOT adjusted (e.g. elementProperty).
export function queueHostBindingForCheck(
dirIndex: number, def: DirectiveDef<any>| ComponentDef<any>): void {
ngDevMode &&
assertEqual(firstTemplatePass, true, 'Should only be called in first template pass.');
(tView.hostBindings || (tView.hostBindings = [
])).push(dirIndex, previousOrParentTNode.index - HEADER_OFFSET);
tView.expandoInstructions !.push(def.hostBindings !, def.hostVars);
}
/**
@ -990,10 +1024,9 @@ function instantiateDirectivesDirectly() {
if (count > 0) {
const start = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const tDirectives = tView.directives !;
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
// ChangeDetectorRefs have a way to store component view on creation.
@ -1046,7 +1079,7 @@ function saveResolvedLocalsInData(localRefExtractor: LocalRefExtractor): void {
let localIndex = previousOrParentTNode.index + 1;
for (let i = 0; i < localNames.length; i += 2) {
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;
}
}
@ -1108,8 +1141,9 @@ export function createTView(
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LViewData
childIndex: -1, // Children set in addToViewTree(), if any
bindingStartIndex: bindingStartIndex,
hostBindingStartIndex: initialViewLength,
expandoStartIndex: initialViewLength,
directives: null,
expandoInstructions: null,
firstTemplatePass: true,
initHooks: null,
checkHooks: null,
@ -1120,7 +1154,6 @@ export function createTView(
destroyHooks: null,
pipeDestroyHooks: null,
cleanup: null,
hostBindings: null,
contentQueries: null,
components: null,
directiveRegistry: typeof directives === 'function' ? directives() : directives,
@ -1224,9 +1257,10 @@ export function hostElement(
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, sanitizer));
if (firstTemplatePass) {
tNode.flags = TNodeFlags.isComponent;
tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
if (def.diPublic) def.diPublic(def);
tView.directives = [def];
tNode.flags =
viewData.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
}
return viewData[HEADER_OFFSET];
}
@ -1289,8 +1323,8 @@ export function listener(
*/
function createOutput(outputs: PropertyAliasValue, listener: Function): void {
for (let i = 0; i < outputs.length; i += 2) {
ngDevMode && assertDataInRange(outputs[i] as number, directives !);
const subscription = directives ![outputs[i] as number][outputs[i + 1]].subscribe(listener);
ngDevMode && assertDataInRange(outputs[i] as number, viewData);
const subscription = viewData[outputs[i] as number][outputs[i + 1]].subscribe(listener);
storeCleanupWithContext(viewData, subscription, subscription.unsubscribe);
}
}
@ -1498,8 +1532,8 @@ export function createTNode(
*/
function setInputsForProperty(inputs: PropertyAliasValue, value: any): void {
for (let i = 0; i < inputs.length; i += 2) {
ngDevMode && assertDataInRange(inputs[i] as number, directives !);
directives ![inputs[i] as number][inputs[i + 1]] = value;
ngDevMode && assertDataInRange(inputs[i] as number, viewData);
viewData[inputs[i] as number][inputs[i + 1]] = value;
}
}
@ -1519,7 +1553,7 @@ function generatePropertyAliases(
const start = tNodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const isInput = direction === BindingDirection.Input;
const defs = tView.directives !;
const defs = tView.data;
for (let i = start; i < end; i++) {
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
// any projected components.
queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView);
if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx, directiveDef.hostVars);
}
ngDevMode && assertDefined(previousOrParentTNode, 'previousOrParentTNode');
@ -1810,7 +1842,11 @@ function addComponentLogic<T>(def: ComponentDef<T>): void {
(hostNode as{data: LViewData}).data = componentView;
(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);
}
if (directives == null) viewData[DIRECTIVES] = directives = [];
ngDevMode && assertDataNext(index, directives);
directives[index] = directive;
viewData[index] = directive;
if (firstTemplatePass) {
const flags = previousOrParentTNode.flags;
if ((flags & TNodeFlags.DirectiveCountMask) === 0) {
if (flags === 0) {
// When the first directive is created:
// - save the index,
// - set the number of directives to 1
@ -1852,6 +1885,10 @@ export function baseDirectiveCreate<T>(
'Reached the max number of directives');
previousOrParentTNode.flags++;
}
tView.data.push(directiveDef);
tView.blueprint.push(null);
if (directiveDef.hostBindings) queueHostBindingForCheck(index, directiveDef);
} else {
const diPublic = directiveDef !.diPublic;
if (diPublic) diPublic(directiveDef !);
@ -2828,13 +2865,6 @@ function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
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> {
ngDevMode && assertDefined(
viewData[CONTENT_QUERIES],
@ -2918,7 +2948,7 @@ export function registerContentQuery<Q>(queryList: QueryList<Q>): void {
const savedContentQueriesLength =
(viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList);
if (firstTemplatePass) {
const currentDirectiveIndex = directives !.length - 1;
const currentDirectiveIndex = viewData.length - 1;
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
const lastSavedDirectiveIndex =
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;
/** Refreshes host bindings on the associated directive. */
hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null;
hostBindings: HostBindingsFunction|null;
/**
* Static attributes to set on host element.
@ -330,6 +330,8 @@ export type DirectiveTypeList =
(DirectiveDef<any>| ComponentDef<any>|
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.
*

View File

@ -12,13 +12,13 @@ import {Sanitizer} from '../../sanitization/security';
import {PlayerHandler} from '../interfaces/player';
import {LContainer} from './container';
import {ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDef, PipeDefList} from './definition';
import {LElementNode, LViewNode, TElementNode, TNode, TViewNode} from './node';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
import {TElementNode, TNode, TViewNode} from './node';
import {LQueries} from './query';
import {Renderer3} from './renderer';
/** 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
// without having to remember the specific indices.
@ -30,16 +30,15 @@ export const QUERIES = 3;
export const FLAGS = 4;
export const HOST_NODE = 5;
export const BINDING_INDEX = 6;
export const DIRECTIVES = 7;
export const CLEANUP = 8;
export const CONTEXT = 9;
export const INJECTOR = 10;
export const RENDERER = 11;
export const SANITIZER = 12;
export const TAIL = 13;
export const CONTAINER_INDEX = 14;
export const CONTENT_QUERIES = 15;
export const DECLARATION_VIEW = 16;
export const CLEANUP = 7;
export const CONTEXT = 8;
export const INJECTOR = 9;
export const RENDERER = 10;
export const SANITIZER = 11;
export const TAIL = 12;
export const CONTAINER_INDEX = 13;
export const CONTENT_QUERIES = 14;
export const DECLARATION_VIEW = 15;
// 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
@ -115,15 +114,6 @@ export interface LViewData extends Array<any> {
*/
[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
* unsubscribed. This context array stores both listener functions wrapped with
@ -307,11 +297,16 @@ export interface TView {
bindingStartIndex: number;
/**
* The index at which the data array begins to store host bindings for components
* or directives in its template. Saving this value ensures that we can set the
* binding root and binding index correctly before checking host bindings.
* The index where the "expando" section of `LViewData` begins. The expando
* section contains injectors, directive instances, and host binding values.
* 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
@ -357,6 +352,13 @@ export interface TView {
*/
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.
*
@ -479,18 +481,6 @@ export interface TView {
*/
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.
*
@ -558,7 +548,7 @@ export type HookData = (number | (() => void))[];
*
* 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 */
export type CurrentMatchesList = [DirectiveDef<any>, (string | number | null)];

View File

@ -41,7 +41,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵnextContext': r3.nextContext,
'ɵcontainerRefreshStart': r3.containerRefreshStart,
'ɵcontainerRefreshEnd': r3.containerRefreshEnd,
'ɵloadDirective': r3.loadDirective,
'ɵloadQueryList': r3.loadQueryList,
'ɵnamespaceHTML': r3.namespaceHTML,
'ɵ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 {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
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 {getLNode, stringify} from './util';
@ -499,7 +499,7 @@ function executeOnDestroys(view: LViewData): void {
const tView = view[TVIEW];
let destroyHooks: HookData|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.
*
* |--------consts--------|----------------vars----------------|------ hostVars (dir1) ------|
* ---------------------------------------------------------------------------------------------
* | nodes / refs / pipes | bindings | pure function bindings | host bindings | host slots |
* ---------------------------------------------------------------------------------------------
* ^ ^
* TView.bindingStartIndex TView.hostBindingStartIndex
* |------consts------|---------vars---------| |----- hostVars (dir1) ------|
* ------------------------------------------------------------------------------------------
* | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
* ------------------------------------------------------------------------------------------
* ^ ^
* TView.bindingStartIndex TView.expandoStartIndex
*
* 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
* root is the bindingStartIndex. In host bindings, the binding root is the hostBindingStartIndex +
* any hostVars in directives evaluated before it.
* root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
* 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 {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
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 {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|
null {
const defs = currentView[TVIEW].directives;
const defs = currentView[TVIEW].data;
if (defs) {
const flags = tNode.flags;
const count = flags & TNodeFlags.DirectiveCountMask;
@ -275,7 +275,7 @@ function queryRead(tNode: TNode, currentView: LViewData, read: any): any {
} else {
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
if (matchingIdx !== null) {
return currentView[DIRECTIVES] ![matchingIdx];
return currentView[matchingIdx];
}
}
return null;
@ -314,7 +314,7 @@ function add(
result = queryRead(tNode, currentView, predicate.read);
} else {
if (directiveIdx > -1) {
result = currentView[DIRECTIVES] ![directiveIdx];
result = currentView[directiveIdx];
} else {
// if read token and / or strategy is not specified,
// 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) {
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 {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';
@ -256,7 +256,7 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
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": "DIRECTIVES"
},
{
"name": "DefaultIterableDiffer"
},
@ -164,6 +161,9 @@
{
"name": "RENDER_PARENT"
},
{
"name": "ROOT_EXPANDO_INSTRUCTIONS"
},
{
"name": "RecordViewTuple"
},
@ -527,6 +527,9 @@
{
"name": "firstTemplatePass"
},
{
"name": "generateExpandoBlock"
},
{
"name": "generateInitialInputs"
},
@ -833,6 +836,9 @@
{
"name": "pointers"
},
{
"name": "prefillHostVars"
},
{
"name": "prepareInitialFlag"
},
@ -905,9 +911,6 @@
{
"name": "resolveDirective"
},
{
"name": "sameHostView"
},
{
"name": "saveNameToExportMap"
},
@ -968,6 +971,9 @@
{
"name": "setValue"
},
{
"name": "shouldNotSearchParent"
},
{
"name": "storeCleanupFn"
},

View File

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

View File

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

View File

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

View File

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

View File

@ -121,7 +121,7 @@ describe('queries', () => {
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(
dirIndex: $number$, queryStartIndex: $number$) {
let $tmp$: any;
const $instance$ = $r3$.ɵloadDirective<ContentQueryComponent>(dirIndex);
const $instance$ = $r3$.ɵload<ContentQueryComponent>(dirIndex);
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex)) &&
($instance$.someDir = $tmp$.first);
$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 {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 {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 {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
@ -677,8 +677,7 @@ describe('di', () => {
factory: () => hostBindingDir = new HostBindingDir(),
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
}
});
}
@ -1298,7 +1297,8 @@ describe('di', () => {
projectionDef();
projection(0);
}
}
},
features: [PublicFeature]
});
}
@ -1323,7 +1323,8 @@ describe('di', () => {
type: DirectiveSameInstance,
selectors: [['', 'dirSame', '']],
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));
}
},
directives: directives
directives: directives,
features: [PublicFeature]
});
}
@ -1563,6 +1565,7 @@ describe('di', () => {
});
describe('@Attribute', () => {
let myDirectiveInstance !: MyDirective | null;
class MyDirective {
exists = 'wrong' as string | undefined;
@ -1577,10 +1580,13 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
type: 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', () => {
let exist = 'wrong' as string | undefined;
let nonExist = 'wrong' as string | undefined;
@ -1843,7 +1849,7 @@ describe('di', () => {
(parentTNode as{parent: any}).parent = undefined;
const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904)
expect(injector).not.toBe(null);
expect(injector).not.toEqual(-1);
} finally {
leaveView(oldView);
}

View File

@ -11,10 +11,10 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
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 {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 {Sanitizer, SecurityContext} from '../../src/sanitization/security';
@ -449,8 +449,7 @@ describe('render3 integration test', () => {
hostBindings: function(directiveIndex: number, elementIndex: number): void {
// host bindings
elementProperty(
elementIndex, 'title',
bind(loadDirective<TodoComponentHostBinding>(directiveIndex).title));
elementIndex, 'title', bind(load<TodoComponentHostBinding>(directiveIndex).title));
}
});
}
@ -750,7 +749,7 @@ describe('render3 integration test', () => {
type: TestDirective,
selectors: [['', 'testDirective', '']],
factory:
() => new TestDirective(
() => testDirective = new TestDirective(
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
});
}
@ -777,9 +776,6 @@ describe('render3 integration test', () => {
template(
0, embeddedTemplate, 2, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
}
if (rf & RenderFlags.Update) {
testDirective = loadDirective<TestDirective>(0);
}
}, 1, 0, [TestDirective]);
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', () => {
let testDirective: TestDirective;
class TestDirective {
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
@ -857,12 +854,11 @@ describe('render3 integration test', () => {
type: TestDirective,
selectors: [['', 'testDirective', '']],
factory:
() => new TestDirective(
() => testDirective = new TestDirective(
directiveInject(TemplateRef as any), directiveInject(ViewContainerRef as any)),
});
}
let testDirective: TestDirective;
function embeddedTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
@ -895,9 +891,6 @@ describe('render3 integration test', () => {
if (rf & RenderFlags.Create) {
template(0, embeddedTemplate, 4, 0, null, [AttributeMarker.SelectOnly, 'testDirective']);
}
if (rf & RenderFlags.Update) {
testDirective = loadDirective<TestDirective>(0);
}
}, 1, 0, [TestDirective]);
function App() { element(0, 'test-cmpt'); }
@ -926,7 +919,7 @@ describe('render3 integration test', () => {
static ngDirectiveDef = defineDirective({
type: Directive,
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']);
elementContainerEnd();
directive = loadDirective<Directive>(0);
}
elementEnd();
}
@ -1277,8 +1269,7 @@ describe('render3 integration test', () => {
},
hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) {
elementAttribute(
elIndex, 'aria-label', bind(loadDirective<HostBindingDir>(dirIndex).label));
elementAttribute(elIndex, 'aria-label', bind(load<HostBindingDir>(dirIndex).label));
}
});
}
@ -2112,11 +2103,10 @@ describe('render3 integration test', () => {
const context = getContext(hostElm) !;
const elementNode = context.lViewData[context.nodeIndex];
const elmData = elementNode.data !;
const dirs = elmData[DIRECTIVES];
expect(dirs).toContain(myDir1Instance);
expect(dirs).toContain(myDir2Instance);
expect(dirs).toContain(myDir3Instance);
expect(elmData).toContain(myDir1Instance);
expect(elmData).toContain(myDir2Instance);
expect(elmData).toContain(myDir3Instance);
expect(Array.isArray((myDir1Instance 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 {AttributeMarker, PublicFeature, defineComponent, 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 {AttributeMarker, PublicFeature, defineComponent, template, defineDirective} from '../../src/render3/index';
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 {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', () => {
@ -109,8 +110,7 @@ describe('elementProperty', () => {
factory: () => directiveInstance = new Directive,
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(
elementIndex, 'className', bind(loadDirective<Directive>(directiveIndex).klass));
elementProperty(elementIndex, 'className', bind(load<Directive>(directiveIndex).klass));
}
});
}
@ -138,7 +138,7 @@ describe('elementProperty', () => {
vars: 0,
hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => {
const instance = loadDirective(dirIndex) as HostBindingComp;
const instance = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'id', bind(instance.id));
},
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -153,6 +153,71 @@ describe('elementProperty', () => {
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', () => {
class HostBindingDir {
// @HostBinding()
@ -164,8 +229,7 @@ describe('elementProperty', () => {
factory: () => new HostBindingDir(),
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(
elementIndex, 'id', bind(loadDirective<HostBindingDir>(directiveIndex).id));
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
});
@ -195,6 +259,55 @@ describe('elementProperty', () => {
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', () => {
const ff = (v: any) => ['Nancy', v, 'Ned'];
@ -210,7 +323,7 @@ describe('elementProperty', () => {
vars: 0,
hostVars: 1,
hostBindings: (dirIndex: number, elIndex: number) => {
const ctx = loadDirective(dirIndex) as HostBindingComp;
const ctx = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'id', bind(ctx.id));
},
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -283,7 +396,7 @@ describe('elementProperty', () => {
vars: 0,
hostVars: 8,
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]
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
elementProperty(elIndex, 'dir', bind(ctx.dir));
@ -359,7 +472,7 @@ describe('elementProperty', () => {
hostVars: 3,
hostBindings: (dirIndex: number, elIndex: number) => {
// 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)));
},
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
@ -387,7 +500,7 @@ describe('elementProperty', () => {
hostVars: 3,
hostBindings: (dirIndex: number, elIndex: number) => {
// 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)));
}
});
@ -448,7 +561,7 @@ describe('elementProperty', () => {
hostVars: 6,
hostBindings: (dirIndex: number, elIndex: number) => {
// LViewData: [..., id, title, ctx.id, pf1, ctx.title, pf1]
const ctx = loadDirective(dirIndex) as HostBindingComp;
const ctx = load(dirIndex) as HostBindingComp;
elementProperty(
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
elementProperty(

View File

@ -14,7 +14,7 @@ import {directiveInject} from '../../src/render3/di';
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 {query, queryRefresh} from '../../src/render3/query';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
@ -1914,7 +1914,7 @@ describe('query', () => {
contentQueries: () => { registerContentQuery(query(null, ['foo'], true)); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
withContentInstance = loadDirective<WithContentDirective>(dirIndex);
withContentInstance = load<WithContentDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(withContentInstance.foos = tmp);
}
@ -1935,7 +1935,7 @@ describe('query', () => {
contentQueries: () => { registerContentQuery(query(null, ['foo'], false)); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
shallowCompInstance = loadDirective<ShallowComp>(dirIndex);
shallowCompInstance = load<ShallowComp>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(shallowCompInstance.foos = tmp);
}
@ -2116,7 +2116,7 @@ describe('query', () => {
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = loadDirective<QueryDirective>(dirIndex);
const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.fooBars = tmp);
},
@ -2180,7 +2180,7 @@ describe('query', () => {
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = loadDirective<QueryDirective>(dirIndex);
const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.fooBars = tmp);
},
@ -2233,7 +2233,7 @@ describe('query', () => {
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = loadDirective<ShallowQueryDirective>(dirIndex);
const instance = load<ShallowQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.foos = tmp);
},
@ -2253,7 +2253,7 @@ describe('query', () => {
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = loadDirective<DeepQueryDirective>(dirIndex);
const instance = load<DeepQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(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 {ViewEncapsulation} from '../../src/metadata';
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 {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
import {NgModuleFactory} from '../../src/render3/ng_module_ref';
@ -1728,7 +1728,7 @@ describe('ViewContainerRef', () => {
hostVars: 1,
attributes: ['id', 'attribute'],
hostBindings: function(dirIndex, elIndex) {
const cmptInstance = loadDirective<HostBindingCmpt>(dirIndex);
const cmptInstance = load<HostBindingCmpt>(dirIndex);
elementProperty(elIndex, 'title', bind(cmptInstance.title));
},
});