refactor(ivy): define new `AttributeMarker.Template` marker (#29041)

This commit adds a new `AttributeMarker` type that will be used, in a
future commit, to mark attributes as coming from an inline-template
expansion, rather than the element that is being contained in the template.

PR Close #29041
This commit is contained in:
Pete Bacon Darwin 2019-03-07 08:31:31 +00:00 committed by Kara Erickson
parent 423ac01dcf
commit e3a401d20c
7 changed files with 106 additions and 10 deletions

View File

@ -450,4 +450,29 @@ export const enum AttributeMarker {
* ```
*/
Bindings = 3,
/**
* Signals that the following attribute names were hoisted from an inline-template declaration.
*
* For example, given the following HTML:
*
* ```
* <div *ngFor="let value of values; trackBy:trackBy" dirA [dirB]="value">
* ```
*
* the generated code for the `template()` instruction would include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB', AttributeMarker.Template, 'ngFor', 'ngForOf',
* 'ngForTrackBy', 'let-value']
* ```
*
* while the generated code for the `element()` instruction inside the template function would
* include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB']
* ```
*/
Template = 4,
}

View File

@ -1044,7 +1044,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
* attrs = [prop, value, prop2, value2,
* CLASSES, class1, class2,
* STYLES, style1, value1, style2, value2,
* BINDINGS, name1, name2, name3, ...]
* BINDINGS, name1, name2, name3,
* TEMPLATE, name4, name5, ...]
* ```
*
* Note that this function will fully ignore all synthetic (@foo) attribute values
@ -1068,8 +1069,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
}
// it's important that this occurs before BINDINGS because once `elementStart`
// comes across the BINDINGS marker then it will continue reading each value as
// it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
// comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
// as single property value cell by cell.
if (styles) {
styles.populateInitialStylingAttrs(attrExprs);

View File

@ -17,7 +17,7 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
import {NG_ELEMENT_ID} from './fields';
import {DirectiveDef} from './interfaces/definition';
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, isNameOnlyAttributeMarker} from './interfaces/node';
import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
@ -272,9 +272,10 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
if (attrs) {
for (let i = 0; i < attrs.length; i = i + 2) {
const attrName = attrs[i];
if (attrName === AttributeMarker.Bindings) break;
// TODO: What happens here if an attribute has a namespace?
// TODO: What happens here if the attribute name happens to match a CSS class or style?
// If we hit a `Bindings` or `Template` marker then we are done.
if (isNameOnlyAttributeMarker(attrName)) break;
// TODO(FW-1137): Skip namespaced attributes
// TODO(FW-1139): supports classes/styles in @Attribute injection
if (attrName == attrNameToInject) {
return attrs[i + 1] as string;
}

View File

@ -122,6 +122,35 @@ export const enum AttributeMarker {
* ```
*/
Bindings = 3,
/**
* Signals that the following attribute names were hoisted from an inline-template declaration.
*
* For example, given the following HTML:
*
* ```
* <div *ngFor="let value of values; trackBy:trackBy" dirA [dirB]="value">
* ```
*
* the generated code for the `template()` instruction would include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB', AttributeMarker.Template, 'ngFor', 'ngForOf',
* 'ngForTrackBy', 'let-value']
* ```
*
* while the generated code for the `element()` instruction inside the template function would
* include:
*
* ```
* ['dirA', '', AttributeMarker.Bindings, 'dirB']
* ```
*/
Template = 4,
}
export function isNameOnlyAttributeMarker(marker: string | AttributeMarker) {
return marker === AttributeMarker.Bindings || marker === AttributeMarker.Template;
}
/**

View File

@ -9,7 +9,8 @@
import '../util/ng_dev_mode';
import {assertDefined, assertNotEqual} from '../util/assert';
import {AttributeMarker, TAttributes, TNode, TNodeType, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
import {AttributeMarker, TAttributes, TNode, TNodeType, isNameOnlyAttributeMarker, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection';
import {getInitialClassNameValue} from './styling/class_and_style_bindings';
@ -61,7 +62,16 @@ export function isNodeMatchingSelector(
ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
let mode: SelectorFlags = SelectorFlags.ELEMENT;
const nodeAttrs = tNode.attrs || [];
const nameOnlyMarkerIdx = nodeAttrs.indexOf(AttributeMarker.Bindings);
// Find the index of first attribute that has no value, only a name.
let nameOnlyMarkerIdx = nodeAttrs && nodeAttrs.length;
for (let i = 0; i < nodeAttrs.length; i++) {
const nodeAttr = nodeAttrs[i];
if (isNameOnlyAttributeMarker(nodeAttr)) {
nameOnlyMarkerIdx = i;
break;
}
}
// When processing ":not" selectors, we skip to the next ":not" if the
// current one doesn't match
@ -174,7 +184,7 @@ function findAttrIndexInNode(name: string, attrs: TAttributes | null): number {
// NOTE(benlesh): will not find namespaced attributes. This is by design.
i += 4;
} else {
if (maybeAttrName === AttributeMarker.Bindings) {
if (isNameOnlyAttributeMarker(maybeAttrName)) {
nameOnlyMode = true;
}
i += nameOnlyMode ? 1 : 2;

View File

@ -374,6 +374,9 @@
{
"name": "getLViewParent"
},
{
"name": "getNameOnlyMarkerIndex"
},
{
"name": "getNativeAnchorNode"
},
@ -494,6 +497,9 @@
{
"name": "isLView"
},
{
"name": "isNameOnlyAttributeMarker"
},
{
"name": "isNodeMatchingSelector"
},
@ -518,6 +524,9 @@
{
"name": "locateHostElement"
},
{
"name": "matchTemplateAttribute"
},
{
"name": "namespaceHTML"
},

View File

@ -323,9 +323,21 @@
{
"name": "_c18"
},
{
"name": "_c19"
},
{
"name": "_c2"
},
{
"name": "_c20"
},
{
"name": "_c21"
},
{
"name": "_c22"
},
{
"name": "_c3"
},
@ -749,6 +761,9 @@
{
"name": "getMultiStylesStartIndex"
},
{
"name": "getNameOnlyMarkerIndex"
},
{
"name": "getNativeAnchorNode"
},
@ -983,6 +998,9 @@
{
"name": "isListLikeIterable"
},
{
"name": "isNameOnlyAttributeMarker"
},
{
"name": "isNodeMatchingSelector"
},
@ -1040,6 +1058,9 @@
{
"name": "markViewDirty"
},
{
"name": "matchTemplateAttribute"
},
{
"name": "namespaceHTML"
},