, 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);
diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts
index 571733e2c4..b75f578d9a 100644
--- a/packages/core/src/render3/di.ts
+++ b/packages/core/src/render3/di.ts
@@ -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;
}
diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts
index 1350085948..79bf033e9e 100644
--- a/packages/core/src/render3/interfaces/node.ts
+++ b/packages/core/src/render3/interfaces/node.ts
@@ -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:
+ *
+ * ```
+ *
+ * ```
+ *
+ * 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;
}
/**
diff --git a/packages/core/src/render3/node_selector_matcher.ts b/packages/core/src/render3/node_selector_matcher.ts
index f85c15b28d..b5c63a4980 100644
--- a/packages/core/src/render3/node_selector_matcher.ts
+++ b/packages/core/src/render3/node_selector_matcher.ts
@@ -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;
diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
index 4da4db673a..40a176494e 100644
--- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json
@@ -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"
},
diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json
index 87495a342b..53a801a8d9 100644
--- a/packages/core/test/bundling/todo/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json
@@ -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"
},