feat(ivy): now supports SVG and MathML elements (#24377)
- Adds support for ivy creating SVG and MathML elements properly using createElementNS PR Close #24377
This commit is contained in:
@ -17,6 +17,12 @@ export class Identifiers {
|
||||
static PATCH_DEPS = 'patchedDeps';
|
||||
|
||||
/* Instructions */
|
||||
static namespaceHTML: o.ExternalReference = {name: 'ɵNH', moduleName: CORE};
|
||||
|
||||
static namespaceMathML: o.ExternalReference = {name: 'ɵNM', moduleName: CORE};
|
||||
|
||||
static namespaceSVG: o.ExternalReference = {name: 'ɵNS', moduleName: CORE};
|
||||
|
||||
static createElement: o.ExternalReference = {name: 'ɵE', moduleName: CORE};
|
||||
|
||||
static elementEnd: o.ExternalReference = {name: 'ɵe', moduleName: CORE};
|
||||
|
@ -136,7 +136,8 @@ export function compileComponentFromMetadata(
|
||||
const templateFunctionExpression =
|
||||
new TemplateDefinitionBuilder(
|
||||
constantPool, CONTEXT_NAME, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName,
|
||||
meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed)
|
||||
meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed,
|
||||
R3.namespaceHTML)
|
||||
.buildTemplateFunction(
|
||||
template.nodes, [], template.hasNgContent, template.ngContentSelectors);
|
||||
|
||||
@ -443,4 +444,4 @@ function typeMapToExpressionMap(
|
||||
const entries = Array.from(map).map(
|
||||
([key, type]): [string, o.Expression] => [key, outputCtx.importExpr(type)]);
|
||||
return new Map(entries);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import * as html from '../../ml_parser/ast';
|
||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
||||
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import {splitNsName} from '../../ml_parser/tags';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
||||
import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry';
|
||||
@ -66,7 +67,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
parentBindingScope: BindingScope, private level = 0, private contextName: string|null,
|
||||
private templateName: string|null, private viewQueries: R3QueryMetadata[],
|
||||
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
|
||||
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>) {
|
||||
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>,
|
||||
private _namespace: o.ExternalReference) {
|
||||
this._bindingScope =
|
||||
parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => {
|
||||
this._bindingCode.push(
|
||||
@ -89,6 +91,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
buildTemplateFunction(
|
||||
nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false,
|
||||
ngContentSelectors: string[] = []): o.FunctionExpr {
|
||||
if (this._namespace !== R3.namespaceHTML) {
|
||||
this.instruction(this._creationCode, null, this._namespace);
|
||||
}
|
||||
|
||||
// Create variable bindings
|
||||
for (const variable of variables) {
|
||||
const variableName = variable.name;
|
||||
@ -220,6 +226,23 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
this.instruction(this._creationCode, ngContent.sourceSpan, R3.projection, ...parameters);
|
||||
}
|
||||
|
||||
|
||||
getNamespaceInstruction(namespaceKey: string|null) {
|
||||
switch (namespaceKey) {
|
||||
case 'math':
|
||||
return R3.namespaceMathML;
|
||||
case 'svg':
|
||||
return R3.namespaceSVG;
|
||||
default:
|
||||
return R3.namespaceHTML;
|
||||
}
|
||||
}
|
||||
|
||||
addNamespaceInstruction(nsInstruction: o.ExternalReference, element: t.Element) {
|
||||
this._namespace = nsInstruction;
|
||||
this.instruction(this._creationCode, element.sourceSpan, nsInstruction);
|
||||
}
|
||||
|
||||
visitElement(element: t.Element) {
|
||||
const elementIndex = this.allocateDataSlot();
|
||||
const referenceDataSlots = new Map<string, number>();
|
||||
@ -229,6 +252,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const attrI18nMetas: {[name: string]: string} = {};
|
||||
let i18nMeta: string = '';
|
||||
|
||||
const [namespaceKey, elementName] = splitNsName(element.name);
|
||||
|
||||
// Elements inside i18n sections are replaced with placeholders
|
||||
// TODO(vicb): nested elements are a WIP in this phase
|
||||
if (this._inI18nSection) {
|
||||
@ -269,7 +294,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// Element creation mode
|
||||
const parameters: o.Expression[] = [
|
||||
o.literal(elementIndex),
|
||||
o.literal(element.name),
|
||||
o.literal(elementName),
|
||||
];
|
||||
|
||||
// Add the attributes
|
||||
@ -314,6 +339,16 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
if (i18nMessages.length > 0) {
|
||||
this._creationCode.push(...i18nMessages);
|
||||
}
|
||||
|
||||
const wasInNamespace = this._namespace;
|
||||
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
||||
|
||||
// If the namespace is changing now, include an instruction to change it
|
||||
// during element creation.
|
||||
if (currentNamespace !== wasInNamespace) {
|
||||
this.addNamespaceInstruction(currentNamespace, element);
|
||||
}
|
||||
|
||||
this.instruction(
|
||||
this._creationCode, element.sourceSpan, R3.createElement, ...trimTrailingNulls(parameters));
|
||||
|
||||
@ -433,7 +468,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
// Create the template function
|
||||
const templateVisitor = new TemplateDefinitionBuilder(
|
||||
this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName,
|
||||
templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes);
|
||||
templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes,
|
||||
this._namespace);
|
||||
const templateFunctionExpr =
|
||||
templateVisitor.buildTemplateFunction(template.children, template.variables);
|
||||
this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||
|
Reference in New Issue
Block a user