feat(ivy): namespaced attributes added to output instructions (#24386)
NOTE: This does NOT add parsing of namespaced attributes - Adds AttributeMarker for namespaced attributes - Adds test for namespaced attributes - Updates AttributeMarker enum to use CamelCase, and not UPPER_CASE names PR Close #24386
This commit is contained in:
@ -147,6 +147,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S
|
||||
| `<div style="literal">` | ✅ | ✅ | ✅ |
|
||||
| `<div [style]="exp">` | ✅ | ✅ | ✅ |
|
||||
| `<div [style.foo]="exp">` | ✅ | ✅ | ✅ |
|
||||
| `<div xmlns:foo="url" foo:bar="baz">` <br/>Compiler still needs to be updated to process templates with namespaced attributes. ([see #24386](https://github.com/angular/angular/pull/24386)) | ✅ | ✅ | ❌ |
|
||||
| `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ |
|
||||
| `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ |
|
||||
| `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ |
|
||||
|
@ -262,7 +262,7 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
|
||||
if (attrs) {
|
||||
for (let i = 0; i < attrs.length; i = i + 2) {
|
||||
const attrName = attrs[i];
|
||||
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
||||
if (attrName === AttributeMarker.SelectOnly) break;
|
||||
if (attrName == attrNameToInject) {
|
||||
return attrs[i + 1] as string;
|
||||
}
|
||||
|
@ -858,16 +858,34 @@ export function createTView(
|
||||
|
||||
function setUpAttributes(native: RElement, attrs: TAttributes): void {
|
||||
const isProc = isProceduralRenderer(renderer);
|
||||
for (let i = 0; i < attrs.length; i += 2) {
|
||||
let i = 0;
|
||||
|
||||
while (i < attrs.length) {
|
||||
const attrName = attrs[i];
|
||||
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
||||
if (attrName !== NG_PROJECT_AS_ATTR_NAME) {
|
||||
const attrVal = attrs[i + 1];
|
||||
if (attrName === AttributeMarker.SelectOnly) break;
|
||||
if (attrName === NG_PROJECT_AS_ATTR_NAME) {
|
||||
i += 2;
|
||||
} else {
|
||||
ngDevMode && ngDevMode.rendererSetAttribute++;
|
||||
isProc ?
|
||||
(renderer as ProceduralRenderer3)
|
||||
.setAttribute(native, attrName as string, attrVal as string) :
|
||||
native.setAttribute(attrName as string, attrVal as string);
|
||||
if (attrName === AttributeMarker.NamespaceURI) {
|
||||
// Namespaced attributes
|
||||
const namespaceURI = attrs[i + 1] as string;
|
||||
const attrName = attrs[i + 2] as string;
|
||||
const attrVal = attrs[i + 3] as string;
|
||||
isProc ?
|
||||
(renderer as ProceduralRenderer3)
|
||||
.setAttribute(native, attrName, attrVal, namespaceURI) :
|
||||
native.setAttributeNS(namespaceURI, attrName, attrVal);
|
||||
i += 4;
|
||||
} else {
|
||||
// Standard attributes
|
||||
const attrVal = attrs[i + 1];
|
||||
isProc ?
|
||||
(renderer as ProceduralRenderer3)
|
||||
.setAttribute(native, attrName as string, attrVal as string) :
|
||||
native.setAttribute(attrName as string, attrVal as string);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1509,17 +1527,25 @@ function generateInitialInputs(
|
||||
initialInputData[directiveIndex] = null;
|
||||
|
||||
const attrs = tNode.attrs !;
|
||||
for (let i = 0; i < attrs.length; i += 2) {
|
||||
let i = 0;
|
||||
while (i < attrs.length) {
|
||||
const attrName = attrs[i];
|
||||
if (attrName === AttributeMarker.SelectOnly) break;
|
||||
if (attrName === AttributeMarker.NamespaceURI) {
|
||||
// We do not allow inputs on namespaced attributes.
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
const minifiedInputName = inputs[attrName];
|
||||
const attrValue = attrs[i + 1];
|
||||
|
||||
if (attrName === AttributeMarker.SELECT_ONLY) break;
|
||||
if (minifiedInputName !== undefined) {
|
||||
const inputsToStore: InitialInputs =
|
||||
initialInputData[directiveIndex] || (initialInputData[directiveIndex] = []);
|
||||
inputsToStore.push(minifiedInputName, attrValue as string);
|
||||
}
|
||||
|
||||
i += 2;
|
||||
}
|
||||
return initialInputData;
|
||||
}
|
||||
|
@ -163,15 +163,20 @@ export interface LProjectionNode extends LNode {
|
||||
* items are not regular attributes and the processing should be adapted accordingly.
|
||||
*/
|
||||
export const enum AttributeMarker {
|
||||
NS = 0, // namespace. Has to be repeated.
|
||||
/**
|
||||
* Marker indicates that the following 3 values in the attributes array are:
|
||||
* namespaceUri, attributeName, attributeValue
|
||||
* in that order.
|
||||
*/
|
||||
NamespaceURI = 0,
|
||||
|
||||
/**
|
||||
* This marker indicates that the following attribute names were extracted from bindings (ex.:
|
||||
* [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
|
||||
* Taking the above bindings and outputs as an example an attributes array could look as follows:
|
||||
* ['class', 'fade in', AttributeMarker.SELECT_ONLY, 'foo', 'bar']
|
||||
* ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
|
||||
*/
|
||||
SELECT_ONLY = 1
|
||||
SelectOnly = 1
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {assertDefined, assertNotEqual} from './assert';
|
||||
import {AttributeMarker, TAttributes, TNode, unusedValueExportToPlacateAjd as unused1} from './interfaces/node';
|
||||
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection';
|
||||
|
||||
@ -40,7 +40,7 @@ export function isNodeMatchingSelector(tNode: TNode, selector: CssSelector): boo
|
||||
|
||||
let mode: SelectorFlags = SelectorFlags.ELEMENT;
|
||||
const nodeAttrs = tNode.attrs !;
|
||||
const selectOnlyMarkerIdx = nodeAttrs ? nodeAttrs.indexOf(AttributeMarker.SELECT_ONLY) : -1;
|
||||
const selectOnlyMarkerIdx = nodeAttrs ? nodeAttrs.indexOf(AttributeMarker.SelectOnly) : -1;
|
||||
|
||||
// When processing ":not" selectors, we skip to the next ":not" if the
|
||||
// current one doesn't match
|
||||
@ -81,9 +81,16 @@ export function isNodeMatchingSelector(tNode: TNode, selector: CssSelector): boo
|
||||
|
||||
const selectorAttrValue = mode & SelectorFlags.CLASS ? current : selector[++i];
|
||||
if (selectorAttrValue !== '') {
|
||||
const nodeAttrValue = selectOnlyMarkerIdx > -1 && attrIndexInNode > selectOnlyMarkerIdx ?
|
||||
'' :
|
||||
nodeAttrs[attrIndexInNode + 1];
|
||||
let nodeAttrValue: string;
|
||||
const maybeAttrName = nodeAttrs[attrIndexInNode];
|
||||
if (selectOnlyMarkerIdx > -1 && attrIndexInNode > selectOnlyMarkerIdx) {
|
||||
nodeAttrValue = '';
|
||||
} else {
|
||||
ngDevMode && assertNotEqual(
|
||||
maybeAttrName, AttributeMarker.NamespaceURI,
|
||||
'We do not match directives on namespaced attributes');
|
||||
nodeAttrValue = nodeAttrs[attrIndexInNode + 1] as string;
|
||||
}
|
||||
if (mode & SelectorFlags.CLASS &&
|
||||
!isCssClassMatching(nodeAttrValue as string, selectorAttrValue as string) ||
|
||||
mode & SelectorFlags.ATTRIBUTE && selectorAttrValue !== nodeAttrValue) {
|
||||
@ -101,16 +108,34 @@ function isPositive(mode: SelectorFlags): boolean {
|
||||
return (mode & SelectorFlags.NOT) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines an attributes definition array from a node to find the index of the
|
||||
* attribute with the specified name.
|
||||
*
|
||||
* NOTE: Will not find namespaced attributes.
|
||||
*
|
||||
* @param name the name of the attribute to find
|
||||
* @param attrs the attribute array to examine
|
||||
*/
|
||||
function findAttrIndexInNode(name: string, attrs: TAttributes | null): number {
|
||||
let step = 2;
|
||||
if (attrs === null) return -1;
|
||||
for (let i = 0; i < attrs.length; i += step) {
|
||||
const attrName = attrs[i];
|
||||
if (attrName === name) return i;
|
||||
if (attrName === AttributeMarker.SELECT_ONLY) {
|
||||
step = 1;
|
||||
let selectOnlyMode = false;
|
||||
let i = 0;
|
||||
while (i < attrs.length) {
|
||||
const maybeAttrName = attrs[i];
|
||||
if (maybeAttrName === name) {
|
||||
return i;
|
||||
} else if (maybeAttrName === AttributeMarker.NamespaceURI) {
|
||||
// NOTE(benlesh): will not find namespaced attributes. This is by design.
|
||||
i += 4;
|
||||
} else {
|
||||
if (maybeAttrName === AttributeMarker.SelectOnly) {
|
||||
selectOnlyMode = true;
|
||||
}
|
||||
i += selectOnlyMode ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user