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:
parent
5ef7a07c4b
commit
8c1ac28275
|
@ -17,6 +17,12 @@ export class Identifiers {
|
||||||
static PATCH_DEPS = 'patchedDeps';
|
static PATCH_DEPS = 'patchedDeps';
|
||||||
|
|
||||||
/* Instructions */
|
/* 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 createElement: o.ExternalReference = {name: 'ɵE', moduleName: CORE};
|
||||||
|
|
||||||
static elementEnd: o.ExternalReference = {name: 'ɵe', moduleName: CORE};
|
static elementEnd: o.ExternalReference = {name: 'ɵe', moduleName: CORE};
|
||||||
|
|
|
@ -136,7 +136,8 @@ export function compileComponentFromMetadata(
|
||||||
const templateFunctionExpression =
|
const templateFunctionExpression =
|
||||||
new TemplateDefinitionBuilder(
|
new TemplateDefinitionBuilder(
|
||||||
constantPool, CONTEXT_NAME, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName,
|
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(
|
.buildTemplateFunction(
|
||||||
template.nodes, [], template.hasNgContent, template.ngContentSelectors);
|
template.nodes, [], template.hasNgContent, template.ngContentSelectors);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import * as html from '../../ml_parser/ast';
|
||||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
import {HtmlParser} from '../../ml_parser/html_parser';
|
||||||
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||||
|
import {splitNsName} from '../../ml_parser/tags';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
||||||
import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry';
|
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,
|
parentBindingScope: BindingScope, private level = 0, private contextName: string|null,
|
||||||
private templateName: string|null, private viewQueries: R3QueryMetadata[],
|
private templateName: string|null, private viewQueries: R3QueryMetadata[],
|
||||||
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
|
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 =
|
this._bindingScope =
|
||||||
parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => {
|
parentBindingScope.nestedScope((lhsVar: o.ReadVarExpr, expression: o.Expression) => {
|
||||||
this._bindingCode.push(
|
this._bindingCode.push(
|
||||||
|
@ -89,6 +91,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
buildTemplateFunction(
|
buildTemplateFunction(
|
||||||
nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false,
|
nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false,
|
||||||
ngContentSelectors: string[] = []): o.FunctionExpr {
|
ngContentSelectors: string[] = []): o.FunctionExpr {
|
||||||
|
if (this._namespace !== R3.namespaceHTML) {
|
||||||
|
this.instruction(this._creationCode, null, this._namespace);
|
||||||
|
}
|
||||||
|
|
||||||
// Create variable bindings
|
// Create variable bindings
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
const variableName = variable.name;
|
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);
|
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) {
|
visitElement(element: t.Element) {
|
||||||
const elementIndex = this.allocateDataSlot();
|
const elementIndex = this.allocateDataSlot();
|
||||||
const referenceDataSlots = new Map<string, number>();
|
const referenceDataSlots = new Map<string, number>();
|
||||||
|
@ -229,6 +252,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
const attrI18nMetas: {[name: string]: string} = {};
|
const attrI18nMetas: {[name: string]: string} = {};
|
||||||
let i18nMeta: string = '';
|
let i18nMeta: string = '';
|
||||||
|
|
||||||
|
const [namespaceKey, elementName] = splitNsName(element.name);
|
||||||
|
|
||||||
// Elements inside i18n sections are replaced with placeholders
|
// Elements inside i18n sections are replaced with placeholders
|
||||||
// TODO(vicb): nested elements are a WIP in this phase
|
// TODO(vicb): nested elements are a WIP in this phase
|
||||||
if (this._inI18nSection) {
|
if (this._inI18nSection) {
|
||||||
|
@ -269,7 +294,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
// Element creation mode
|
// Element creation mode
|
||||||
const parameters: o.Expression[] = [
|
const parameters: o.Expression[] = [
|
||||||
o.literal(elementIndex),
|
o.literal(elementIndex),
|
||||||
o.literal(element.name),
|
o.literal(elementName),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Add the attributes
|
// Add the attributes
|
||||||
|
@ -314,6 +339,16 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
if (i18nMessages.length > 0) {
|
if (i18nMessages.length > 0) {
|
||||||
this._creationCode.push(...i18nMessages);
|
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.instruction(
|
||||||
this._creationCode, element.sourceSpan, R3.createElement, ...trimTrailingNulls(parameters));
|
this._creationCode, element.sourceSpan, R3.createElement, ...trimTrailingNulls(parameters));
|
||||||
|
|
||||||
|
@ -433,7 +468,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
// Create the template function
|
// Create the template function
|
||||||
const templateVisitor = new TemplateDefinitionBuilder(
|
const templateVisitor = new TemplateDefinitionBuilder(
|
||||||
this.constantPool, templateContext, this._bindingScope, this.level + 1, contextName,
|
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 =
|
const templateFunctionExpr =
|
||||||
templateVisitor.buildTemplateFunction(template.children, template.variables);
|
templateVisitor.buildTemplateFunction(template.children, template.variables);
|
||||||
this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this._postfixCode.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||||
|
|
|
@ -21,6 +21,105 @@ describe('compiler compliance', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('elements', () => {
|
describe('elements', () => {
|
||||||
|
it('should handle SVG', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<div class="my-app" title="Hello"><svg><circle cx="20" cy="30" r="50"/></svg><p>test</p></div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The factory should look like this:
|
||||||
|
const factory = 'factory: function MyComponent_Factory() { return new MyComponent(); }';
|
||||||
|
|
||||||
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
|
const template = `
|
||||||
|
const $c1$ = ['class', 'my-app', 'title', 'Hello'];
|
||||||
|
const $c2$ = ['cx', '20', 'cy', '30', 'r', '50'];
|
||||||
|
…
|
||||||
|
template: function MyComponent_Template(rf: IDENT, ctx: IDENT) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵE(0, 'div', $c1$);
|
||||||
|
$r3$.ɵNS();
|
||||||
|
$r3$.ɵE(1, 'svg');
|
||||||
|
$r3$.ɵE(2, 'circle', $c2$);
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵNH();
|
||||||
|
$r3$.ɵE(3, 'p');
|
||||||
|
$r3$.ɵT(4, 'test');
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, factory, 'Incorrect factory');
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle MathML', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<div class="my-app" title="Hello"><math><infinity/></math><p>test</p></div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The factory should look like this:
|
||||||
|
const factory = 'factory: function MyComponent_Factory() { return new MyComponent(); }';
|
||||||
|
|
||||||
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
|
const template = `
|
||||||
|
const $c1$ = ['class', 'my-app', 'title', 'Hello'];
|
||||||
|
…
|
||||||
|
template: function MyComponent_Template(rf: IDENT, ctx: IDENT) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵE(0, 'div', $c1$);
|
||||||
|
$r3$.ɵNM();
|
||||||
|
$r3$.ɵE(1, 'math');
|
||||||
|
$r3$.ɵE(2, 'infinity');
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵNH();
|
||||||
|
$r3$.ɵE(3, 'p');
|
||||||
|
$r3$.ɵT(4, 'test');
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, factory, 'Incorrect factory');
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
it('should translate DOM structure', () => {
|
it('should translate DOM structure', () => {
|
||||||
const files = {
|
const files = {
|
||||||
app: {
|
app: {
|
||||||
|
@ -1164,6 +1263,80 @@ describe('compiler compliance', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
it('should support embedded views in the SVG namespace', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
...shared,
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {ForOfDirective} from './shared/for_of';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`<svg><g *for="let item of items"><circle></circle></g></svg>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
items = [{ data: 42 }, { data: 42 }];
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [MyComponent, ForOfDirective]
|
||||||
|
})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(benlesh): Enforce this when the directives are specified
|
||||||
|
const ForDirectiveDefinition = `
|
||||||
|
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||||
|
type: ForOfDirective,
|
||||||
|
selectors: [['', 'forOf', '']],
|
||||||
|
factory: function ForOfDirective_Factory() {
|
||||||
|
return new ForOfDirective($r3$.ɵinjectViewContainerRef(), $r3$.ɵinjectTemplateRef());
|
||||||
|
},
|
||||||
|
features: [$r3$.ɵNgOnChangesFeature(NgForOf)],
|
||||||
|
inputs: {forOf: 'forOf'}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MyComponentDefinition = `
|
||||||
|
const $_c0$ = ['for','','forOf',''];
|
||||||
|
…
|
||||||
|
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: MyComponent,
|
||||||
|
selectors: [['my-component']],
|
||||||
|
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||||
|
template: function MyComponent_Template(rf:IDENT,ctx:IDENT){
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵNS();
|
||||||
|
$r3$.ɵE(0,'svg');
|
||||||
|
$r3$.ɵC(1,MyComponent__svg_g_Template_1,null,$_c0$);
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) { $r3$.ɵp(1,'forOf',$r3$.ɵb(ctx.items)); }
|
||||||
|
function MyComponent__svg_g_Template_1(rf:IDENT,ctx0:IDENT) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵNS();
|
||||||
|
$r3$.ɵE(0,'g');
|
||||||
|
$r3$.ɵE(1,'circle');
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: [ForOfDirective]
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const source = result.source;
|
||||||
|
|
||||||
|
// TODO(benlesh): Enforce this when the directives are specified
|
||||||
|
// expectEmit(source, ForDirectiveDefinition, 'Invalid directive definition');
|
||||||
|
expectEmit(source, MyComponentDefinition, 'Invalid component definition');
|
||||||
|
});
|
||||||
|
|
||||||
it('should support a let variable and reference', () => {
|
it('should support a let variable and reference', () => {
|
||||||
const files = {
|
const files = {
|
||||||
app: {
|
app: {
|
||||||
|
|
|
@ -30,6 +30,9 @@ export {
|
||||||
NC as ɵNC,
|
NC as ɵNC,
|
||||||
C as ɵC,
|
C as ɵC,
|
||||||
E as ɵE,
|
E as ɵE,
|
||||||
|
NH as ɵNH,
|
||||||
|
NM as ɵNM,
|
||||||
|
NS as ɵNS,
|
||||||
L as ɵL,
|
L as ɵL,
|
||||||
T as ɵT,
|
T as ɵT,
|
||||||
V as ɵV,
|
V as ɵV,
|
||||||
|
|
|
@ -150,7 +150,7 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S
|
||||||
| `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ |
|
| `{{ ['literal', exp ] }}` | ✅ | ✅ | ✅ |
|
||||||
| `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ |
|
| `{{ { a: 'literal', b: exp } }}` | ✅ | ✅ | ✅ |
|
||||||
| `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ |
|
| `{{ exp \| pipe: arg }}` | ✅ | ✅ | ✅ |
|
||||||
| `<svg:g svg:p>` | ❌ | ❌ | ❌ |
|
| `<svg:g svg:p>` | ✅ | ✅ | ✅ |
|
||||||
| `<img src=[userData]>` sanitization | ❌ | ❌ | ❌ |
|
| `<img src=[userData]>` sanitization | ❌ | ❌ | ❌ |
|
||||||
| `<div (nocd.click)>` | ❌ | ❌ | ❌ |
|
| `<div (nocd.click)>` | ❌ | ❌ | ❌ |
|
||||||
| `<div (bubble.click)>` | ❌ | ❌ | ❌ |
|
| `<div (bubble.click)>` | ❌ | ❌ | ❌ |
|
||||||
|
|
|
@ -58,6 +58,10 @@ export {
|
||||||
load as ld,
|
load as ld,
|
||||||
loadDirective as d,
|
loadDirective as d,
|
||||||
|
|
||||||
|
namespaceHTML as NH,
|
||||||
|
namespaceMathML as NM,
|
||||||
|
namespaceSVG as NS,
|
||||||
|
|
||||||
projection as P,
|
projection as P,
|
||||||
projectionDef as pD,
|
projectionDef as pD,
|
||||||
|
|
||||||
|
|
|
@ -495,6 +495,7 @@ export function renderEmbeddedTemplate<T>(
|
||||||
rf = RenderFlags.Create;
|
rf = RenderFlags.Create;
|
||||||
}
|
}
|
||||||
oldView = enterView(viewNode.data, viewNode);
|
oldView = enterView(viewNode.data, viewNode);
|
||||||
|
namespaceHTML();
|
||||||
tView.template !(rf, context);
|
tView.template !(rf, context);
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
refreshView();
|
refreshView();
|
||||||
|
@ -520,6 +521,7 @@ export function renderComponentOrTemplate<T>(
|
||||||
rendererFactory.begin();
|
rendererFactory.begin();
|
||||||
}
|
}
|
||||||
if (template) {
|
if (template) {
|
||||||
|
namespaceHTML();
|
||||||
template(getRenderFlags(hostView), componentOrContext !);
|
template(getRenderFlags(hostView), componentOrContext !);
|
||||||
refreshView();
|
refreshView();
|
||||||
} else {
|
} else {
|
||||||
|
@ -552,6 +554,24 @@ function getRenderFlags(view: LView): RenderFlags {
|
||||||
RenderFlags.Update;
|
RenderFlags.Update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
//// Namespace
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
let _currentNamespace: string|null = null;
|
||||||
|
|
||||||
|
export function namespaceSVG() {
|
||||||
|
_currentNamespace = 'http://www.w3.org/2000/svg/';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function namespaceMathML() {
|
||||||
|
_currentNamespace = 'http://www.w3.org/1998/MathML/';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function namespaceHTML() {
|
||||||
|
_currentNamespace = null;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
//// Element
|
//// Element
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -575,7 +595,19 @@ export function elementStart(
|
||||||
assertEqual(currentView.bindingIndex, -1, 'elements should be created before any bindings');
|
assertEqual(currentView.bindingIndex, -1, 'elements should be created before any bindings');
|
||||||
|
|
||||||
ngDevMode && ngDevMode.rendererCreateElement++;
|
ngDevMode && ngDevMode.rendererCreateElement++;
|
||||||
const native: RElement = renderer.createElement(name);
|
|
||||||
|
let native: RElement;
|
||||||
|
|
||||||
|
if (isProceduralRenderer(renderer)) {
|
||||||
|
native = renderer.createElement(name, _currentNamespace);
|
||||||
|
} else {
|
||||||
|
if (_currentNamespace === null) {
|
||||||
|
native = renderer.createElement(name);
|
||||||
|
} else {
|
||||||
|
native = renderer.createElementNS(_currentNamespace, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngDevMode && assertDataInRange(index - 1);
|
ngDevMode && assertDataInRange(index - 1);
|
||||||
|
|
||||||
const node: LElementNode =
|
const node: LElementNode =
|
||||||
|
@ -2130,6 +2162,7 @@ export function detectChangesInternal<T>(hostView: LView, hostNode: LElementNode
|
||||||
const template = hostView.tView.template !;
|
const template = hostView.tView.template !;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
namespaceHTML();
|
||||||
template(getRenderFlags(hostView), component);
|
template(getRenderFlags(hostView), component);
|
||||||
refreshView();
|
refreshView();
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -36,6 +36,7 @@ export type Renderer3 = ObjectOrientedRenderer3 | ProceduralRenderer3;
|
||||||
* */
|
* */
|
||||||
export interface ObjectOrientedRenderer3 {
|
export interface ObjectOrientedRenderer3 {
|
||||||
createElement(tagName: string): RElement;
|
createElement(tagName: string): RElement;
|
||||||
|
createElementNS(namespace: string, tagName: string): RElement;
|
||||||
createTextNode(data: string): RText;
|
createTextNode(data: string): RText;
|
||||||
|
|
||||||
querySelector(selectors: string): RElement|null;
|
querySelector(selectors: string): RElement|null;
|
||||||
|
|
|
@ -36,6 +36,9 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||||
'ɵcR': r3.cR,
|
'ɵcR': r3.cR,
|
||||||
'ɵcr': r3.cr,
|
'ɵcr': r3.cr,
|
||||||
'ɵd': r3.d,
|
'ɵd': r3.d,
|
||||||
|
'ɵNH': r3.NH,
|
||||||
|
'ɵNM': r3.NM,
|
||||||
|
'ɵNS': r3.NS,
|
||||||
'ɵE': r3.E,
|
'ɵE': r3.E,
|
||||||
'ɵe': r3.e,
|
'ɵe': r3.e,
|
||||||
'ɵf0': r3.f0,
|
'ɵf0': r3.f0,
|
||||||
|
|
|
@ -176,6 +176,9 @@
|
||||||
{
|
{
|
||||||
"name": "_currentInjector"
|
"name": "_currentInjector"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_currentNamespace"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_devMode"
|
"name": "_devMode"
|
||||||
},
|
},
|
||||||
|
@ -545,6 +548,9 @@
|
||||||
{
|
{
|
||||||
"name": "markViewDirty"
|
"name": "markViewDirty"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "namespaceHTML"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "notImplemented"
|
"name": "notImplemented"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue