fix(ivy): cache local names and support multiple locals with same value (#22807)

PR Close #22807
This commit is contained in:
Kara Erickson
2018-03-15 12:18:31 -07:00
committed by Miško Hevery
parent 9220521149
commit bafdad9083
2 changed files with 102 additions and 27 deletions

View File

@ -491,13 +491,11 @@ export function elementStart(
// accessed through their containers because they may be removed / re-added later.
node = createLNode(index, LNodeFlags.Element, native, componentView);
// TODO(misko): implement code which caches the local reference resolution
const queryName: string|null = hack_findQueryName(hostComponentDef, localRefs, '');
if (node.tNode == null) {
const localNames: (string | number)[]|null =
findMatchingLocalNames(hostComponentDef, localRefs, isHostElement ? index + 1 : -1, '');
ngDevMode && assertDataInRange(index - 1);
node.tNode = tData[index] =
createTNode(name, attrs || null, null, hostComponentDef ? null : queryName);
node.tNode = tData[index] = createTNode(name, attrs || null, null, localNames);
}
if (attrs) setUpAttributes(native, attrs);
@ -508,7 +506,7 @@ export function elementStart(
// is not guaranteed. Must be refactored to take it into account.
const instance = hostComponentDef.n();
storeComponentIndex(index);
directiveCreate(++index, instance, hostComponentDef, queryName);
directiveCreate(++index, instance, hostComponentDef, null);
initChangeDetectorIfExisting(node.nodeInjector, instance);
}
hack_declareDirectives(index, directiveTypes, localRefs);
@ -532,8 +530,8 @@ export function initChangeDetectorIfExisting(injector: LInjector | null, instanc
}
/**
* This function instantiates a directive with a correct queryName. It is a hack since we should
* compute the query value only once and store it with the template (rather than on each invocation)
* This function instantiates the given directives. It is a hack since it assumes the directives
* come in the correct order for DI.
*/
function hack_declareDirectives(
index: number, directiveTypes: DirectiveType<any>[] | null | undefined,
@ -546,30 +544,35 @@ function hack_declareDirectives(
const directiveType = directiveTypes[i];
const directiveDef = currentView.tView.firstTemplatePass ? directiveType.ngDirectiveDef :
tData[index] as DirectiveDef<any>;
directiveCreate(
index, directiveDef.n(), directiveDef, hack_findQueryName(directiveDef, localRefs));
const localNames = currentView.tView.firstTemplatePass ?
findMatchingLocalNames(directiveDef, localRefs, index) :
null;
directiveCreate(index, directiveDef.n(), directiveDef, localNames);
}
}
}
/**
* This function returns the queryName for a directive. It is a hack since we should
* compute the query value only once and store it with the template (rather than on each invocation)
* Finds any local names that match the given directive's exportAs and returns them with directive
* index. If the directiveDef is null, it matches against the default '' value instead of
* exportAs.
*/
function hack_findQueryName(
directiveDef: DirectiveDef<any>| null, localRefs: string[] | null | undefined,
defaultExport?: string, ): string|null {
function findMatchingLocalNames(
directiveDef: DirectiveDef<any>| null, localRefs: string[] | null | undefined, index: number,
defaultExport?: string): (string | number)[]|null {
const exportAs = directiveDef && directiveDef.exportAs || defaultExport;
let matches: (string | number)[]|null = null;
if (exportAs != null && localRefs) {
for (let i = 0; i < localRefs.length; i = i + 2) {
const local = localRefs[i];
const toExportAs = localRefs[i | 1];
if (toExportAs === exportAs || toExportAs === defaultExport) {
return local;
(matches || (matches = [])).push(local, index);
}
}
}
return null;
return matches;
}
/**
@ -801,15 +804,16 @@ export function elementProperty<T>(
* @param tagName
* @param attrs
* @param data
* @param localNames A list of local names and their matching indices
* @returns the TNode object
*/
function createTNode(
tagName: string | null, attrs: string[] | null, data: TContainer | null,
localName: string | null): TNode {
localNames: (string | number)[] | null): TNode {
return {
tagName: tagName,
attrs: attrs,
localNames: localName ? [localName, -1] : null,
localNames: localNames,
initialInputs: undefined,
inputs: undefined,
outputs: undefined,
@ -1043,10 +1047,11 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
* be created or retrieved out of order.
* @param directive The directive instance.
* @param directiveDef DirectiveDef object which contains information about the template.
* @param queryName Name under which the query can retrieve the directive instance.
* @param localNames Names under which a query can retrieve the directive instance
*/
export function directiveCreate<T>(
index: number, directive: T, directiveDef: DirectiveDef<T>, queryName?: string | null): T {
index: number, directive: T, directiveDef: DirectiveDef<T>,
localNames?: (string | number)[] | null): T {
let instance;
ngDevMode &&
assertNull(currentView.bindingStartIndex, 'directives should be created before any bindings');
@ -1068,10 +1073,10 @@ export function directiveCreate<T>(
if (index >= tData.length) {
tData[index] = directiveDef !;
if (queryName) {
if (localNames) {
ngDevMode && assertNotNull(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
const tNode = previousOrParentNode !.tNode !;
(tNode.localNames || (tNode.localNames = [])).push(queryName, index);
tNode.localNames = tNode.localNames ? tNode.localNames.concat(localNames) : localNames;
}
}
@ -1197,9 +1202,8 @@ export function container(
const node = createLNode(index, LNodeFlags.Container, undefined, lContainer);
if (node.tNode == null) {
// TODO(misko): implement queryName caching
const queryName: string|null = hack_findQueryName(null, localRefs, '');
node.tNode = tData[index] = createTNode(tagName || null, attrs || null, [], queryName || null);
const localNames: (string | number)[]|null = findMatchingLocalNames(null, localRefs, -1, '');
node.tNode = tData[index] = createTNode(tagName || null, attrs || null, [], localNames);
}
// Containers are added to the current view tree instead of their embedded views