feat(compiler): implement style encapsulation for new view engine (#14518)

Included refactoring:
- splits the `RendererV2` into a `RendererFactoryV2` and a `RendererV2`
- makes the `DebugRendererV2` a private class in `@angular/core`
- remove `setBindingDebugInfo` from `RendererV2`, but rename `RendererV2.setText` to 
  `RendererV2.setValue` and allow it on comments and text nodes.

Part of #14013
This commit is contained in:
Tobias Bosch 2017-02-16 13:55:55 -08:00 committed by Igor Minar
parent ba17dcbf2b
commit 0fa3895d5b
38 changed files with 828 additions and 595 deletions

View File

@ -101,11 +101,12 @@ export class AotCompiler {
});
// compile components
const compViewVars = this._compileComponent(
compMeta, ngModule, ngModule.transitiveModule.directives,
stylesCompileResults.componentStylesheet, fileSuffix, statements);
exportedVars.push(
this._compileComponentFactory(compMeta, ngModule, fileSuffix, statements),
this._compileComponent(
compMeta, ngModule, ngModule.transitiveModule.directives,
stylesCompileResults.componentStylesheet, fileSuffix, statements));
compViewVars.viewClassVar, compViewVars.compRenderTypeVar);
});
if (statements.length > 0) {
const srcModule = this._codegenSourceModule(
@ -175,8 +176,10 @@ export class AotCompiler {
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
const hostMeta = createHostComponentMeta(
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const hostViewFactoryVar =
this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements)
.viewClassVar;
const compFactoryVar = componentFactoryName(compMeta.type.reference);
targetStatements.push(
o.variable(compFactoryVar)
@ -198,7 +201,8 @@ export class AotCompiler {
private _compileComponent(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata,
directiveIdentifiers: CompileIdentifierMetadata[], componentStyles: CompiledStylesheet,
fileSuffix: string, targetStatements: o.Statement[]): string {
fileSuffix: string,
targetStatements: o.Statement[]): {viewClassVar: string, compRenderTypeVar: string} {
const parsedAnimations = this._animationParser.parseComponent(compMeta);
const directives =
directiveIdentifiers.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
@ -219,7 +223,10 @@ export class AotCompiler {
}
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(...viewResult.statements);
return viewResult.viewClassVar;
return {
viewClassVar: viewResult.viewClassVar,
compRenderTypeVar: viewResult.componentRenderTypeVar
};
}
private _codgenStyles(

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectionStrategy, ComponentFactory, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {ChangeDetectionStrategy, ComponentFactory, ComponentRenderTypeV2, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection';
@ -15,6 +15,7 @@ import {LifecycleHooks, reflector} from './private_import_core';
import {CssSelector} from './selector';
import {splitAtColon} from './util';
// group 0: "[prop] or (event) or @trigger"
// group 1: "prop" from "[prop]"
// group 2: "event" from "(event)"
@ -116,6 +117,10 @@ export function viewClassName(compType: any, embeddedTemplateIndex: number): str
return `View_${identifierName({reference: compType})}_${embeddedTemplateIndex}`;
}
export function componentRenderTypeName(compType: any): string {
return `RenderType_${identifierName({reference: compType})}`;
}
export function hostViewClassName(compType: any): string {
return `HostView_${identifierName({reference: compType})}`;
}
@ -310,6 +315,7 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
template: CompileTemplateSummary;
wrapperType: StaticSymbol|ProxyClass;
componentViewType: StaticSymbol|ProxyClass;
componentRenderType: StaticSymbol|ComponentRenderTypeV2;
componentFactory: StaticSymbol|ComponentFactory<any>;
}
@ -320,7 +326,7 @@ export class CompileDirectiveMetadata {
static create(
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType,
componentViewType, componentFactory}: {
componentViewType, componentRenderType, componentFactory}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
@ -338,6 +344,7 @@ export class CompileDirectiveMetadata {
template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentRenderType?: StaticSymbol|ComponentRenderTypeV2,
componentFactory?: StaticSymbol|ComponentFactory<any>,
} = {}): CompileDirectiveMetadata {
const hostListeners: {[key: string]: string} = {};
@ -392,6 +399,7 @@ export class CompileDirectiveMetadata {
template,
wrapperType,
componentViewType,
componentRenderType,
componentFactory,
});
}
@ -416,12 +424,14 @@ export class CompileDirectiveMetadata {
wrapperType: StaticSymbol|ProxyClass;
componentViewType: StaticSymbol|ProxyClass;
componentRenderType: StaticSymbol|ComponentRenderTypeV2;
componentFactory: StaticSymbol|ComponentFactory<any>;
constructor({isHost, type, isComponent, selector, exportAs,
changeDetection, inputs, outputs, hostListeners, hostProperties,
hostAttributes, providers, viewProviders, queries, viewQueries,
entryComponents, template, wrapperType, componentViewType, componentFactory}: {
entryComponents, template, wrapperType, componentViewType, componentRenderType,
componentFactory}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
@ -441,6 +451,7 @@ export class CompileDirectiveMetadata {
template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentRenderType?: StaticSymbol|ComponentRenderTypeV2,
componentFactory?: StaticSymbol|ComponentFactory<any>,
} = {}) {
this.isHost = !!isHost;
@ -463,6 +474,7 @@ export class CompileDirectiveMetadata {
this.wrapperType = wrapperType;
this.componentViewType = componentViewType;
this.componentRenderType = componentRenderType;
this.componentFactory = componentFactory;
}
@ -487,6 +499,7 @@ export class CompileDirectiveMetadata {
template: this.template && this.template.toSummary(),
wrapperType: this.wrapperType,
componentViewType: this.componentViewType,
componentRenderType: this.componentRenderType,
componentFactory: this.componentFactory
};
}
@ -521,7 +534,9 @@ export function createHostComponentMeta(
viewProviders: [],
queries: [],
viewQueries: [],
componentViewType: hostViewType
componentViewType: hostViewType,
componentRenderType:
{id: '__Host__', encapsulation: ViewEncapsulation.None, styles: [], data: {}}
});
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵAnimationGroupPlayer, ɵAnimationKeyframe, ɵAnimationSequencePlayer, ɵAnimationStyles, ɵAnimationTransition, ɵAppView, ɵChangeDetectorStatus, ɵCodegenComponentFactoryResolver, ɵComponentRef_, ɵDebugAppView, ɵDebugContext, ɵNgModuleInjector, ɵNoOpAnimationPlayer, ɵStaticNodeDebugInfo, ɵTemplateRef_, ɵValueUnwrapper, ɵViewContainer, ɵViewType, ɵbalanceAnimationKeyframes, ɵclearStyles, ɵcollectAndResolveStyles, ɵdevModeEqual, ɵprepareFinalAnimationStyles, ɵreflector, ɵregisterModuleFactory, ɵrenderStyles, ɵviewEngine, ɵview_utils} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ComponentRenderTypeV2, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation, ɵAnimationGroupPlayer, ɵAnimationKeyframe, ɵAnimationSequencePlayer, ɵAnimationStyles, ɵAnimationTransition, ɵAppView, ɵChangeDetectorStatus, ɵCodegenComponentFactoryResolver, ɵComponentRef_, ɵDebugAppView, ɵDebugContext, ɵNgModuleInjector, ɵNoOpAnimationPlayer, ɵStaticNodeDebugInfo, ɵTemplateRef_, ɵValueUnwrapper, ɵViewContainer, ɵViewType, ɵbalanceAnimationKeyframes, ɵclearStyles, ɵcollectAndResolveStyles, ɵdevModeEqual, ɵprepareFinalAnimationStyles, ɵreflector, ɵregisterModuleFactory, ɵrenderStyles, ɵviewEngine, ɵview_utils} from '@angular/core';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
@ -380,6 +380,12 @@ export class Identifiers {
member: 'unwrapValue',
runtime: ɵviewEngine.unwrapValue
};
static createComponentRenderTypeV2: IdentifierSpec = {
name: 'ɵviewEngine',
moduleUrl: CORE,
member: 'createComponentRenderTypeV2',
runtime: ɵviewEngine.createComponentRenderTypeV2
};
}
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Compiler, ComponentFactory, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {Compiler, ComponentFactory, ComponentRenderTypeV2, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
@ -133,11 +133,11 @@ export class JitCompiler implements Compiler {
const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders);
if (!this._compilerConfig.useJit) {
ngModuleFactory =
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
interpretStatements(compileResult.statements, [compileResult.ngModuleFactoryVar])[0];
} else {
ngModuleFactory = jitStatements(
`/${identifierName(moduleMeta.type)}/module.ngfactory.js`, compileResult.statements,
compileResult.ngModuleFactoryVar);
[compileResult.ngModuleFactoryVar])[0];
}
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
}
@ -252,11 +252,12 @@ export class JitCompiler implements Compiler {
const statements = compileResult.statements;
let directiveWrapperClass: any;
if (!this._compilerConfig.useJit) {
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
directiveWrapperClass =
interpretStatements(statements, [compileResult.dirWrapperClassVar])[0];
} else {
directiveWrapperClass = jitStatements(
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
statements, compileResult.dirWrapperClassVar);
statements, [compileResult.dirWrapperClassVar])[0];
}
(<ProxyClass>dirMeta.wrapperType).setDelegate(directiveWrapperClass);
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
@ -290,14 +291,18 @@ export class JitCompiler implements Compiler {
.concat(...compiledAnimations.map(ca => ca.statements))
.concat(compileResult.statements);
let viewClass: any;
let componentRenderType: any;
if (!this._compilerConfig.useJit) {
viewClass = interpretStatements(statements, compileResult.viewClassVar);
[viewClass, componentRenderType] = interpretStatements(
statements, [compileResult.viewClassVar, compileResult.componentRenderTypeVar]);
} else {
viewClass = jitStatements(
`/${identifierName(template.ngModule.type)}/${identifierName(template.compType)}/${template.isHost?'host':'component'}.ngfactory.js`,
statements, compileResult.viewClassVar);
const sourceUrl =
`/${identifierName(template.ngModule.type)}/${identifierName(template.compType)}/${template.isHost?'host':'component'}.ngfactory.js`;
[viewClass, componentRenderType] = jitStatements(
sourceUrl, statements,
[compileResult.viewClassVar, compileResult.componentRenderTypeVar]);
}
template.compiled(viewClass);
template.compiled(viewClass, componentRenderType);
}
private _resolveStylesCompileResult(
@ -315,10 +320,10 @@ export class JitCompiler implements Compiler {
externalStylesheetsByModuleUrl: Map<string, CompiledStylesheet>): string[] {
this._resolveStylesCompileResult(result, externalStylesheetsByModuleUrl);
if (!this._compilerConfig.useJit) {
return interpretStatements(result.statements, result.stylesVar);
return interpretStatements(result.statements, [result.stylesVar])[0];
} else {
return jitStatements(
`/${result.meta.moduleUrl}.ngstyle.js`, result.statements, result.stylesVar);
`/${result.meta.moduleUrl}.ngstyle.js`, result.statements, [result.stylesVar])[0];
}
}
}
@ -332,9 +337,12 @@ class CompiledTemplate {
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
public directives: CompileIdentifierMetadata[]) {}
compiled(viewClass: Function) {
compiled(viewClass: Function, componentRenderType: any) {
this._viewClass = viewClass;
(<ProxyClass>this.compMeta.componentViewType).setDelegate(viewClass);
for (let prop in componentRenderType) {
(<any>this.compMeta.componentRenderType)[prop] = componentRenderType[prop];
}
this.isCompiled = true;
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, ComponentRenderTypeV2, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
@ -131,6 +131,17 @@ export class CompileMetadataResolver {
}
}
private getComponentRenderType(dirType: any): StaticSymbol|ComponentRenderTypeV2 {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(
ngfactoryFilePath(dirType.filePath), cpl.componentRenderTypeName(dirType));
} else {
// returning an object as proxy,
// that we fill later during runtime compilation.
return <any>{};
}
}
private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory<any> {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(
@ -235,6 +246,7 @@ export class CompileMetadataResolver {
entryComponents: metadata.entryComponents,
wrapperType: metadata.wrapperType,
componentViewType: metadata.componentViewType,
componentRenderType: metadata.componentRenderType,
componentFactory: metadata.componentFactory,
template: templateMetadata
});
@ -372,6 +384,8 @@ export class CompileMetadataResolver {
wrapperType: this.getDirectiveWrapperClass(directiveType),
componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
undefined,
componentRenderType:
nonNormalizedTemplateMetadata ? this.getComponentRenderType(directiveType) : undefined,
componentFactory: nonNormalizedTemplateMetadata ?
this.getComponentFactory(selector, directiveType) :
undefined

View File

@ -12,8 +12,9 @@ import {isPresent} from '../facade/lang';
import * as o from './output_ast';
import {debugOutputAstAsTypeScript} from './ts_emitter';
export function interpretStatements(statements: o.Statement[], resultVar: string): any {
const stmtsWithReturn = statements.concat([new o.ReturnStatement(o.variable(resultVar))]);
export function interpretStatements(statements: o.Statement[], resultVars: string[]): any[] {
const stmtsWithReturn = statements.concat(
[new o.ReturnStatement(o.literalArr(resultVars.map(resultVar => o.variable(resultVar))))]);
const ctx = new _ExecutionContext(null, null, null, new Map<string, any>());
const visitor = new StatementInterpreter();
const result = visitor.visitAllStatements(stmtsWithReturn, ctx);

View File

@ -13,9 +13,9 @@ import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
import * as o from './output_ast';
function evalExpression(
sourceUrl: string, expr: string, ctx: EmitterVisitorContext, vars: {[key: string]: any}): any {
sourceUrl: string, ctx: EmitterVisitorContext, vars: {[key: string]: any}): any {
const fnBody =
`${ctx.toSource()}\nreturn ${expr}\n//# sourceURL=${sourceUrl}\n${ctx.toSourceMapGenerator().toJsComment()}`;
`${ctx.toSource()}\n//# sourceURL=${sourceUrl}\n${ctx.toSourceMapGenerator().toJsComment()}`;
const fnArgNames: string[] = [];
const fnArgValues: any[] = [];
for (const argName in vars) {
@ -26,11 +26,13 @@ function evalExpression(
}
export function jitStatements(
sourceUrl: string, statements: o.Statement[], resultVar: string): any {
sourceUrl: string, statements: o.Statement[], resultVars: string[]): any[] {
const converter = new JitEmitterVisitor();
const ctx = EmitterVisitorContext.createRoot([resultVar]);
converter.visitAllStatements(statements, ctx);
return evalExpression(sourceUrl, resultVar, ctx, converter.getArgs());
const ctx = EmitterVisitorContext.createRoot(resultVars);
const returnStmt =
new o.ReturnStatement(o.literalArr(resultVars.map(resultVar => o.variable(resultVar))));
converter.visitAllStatements(statements.concat([returnStmt]), ctx);
return evalExpression(sourceUrl, ctx, converter.getArgs());
}
class JitEmitterVisitor extends AbstractJsEmitterVisitor {

View File

@ -7,7 +7,7 @@
*/
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata';
import {CompileDirectiveMetadata, CompilePipeSummary, componentRenderTypeName, tokenName, viewClassName} from '../compile_metadata';
import {EventHandlerVars, LegacyNameResolver} from '../compiler_util/expression_converter';
import {CompilerConfig} from '../config';
import {isPresent} from '../facade/lang';
@ -70,6 +70,7 @@ export class CompileView implements LegacyNameResolver {
public pipes: CompilePipe[] = [];
public locals = new Map<string, o.Expression>();
public className: string;
public renderComponentTypeName: string;
public classType: o.Type;
public classExpr: o.ReadVarExpr;
@ -102,6 +103,7 @@ export class CompileView implements LegacyNameResolver {
this.viewType = getViewType(component, viewIndex);
this.className = viewClassName(component.type.reference, viewIndex);
this.renderComponentTypeName = componentRenderTypeName(component.type.reference);
this.classType = o.expressionType(o.variable(this.className));
this.classExpr = o.variable(this.className);
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {

View File

@ -386,7 +386,7 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
const renderCompTypeVar: o.ReadVarExpr =
o.variable(`renderType_${identifierName(view.component.type)}`); // fix highlighting: `
o.variable(view.renderComponentTypeName); // fix highlighting: `
if (view.viewIndex === 0) {
let templateUrlInfo: string;
if (view.component.template.templateUrl == identifierModuleUrl(view.component.type)) {

View File

@ -25,6 +25,7 @@ export {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDep
export class ViewCompileResult {
constructor(
public statements: o.Statement[], public viewClassVar: string,
public componentRenderTypeVar: string,
public dependencies:
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
}
@ -50,6 +51,7 @@ export class ViewCompiler {
bindView(view, template, this._schemaRegistry);
finishView(view, statements);
return new ViewCompileResult(statements, view.classExpr.name, dependencies);
return new ViewCompileResult(
statements, view.classExpr.name, view.renderComponentTypeName, dependencies);
}
}

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectionStrategy} from '@angular/core';
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, CompileTypeMetadata, identifierModuleUrl, identifierName, tokenReference} from '../compile_metadata';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, CompileTypeMetadata, componentRenderTypeName, identifierModuleUrl, identifierName, tokenReference, viewClassName} from '../compile_metadata';
import {BuiltinConverter, BuiltinConverterFactory, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
import {CompilerConfig} from '../config';
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
@ -20,7 +20,6 @@ import {convertValueToOutputAst} from '../output/value_util';
import {LifecycleHooks, viewEngine} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {ViewEncapsulationEnum} from '../view_compiler/constants';
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
const CLASS_ATTR = 'class';
@ -44,19 +43,33 @@ export class ViewCompilerNext extends ViewCompiler {
let embeddedViewCount = 0;
const staticQueryIds = findStaticQueryIds(template);
const statements: o.Statement[] = [];
const renderComponentVar = o.variable(componentRenderTypeName(component.type.reference));
statements.push(
renderComponentVar
.set(o.importExpr(createIdentifier(Identifiers.createComponentRenderTypeV2)).callFn([
new o.LiteralMapExpr([
new o.LiteralMapEntry('encapsulation', o.literal(component.template.encapsulation)),
new o.LiteralMapEntry('styles', styles),
// TODO: copy this from the @Component directive...
new o.LiteralMapEntry('data', o.literalMap([])),
])
]))
.toDeclStmt());
const viewBuilderFactory = (parent: ViewBuilder): ViewBuilder => {
const embeddedViewIndex = embeddedViewCount++;
const viewName = `view_${compName}_${embeddedViewIndex}`;
const viewName = viewClassName(component.type.reference, embeddedViewIndex);
return new ViewBuilder(parent, viewName, usedPipes, staticQueryIds, viewBuilderFactory);
};
const visitor = viewBuilderFactory(null);
visitor.visitAll([], template);
const statements: o.Statement[] = [];
statements.push(...visitor.build(component));
return new ViewCompileResult(statements, visitor.viewName, []);
return new ViewCompileResult(statements, visitor.viewName, renderComponentVar.name, []);
}
}
@ -458,9 +471,11 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
}
});
let compRenderType = o.NULL_EXPR;
let compView = o.NULL_EXPR;
if (directiveAst.directive.isComponent) {
compView = o.importExpr({reference: directiveAst.directive.componentViewType});
compRenderType = o.importExpr({reference: directiveAst.directive.componentRenderType});
}
const inputDefs = directiveAst.inputs.map((inputAst, inputIndex) => {
@ -507,7 +522,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
o.literal(childCount), providerExpr, depsExpr,
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR, compView
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR, compView, compRenderType
]);
this.nodeDefs[nodeIndex] = nodeDef;

View File

@ -93,8 +93,6 @@ export const __core_private__: {
makeDecorator: typeof decorators.makeDecorator,
DebugDomRootRenderer: typeof debug.DebugDomRootRenderer,
_DebugDomRootRenderer: debug.DebugDomRootRenderer,
DebugDomRendererV2: typeof debug.DebugDomRendererV2,
_DebugDomRendererV2: debug.DebugDomRendererV2,
Console: typeof console.Console,
_Console: console.Console,
reflector: typeof reflection.reflector,
@ -158,7 +156,6 @@ export const __core_private__: {
ReflectionCapabilities: reflection_capabilities.ReflectionCapabilities,
makeDecorator: decorators.makeDecorator,
DebugDomRootRenderer: debug.DebugDomRootRenderer,
DebugDomRendererV2: debug.DebugDomRendererV2,
Console: console.Console,
reflector: reflection.reflector,
Reflector: reflection.Reflector,

View File

@ -10,7 +10,7 @@ import {AnimationKeyframe} from '../animation/animation_keyframe';
import {AnimationPlayer} from '../animation/animation_player';
import {AnimationStyles} from '../animation/animation_styles';
import {isPresent} from '../facade/lang';
import {RenderComponentType, RenderDebugInfo, Renderer, RendererV2, RootRenderer} from '../render/api';
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from './debug_node';
@ -156,151 +156,3 @@ export class DebugDomRenderer implements Renderer {
element, startingStyles, keyframes, duration, delay, easing, previousPlayers);
}
}
export class DebugDomRendererV2 implements RendererV2 {
constructor(private _delegate: RendererV2) {}
createElement(name: string, namespace?: string, debugInfo?: any): any {
const el = this._delegate.createElement(name, namespace, debugInfo);
const debugEl = new DebugElement(el, null, debugInfo);
debugEl.name = name;
indexDebugNode(debugEl);
return el;
}
createComment(value: string, debugInfo?: any): any {
const comment = this._delegate.createComment(value, debugInfo);
const debugEl = new DebugNode(comment, null, debugInfo);
indexDebugNode(debugEl);
return comment;
}
createText(value: string, debugInfo?: any): any {
const text = this._delegate.createText(value, debugInfo);
const debugEl = new DebugNode(text, null, debugInfo);
indexDebugNode(debugEl);
return text;
}
appendChild(parent: any, newChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.addChild(debugChildEl);
}
this._delegate.appendChild(parent, newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
const debugRefEl = getDebugNode(refChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.insertBefore(debugRefEl, debugChildEl);
}
this._delegate.insertBefore(parent, newChild, refChild);
}
removeChild(parent: any, oldChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(oldChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.removeChild(debugChildEl);
}
this._delegate.removeChild(parent, oldChild);
}
selectRootElement(selectorOrNode: string|any, debugInfo?: any): any {
const el = this._delegate.selectRootElement(selectorOrNode, debugInfo);
const debugEl = new DebugElement(el, null, debugInfo);
indexDebugNode(debugEl);
return el;
}
parentNode(node: any): any { return this._delegate.parentNode(node); }
nextSibling(node: any): any { return this._delegate.nextSibling(node); }
setAttribute(el: any, name: string, value: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = value;
}
this._delegate.setAttribute(el, name, value, namespace);
}
removeAttribute(el: any, name: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = null;
}
this._delegate.removeAttribute(el, name, namespace);
}
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.removeBindingDebugInfo(el, propertyName);
}
addClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = true;
}
this._delegate.addClass(el, name);
}
removeClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = false;
}
this._delegate.removeClass(el, name);
}
setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean):
void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = value;
}
this._delegate.setStyle(el, style, value, hasVendorPrefix, hasImportant);
}
removeStyle(el: any, style: string, hasVendorPrefix: boolean): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = null;
}
this._delegate.removeStyle(el, style, hasVendorPrefix);
}
setProperty(el: any, name: string, value: any): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.properties[name] = value;
}
this._delegate.setProperty(el, name, value);
}
setText(node: any, value: string): void { this._delegate.setText(node, value); }
listen(
target: 'document'|'windows'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void {
if (typeof target !== 'string') {
const debugEl = getDebugNode(target);
if (debugEl) {
debugEl.listeners.push(new EventListener(eventName, callback));
}
}
return this._delegate.listen(target, eventName, callback);
}
}

View File

@ -7,4 +7,4 @@
*/
// Public API for render
export {RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer} from './render/api';
export {ComponentRenderTypeV2, RenderComponentType, Renderer, RendererFactoryV2, RendererV2, RootRenderer} from './render/api';

View File

@ -12,13 +12,6 @@ import {AnimationStyles} from '../../src/animation/animation_styles';
import {InjectionToken, Injector} from '../di';
import {ViewEncapsulation} from '../metadata/view';
/**
* Provide a concrete implementation of {@link RendererV2}
*
* @experimental
*/
export const RENDERER_V2_DIRECT = new InjectionToken<RendererV2>('Renderer V2');
/**
* @experimental
*/
@ -98,56 +91,6 @@ export abstract class Renderer {
previousPlayers?: AnimationPlayer[]): AnimationPlayer;
}
/**
* @experimental
*/
export abstract class RendererV2 {
abstract createElement(name: string, namespace?: string, debugInfo?: RenderDebugContext): any;
abstract createComment(value: string, debugInfo?: RenderDebugContext): any;
abstract createText(value: string, debugInfo?: RenderDebugContext): any;
abstract appendChild(parent: any, newChild: any): void;
abstract insertBefore(parent: any, newChild: any, refChild: any): void;
abstract removeChild(parent: any, oldChild: any): void;
abstract selectRootElement(selectorOrNode: string|any, debugInfo?: RenderDebugContext): any;
/**
* Attention: On WebWorkers, this will always return a value,
* as we are asking for a result synchronously. I.e.
* the caller can't rely on checking whether this is null or not.
*/
abstract parentNode(node: any): any;
/**
* Attention: On WebWorkers, this will always return a value,
* as we are asking for a result synchronously. I.e.
* the caller can't rely on checking whether this is null or not.
*/
abstract nextSibling(node: any): any;
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void;
abstract removeAttribute(el: any, name: string, namespace?: string): void;
abstract setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void;
abstract removeBindingDebugInfo(el: any, propertyName: string): void;
abstract addClass(el: any, name: string): void;
abstract removeClass(el: any, name: string): void;
abstract setStyle(
el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): void;
abstract removeStyle(el: any, style: string, hasVendorPrefix: boolean): void;
abstract setProperty(el: any, name: string, value: any): void;
abstract setText(node: any, value: string): void;
abstract listen(
target: 'window'|'document'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void;
}
export abstract class RenderDebugContext {
abstract get injector(): Injector;
abstract get component(): any;
abstract get providerTokens(): any[];
abstract get references(): {[key: string]: any};
abstract get context(): any;
abstract get source(): string;
abstract get componentRenderElement(): any;
abstract get renderNode(): any;
}
/**
* Injectable service that provides a low-level interface for modifying the UI.
*
@ -164,3 +107,64 @@ export abstract class RenderDebugContext {
export abstract class RootRenderer {
abstract renderComponent(componentType: RenderComponentType): Renderer;
}
/**
* @experimental
*/
export interface ComponentRenderTypeV2 {
id: string;
encapsulation: ViewEncapsulation;
styles: (string|any[])[];
data: {[kind: string]: any[]};
}
/**
* @experimental
*/
export abstract class RendererFactoryV2 {
abstract createRenderer(hostElement: any, type: ComponentRenderTypeV2): RendererV2;
}
/**
* @experimental
*/
export abstract class RendererV2 {
abstract destroy(): void;
abstract createElement(name: string, namespace?: string): any;
abstract createComment(value: string): any;
abstract createText(value: string): any;
/**
* This property is allowed to be null / undefined,
* in which case the view engine won't call it.
* This is used as a performance optimization for production mode.
*/
destroyNode: (node: any) => void | null;
abstract appendChild(parent: any, newChild: any): void;
abstract insertBefore(parent: any, newChild: any, refChild: any): void;
abstract removeChild(parent: any, oldChild: any): void;
abstract selectRootElement(selectorOrNode: string|any): any;
/**
* Attention: On WebWorkers, this will always return a value,
* as we are asking for a result synchronously. I.e.
* the caller can't rely on checking whether this is null or not.
*/
abstract parentNode(node: any): any;
/**
* Attention: On WebWorkers, this will always return a value,
* as we are asking for a result synchronously. I.e.
* the caller can't rely on checking whether this is null or not.
*/
abstract nextSibling(node: any): any;
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void;
abstract removeAttribute(el: any, name: string, namespace?: string): void;
abstract addClass(el: any, name: string): void;
abstract removeClass(el: any, name: string): void;
abstract setStyle(
el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): void;
abstract removeStyle(el: any, style: string, hasVendorPrefix: boolean): void;
abstract setProperty(el: any, name: string, value: any): void;
abstract setValue(node: any, value: string): void;
abstract listen(
target: 'window'|'document'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void;
}

View File

@ -132,7 +132,7 @@ export function elementDef(
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
const elDef = def.element;
const rootSelectorOrNode = view.root.selectorOrNode;
const renderer = view.root.renderer;
const renderer = view.renderer;
let el: any;
if (view.parent || !rootSelectorOrNode) {
if (elDef.name) {
@ -240,7 +240,7 @@ function setElementAttribute(
const securityContext = binding.securityContext;
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
renderValue = renderValue != null ? renderValue.toString() : null;
const renderer = view.root.renderer;
const renderer = view.renderer;
// TODO(vicb): move the namespace to the node definition
const nsAndName = splitNamespace(name);
if (value != null) {
@ -251,7 +251,7 @@ function setElementAttribute(
}
function setElementClass(view: ViewData, renderNode: any, name: string, value: boolean) {
const renderer = view.root.renderer;
const renderer = view.renderer;
if (value) {
renderer.addClass(renderNode, name);
} else {
@ -271,7 +271,7 @@ function setElementStyle(
} else {
renderValue = null;
}
const renderer = view.root.renderer;
const renderer = view.renderer;
if (renderValue != null) {
renderer.setStyle(renderNode, name, renderValue, false, false);
} else {
@ -283,7 +283,7 @@ function setElementProperty(
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
const securityContext = binding.securityContext;
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
view.root.renderer.setProperty(renderNode, name, renderValue);
view.renderer.setProperty(renderNode, name, renderValue);
}
const NS_PREFIX_RE = /^:([^:]+):(.+)$/;

View File

@ -14,7 +14,7 @@ export {queryDef} from './query';
export {createComponentFactory} from './refs';
export {initServicesIfNeeded} from './services';
export {textDef} from './text';
export {elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
export {createComponentRenderTypeV2, elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
export {viewDef} from './view';
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';

View File

@ -11,13 +11,15 @@ import {Injector} from '../di';
import {ElementRef} from '../linker/element_ref';
import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import * as v1renderer from '../render/api';
import {ViewEncapsulation} from '../metadata/view';
import {ComponentRenderTypeV2, RenderComponentType as RenderComponentTypeV1, Renderer as RendererV1, RendererFactoryV2, RendererV2, RootRenderer as RootRendererV1} from '../render/api';
import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs';
import {BindingDef, BindingType, DepDef, DepFlags, DirectiveOutputDef, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
const RendererV1TokenKey = tokenKey(RendererV1);
const RendererV2TokenKey = tokenKey(RendererV2);
const ElementRefTokenKey = tokenKey(ElementRef);
const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
const TemplateRefTokenKey = tokenKey(TemplateRef);
@ -29,7 +31,8 @@ const NOT_CREATED = new Object();
export function directiveDef(
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef {
outputs?: {[name: string]: string}, component?: () => ViewDefinition,
componentRenderType?: ComponentRenderTypeV2): NodeDef {
const bindings: BindingDef[] = [];
if (props) {
for (let prop in props) {
@ -50,7 +53,7 @@ export function directiveDef(
}
return _def(
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
bindings, outputDefs, component);
bindings, outputDefs, component, componentRenderType);
}
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
@ -67,8 +70,13 @@ export function _def(
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
childCount: number, providerType: ProviderType, token: any, value: any,
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
component?: () => ViewDefinition): NodeDef {
component?: () => ViewDefinition, componentRenderType?: ComponentRenderTypeV2): NodeDef {
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
// This is needed as the jit compiler always uses an empty hash as default ComponentRenderTypeV2,
// which is not filled for host views.
if (componentRenderType && componentRenderType.encapsulation == null) {
componentRenderType = null;
}
if (!outputs) {
outputs = [];
}
@ -111,7 +119,7 @@ export function _def(
type: providerType,
token,
tokenKey: tokenKey(token), value,
deps: depDefs, outputs, component
deps: depDefs, outputs, component, componentRenderType
},
text: undefined,
pureExpression: undefined,
@ -328,16 +336,19 @@ export function resolveDep(
if (elDef) {
switch (tokenKey) {
case RendererV1TokenKey: {
let compView = view;
while (compView && !isComponentView(compView)) {
compView = compView.parent;
}
const rootRenderer: v1renderer.RootRenderer =
view.root.injector.get(v1renderer.RootRenderer);
const compView = findCompView(view, elDef, allowPrivateServices);
const compDef = compView.parentNodeDef;
const rootRendererV1: RootRendererV1 = view.root.injector.get(RootRendererV1);
// Note: Don't fill in the styles as they have been installed already!
return rootRenderer.renderComponent(new v1renderer.RenderComponentType(
view.def.component.id, '', 0, view.def.component.encapsulation, [], {}));
// Note: Don't fill in the styles as they have been installed already via the RendererV2!
const compRenderType = compDef.provider.componentRenderType;
return rootRendererV1.renderComponent(new RenderComponentTypeV1(
compRenderType ? compRenderType.id : '0', '', 0,
compRenderType ? compRenderType.encapsulation : ViewEncapsulation.None, [], {}));
}
case RendererV2TokenKey: {
const compView = findCompView(view, elDef, allowPrivateServices);
return compView.renderer;
}
case ElementRefTokenKey:
return new ElementRef(asElementData(view, elDef.index).renderElement);
@ -350,15 +361,7 @@ export function resolveDep(
break;
}
case ChangeDetectorRefTokenKey: {
let cdView: ViewData;
if (allowPrivateServices) {
cdView = asProviderData(view, elDef.element.component.index).componentView;
} else {
cdView = view;
while (cdView.parent && !isComponentView(cdView)) {
cdView = cdView.parent;
}
}
let cdView = findCompView(view, elDef, allowPrivateServices);
return createChangeDetectorRef(cdView);
}
case InjectorRefTokenKey:
@ -383,6 +386,19 @@ export function resolveDep(
return startView.root.injector.get(depDef.token, notFoundValue);
}
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
let compView: ViewData;
if (allowPrivateServices) {
compView = asProviderData(view, elDef.element.component.index).componentView;
} else {
compView = view;
while (compView.parent && !isComponentView(compView)) {
compView = compView.parent;
}
}
return compView;
}
function checkAndUpdateProp(
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
changes: SimpleChanges): SimpleChanges {

View File

@ -201,8 +201,9 @@ export function createInjector(view: ViewData, elDef: NodeDef): Injector {
class Injector_ implements Injector {
constructor(private view: ViewData, private elDef: NodeDef) {}
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
const allowPrivateServices = !!this.elDef.element.component;
return Services.resolveDep(
this.view, this.elDef, true, {flags: DepFlags.None, token, tokenKey: tokenKey(token)},
notFoundValue);
this.view, this.elDef, allowPrivateServices,
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
}
}

View File

@ -7,8 +7,9 @@
*/
import {isDevMode} from '../application_ref';
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
import {Injector} from '../di';
import {RendererV2} from '../render/api';
import {ComponentRenderTypeV2, RendererFactoryV2, RendererV2} from '../render/api';
import {Sanitizer, SecurityContext} from '../security';
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
@ -87,52 +88,59 @@ function createDebugServices() {
function createProdRootView(
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
def: ViewDefinition, context?: any): ViewData {
const rendererFactory: RendererFactoryV2 = injector.get(RendererFactoryV2);
return createRootView(
createRootData(injector, projectableNodes, rootSelectorOrNode), def, context);
createRootData(injector, rendererFactory, projectableNodes, rootSelectorOrNode), def,
context);
}
function debugCreateRootView(
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: string | any,
def: ViewDefinition, context?: any): ViewData {
const root = createRootData(injector, projectableNodes, rootSelectorOrNode);
const debugRoot: RootData = {
injector: root.injector,
projectableNodes: root.projectableNodes,
selectorOrNode: root.selectorOrNode,
renderer: new DebugRenderer(root.renderer),
sanitizer: root.sanitizer
};
return callWithDebugContext('create', createRootView, null, [debugRoot, def, context]);
const rendererFactory: RendererFactoryV2 = injector.get(RendererFactoryV2);
const root = createRootData(
injector, new DebugRendererFactoryV2(rendererFactory), projectableNodes, rootSelectorOrNode);
return callWithDebugContext(DebugAction.create, createRootView, null, [root, def, context]);
}
function createRootData(
injector: Injector, projectableNodes: any[][], rootSelectorOrNode: any): RootData {
injector: Injector, rendererFactory: RendererFactoryV2, projectableNodes: any[][],
rootSelectorOrNode: any): RootData {
const sanitizer = injector.get(Sanitizer);
const renderer = injector.get(RendererV2);
const rootElement =
rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined;
return {injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer};
const renderer = rendererFactory.createRenderer(null, null);
return {
injector,
projectableNodes,
selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer
};
}
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
return callWithDebugContext('create', createEmbeddedView, null, [parent, anchorDef, context]);
return callWithDebugContext(
DebugAction.create, createEmbeddedView, null, [parent, anchorDef, context]);
}
function debugCheckAndUpdateView(view: ViewData) {
return callWithDebugContext('detectChanges', checkAndUpdateView, null, [view]);
return callWithDebugContext(DebugAction.detectChanges, checkAndUpdateView, null, [view]);
}
function debugCheckNoChangesView(view: ViewData) {
return callWithDebugContext('checkNoChanges', checkNoChangesView, null, [view]);
return callWithDebugContext(DebugAction.checkNoChanges, checkNoChangesView, null, [view]);
}
function debugDestroyView(view: ViewData) {
return callWithDebugContext('destroyView', destroyView, null, [view]);
return callWithDebugContext(DebugAction.destroy, destroyView, null, [view]);
}
enum DebugAction {
create,
detectChanges,
checkNoChanges,
destroy,
handleEvent
}
let _currentAction: string;
let _currentAction: DebugAction;
let _currentView: ViewData;
let _currentNodeIndex: number;
@ -143,16 +151,16 @@ function debugSetCurrentNode(view: ViewData, nodeIndex: number) {
function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string, event: any) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(_currentAction);
throw viewDestroyedError(DebugAction[_currentAction]);
}
debugSetCurrentNode(view, nodeIndex);
return callWithDebugContext(
'handleEvent', view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
DebugAction.handleEvent, view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
}
function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(_currentAction);
throw viewDestroyedError(DebugAction[_currentAction]);
}
debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0));
return view.def.updateDirectives(debugCheckDirectivesFn, view);
@ -167,7 +175,7 @@ function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(_currentAction);
throw viewDestroyedError(DebugAction[_currentAction]);
}
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0));
return view.def.updateRenderer(debugCheckRenderNodeFn, view);
@ -183,35 +191,41 @@ function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
function debugCheckFn(
delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType,
givenValues: any[]) {
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
const nodeDef = view.def.nodes[nodeIndex];
for (let i = 0; i < nodeDef.bindings.length; i++) {
const binding = nodeDef.bindings[i];
const value = values[i];
if ((binding.type === BindingType.ElementProperty ||
binding.type === BindingType.DirectiveProperty) &&
checkBinding(view, nodeDef, i, value)) {
if (_currentAction === DebugAction.detectChanges) {
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
const nodeDef = view.def.nodes[nodeIndex];
if (nodeDef.type === NodeType.Directive || nodeDef.type === NodeType.Element) {
const bindingValues: {[key: string]: string} = {};
for (let i = 0; i < nodeDef.bindings.length; i++) {
const binding = nodeDef.bindings[i];
const value = values[i];
if ((binding.type === BindingType.ElementProperty ||
binding.type === BindingType.DirectiveProperty) &&
checkBinding(view, nodeDef, i, value)) {
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
normalizeDebugBindingValue(value);
}
}
const elDef = nodeDef.type === NodeType.Directive ? nodeDef.parent : nodeDef;
setBindingDebugInfo(
view.root.renderer, asElementData(view, elDef.index).renderElement,
binding.nonMinifiedName, value);
const el = asElementData(view, elDef.index).renderElement;
if (!elDef.element.name) {
// a comment.
view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`);
} else {
// a regular element.
for (let attr in bindingValues) {
view.renderer.setAttribute(el, attr, bindingValues[attr]);
}
}
}
}
return (<any>delegate)(view, nodeIndex, argStyle, ...givenValues);
};
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
if (value) {
try {
renderer.setBindingDebugInfo(renderNode, renderName, value.toString());
} catch (e) {
renderer.setBindingDebugInfo(
renderNode, renderName, '[ERROR] Exception while trying to serialize the value');
}
} else {
renderer.removeBindingDebugInfo(renderNode, renderName);
}
function normalizeDebugBindingName(name: string) {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
name = camelCaseToDashCase(name.replace(/\$/g, '_'));
return `ng-reflect-${name}`;
}
const CAMEL_CASE_REGEXP = /([A-Z])/g;
@ -220,6 +234,15 @@ function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}
function normalizeDebugBindingValue(value: any): string {
try {
// Limit the size of the value as otherwise the DOM just gets polluted.
return value ? value.toString().slice(0, 20) : value;
} catch (e) {
return '[ERROR] Exception while trying to serialize the value';
}
}
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number {
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
@ -241,64 +264,6 @@ function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number {
return undefined;
}
class DebugRenderer implements RendererV2 {
constructor(private _delegate: RendererV2) {}
createElement(name: string, namespace?: string): any {
return this._delegate.createElement(name, namespace, getCurrentDebugContext());
}
createComment(value: string): any {
return this._delegate.createComment(value, getCurrentDebugContext());
}
createText(value: string): any {
return this._delegate.createText(value, getCurrentDebugContext());
}
appendChild(parent: any, newChild: any): void {
return this._delegate.appendChild(parent, newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
return this._delegate.insertBefore(parent, newChild, refChild);
}
removeChild(parent: any, oldChild: any): void {
return this._delegate.removeChild(parent, oldChild);
}
selectRootElement(selectorOrNode: string|any): any {
return this._delegate.selectRootElement(selectorOrNode, getCurrentDebugContext());
}
parentNode(node: any): any { return this._delegate.parentNode(node); }
nextSibling(node: any): any { return this._delegate.nextSibling(node); }
setAttribute(el: any, name: string, value: string, namespace?: string): void {
return this._delegate.setAttribute(el, name, value, namespace);
}
removeAttribute(el: any, name: string, namespace?: string): void {
return this._delegate.removeAttribute(el, name, namespace);
}
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.removeBindingDebugInfo(el, propertyName);
}
addClass(el: any, name: string): void { return this._delegate.addClass(el, name); }
removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); }
setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean):
void {
return this._delegate.setStyle(el, style, value, hasVendorPrefix, hasImportant);
}
removeStyle(el: any, style: string, hasVendorPrefix: boolean): void {
return this._delegate.removeStyle(el, style, hasVendorPrefix);
}
setProperty(el: any, name: string, value: any): void {
return this._delegate.setProperty(el, name, value);
}
setText(node: any, value: string): void { return this._delegate.setText(node, value); }
listen(
target: 'window'|'document'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void {
return this._delegate.listen(target, eventName, callback);
}
}
class DebugContext_ implements DebugContext {
private nodeDef: NodeDef;
private elView: ViewData;
@ -401,7 +366,7 @@ function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key:
}
}
function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any[]) {
const oldAction = _currentAction;
const oldView = _currentView;
const oldNodeIndex = _currentNodeIndex;
@ -421,6 +386,163 @@ function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
}
}
function getCurrentDebugContext() {
export function getCurrentDebugContext(): DebugContext {
return new DebugContext_(_currentView, _currentNodeIndex);
}
}
class DebugRendererFactoryV2 implements RendererFactoryV2 {
constructor(private delegate: RendererFactoryV2) {}
createRenderer(element: any, renderData: ComponentRenderTypeV2): RendererV2 {
return new DebugRendererV2(this.delegate.createRenderer(element, renderData));
}
}
class DebugRendererV2 implements RendererV2 {
constructor(private delegate: RendererV2) {}
destroyNode(node: any) {
removeDebugNodeFromIndex(getDebugNode(node));
if (this.delegate.destroyNode) {
this.delegate.destroyNode(node);
}
}
destroy() { this.delegate.destroy(); }
createElement(name: string, namespace?: string): any {
const el = this.delegate.createElement(name, namespace);
const debugEl = new DebugElement(el, null, getCurrentDebugContext());
debugEl.name = name;
indexDebugNode(debugEl);
return el;
}
createComment(value: string): any {
const comment = this.delegate.createComment(value);
const debugEl = new DebugNode(comment, null, getCurrentDebugContext());
indexDebugNode(debugEl);
return comment;
}
createText(value: string): any {
const text = this.delegate.createText(value);
const debugEl = new DebugNode(text, null, getCurrentDebugContext());
indexDebugNode(debugEl);
return text;
}
appendChild(parent: any, newChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.addChild(debugChildEl);
}
this.delegate.appendChild(parent, newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild);
const debugRefEl = getDebugNode(refChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.insertBefore(debugRefEl, debugChildEl);
}
this.delegate.insertBefore(parent, newChild, refChild);
}
removeChild(parent: any, oldChild: any): void {
const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(oldChild);
if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.removeChild(debugChildEl);
}
this.delegate.removeChild(parent, oldChild);
}
selectRootElement(selectorOrNode: string|any): any {
const el = this.delegate.selectRootElement(selectorOrNode);
const debugEl = new DebugElement(el, null, getCurrentDebugContext());
indexDebugNode(debugEl);
return el;
}
setAttribute(el: any, name: string, value: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = value;
}
this.delegate.setAttribute(el, name, value, namespace);
}
removeAttribute(el: any, name: string, namespace?: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
const fullName = namespace ? namespace + ':' + name : name;
debugEl.attributes[fullName] = null;
}
this.delegate.removeAttribute(el, name, namespace);
}
addClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = true;
}
this.delegate.addClass(el, name);
}
removeClass(el: any, name: string): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.classes[name] = false;
}
this.delegate.removeClass(el, name);
}
setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean):
void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = value;
}
this.delegate.setStyle(el, style, value, hasVendorPrefix, hasImportant);
}
removeStyle(el: any, style: string, hasVendorPrefix: boolean): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.styles[style] = null;
}
this.delegate.removeStyle(el, style, hasVendorPrefix);
}
setProperty(el: any, name: string, value: any): void {
const debugEl = getDebugNode(el);
if (debugEl && debugEl instanceof DebugElement) {
debugEl.properties[name] = value;
}
this.delegate.setProperty(el, name, value);
}
listen(
target: 'document'|'windows'|'body'|any, eventName: string,
callback: (event: any) => boolean): () => void {
if (typeof target !== 'string') {
const debugEl = getDebugNode(target);
if (debugEl) {
debugEl.listeners.push(new EventListener(eventName, callback));
}
}
return this.delegate.listen(target, eventName, callback);
}
parentNode(node: any): any { return this.delegate.parentNode(node); }
nextSibling(node: any): any { return this.delegate.nextSibling(node); }
setValue(node: any, value: string): void { return this.delegate.setValue(node, value); }
}

View File

@ -54,7 +54,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
let renderNode: any;
const renderer = view.root.renderer;
const renderer = view.renderer;
renderNode = renderer.createText(def.text.prefix);
const parentEl = getParentRenderElement(view, renderHost, def);
if (parentEl) {
@ -119,7 +119,7 @@ export function checkAndUpdateTextInline(
}
value = def.text.prefix + value;
const renderNode = asTextData(view, def.index).renderText;
view.root.renderer.setText(renderNode, value);
view.renderer.setValue(renderNode, value);
}
}
@ -140,7 +140,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
}
value = def.text.prefix + value;
const renderNode = asTextData(view, def.index).renderText;
view.root.renderer.setText(renderNode, value);
view.renderer.setValue(renderNode, value);
}
}

View File

@ -14,7 +14,7 @@ import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import {ViewRef} from '../linker/view_ref';
import {ViewEncapsulation} from '../metadata/view';
import {RenderDebugContext, RendererV2} from '../render/api';
import {ComponentRenderTypeV2, RendererFactoryV2, RendererV2} from '../render/api';
import {Sanitizer, SecurityContext} from '../security';
// -------------------------------------
@ -23,7 +23,6 @@ import {Sanitizer, SecurityContext} from '../security';
export interface ViewDefinition {
flags: ViewFlags;
component: ComponentDefinition;
updateDirectives: ViewUpdateFn;
updateRenderer: ViewUpdateFn;
handleEvent: ViewHandleEventFn;
@ -75,13 +74,7 @@ export enum ArgumentType {
*/
export enum ViewFlags {
None = 0,
OnPush = 1 << 1
}
export interface ComponentDefinition {
id: string;
encapsulation: ViewEncapsulation;
styles: string[];
OnPush = 1 << 1,
}
/**
@ -223,6 +216,7 @@ export interface ProviderDef {
value: any;
deps: DepDef[];
outputs: DirectiveOutputDef[];
componentRenderType: ComponentRenderTypeV2;
// closure to allow recursive components
component: ViewDefinitionFactory;
}
@ -306,6 +300,7 @@ export interface NgContentDef {
export interface ViewData {
def: ViewDefinition;
root: RootData;
renderer: RendererV2;
// index of component provider / anchor.
parentNodeDef: NodeDef;
parent: ViewData;
@ -426,12 +421,21 @@ export interface RootData {
projectableNodes: any[][];
selectorOrNode: any;
renderer: RendererV2;
rendererFactory: RendererFactoryV2;
sanitizer: Sanitizer;
}
export abstract class DebugContext extends RenderDebugContext {
export abstract class DebugContext {
abstract get view(): ViewData;
abstract get nodeIndex(): number;
abstract get injector(): Injector;
abstract get component(): any;
abstract get providerTokens(): any[];
abstract get references(): {[key: string]: any};
abstract get context(): any;
abstract get source(): string;
abstract get componentRenderElement(): any;
abstract get renderNode(): any;
}
// -------------------------------------

View File

@ -14,7 +14,8 @@ import {looseIdentical, stringify} from '../facade/lang';
import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import {ViewRef} from '../linker/view_ref';
import {Renderer} from '../render/api';
import {ViewEncapsulation} from '../metadata/view';
import {ComponentRenderTypeV2, Renderer} from '../render/api';
import {expressionChangedAfterItHasBeenCheckedError, isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
import {DebugContext, ElementData, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asProviderData, asTextData} from './types';
@ -40,6 +41,23 @@ export function unwrapValue(value: any): any {
return value;
}
let _renderCompCount = 0;
export function createComponentRenderTypeV2(values: {
styles: (string | any[])[],
encapsulation: ViewEncapsulation,
data: {[kind: string]: any[]}
}): ComponentRenderTypeV2 {
const isFilled = values && (values.encapsulation !== ViewEncapsulation.None ||
values.styles.length || Object.keys(values.data).length);
if (isFilled) {
const id = `c${_renderCompCount++}`;
return {id: id, styles: values.styles, encapsulation: values.encapsulation, data: values.data};
} else {
return null;
}
}
export function checkBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
@ -220,7 +238,7 @@ export function visitRootRenderNodes(
view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) {
// We need to re-compute the parent node in case the nodes have been moved around manually
if (action === RenderNodeAction.RemoveChild) {
parentNode = view.root.renderer.parentNode(renderNode(view, view.def.lastRootNode));
parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRootNode));
}
visitSiblingRenderNodes(
view, action, 0, view.def.nodes.length - 1, parentNode, nextSibling, target);
@ -298,7 +316,7 @@ function visitRenderNode(
function execRenderNodeAction(
view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any,
target: any[]) {
const renderer = view.root.renderer;
const renderer = view.renderer;
switch (action) {
case RenderNodeAction.AppendChild:
renderer.appendChild(parentNode, renderNode);

View File

@ -7,6 +7,7 @@
*/
import {ViewEncapsulation} from '../metadata/view';
import {ComponentRenderTypeV2, RendererV2} from '../render/api';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
@ -15,15 +16,14 @@ import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAn
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
import {ArgumentType, ComponentDefinition, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
import {ArgumentType, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types';
import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util';
const NOOP = (): any => undefined;
export function viewDef(
flags: ViewFlags, nodes: NodeDef[], updateDirectives?: ViewUpdateFn,
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn, compId?: string,
encapsulation?: ViewEncapsulation, styles?: string[]): ViewDefinition {
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
// clone nodes and set auto calculated values
if (nodes.length === 0) {
throw new Error(`Illegal State: Views without nodes are not allowed!`);
@ -57,8 +57,11 @@ export function viewDef(
let currentRenderParent: NodeDef;
if (currentParent &&
!(currentParent.type === NodeType.Element && currentParent.element.component)) {
// children of components should never be attached!
(currentParent.type !== NodeType.Element || !currentParent.element.component ||
(currentParent.element.component.provider.componentRenderType &&
currentParent.element.component.provider.componentRenderType.encapsulation ===
ViewEncapsulation.Native))) {
// children of components that don't use native encapsulation should never be attached!
if (currentParent && currentParent.type === NodeType.Element && !currentParent.element.name) {
currentRenderParent = currentParent.renderParent;
} else {
@ -134,8 +137,6 @@ export function viewDef(
}
currentParent = newParent;
}
const componentDef =
compId ? <ComponentDefinition>{id: compId, encapsulation, styles} : undefined;
return {
nodeFlags: viewNodeFlags,
nodeMatchedQueries: viewMatchedQueries, flags,
@ -143,7 +144,6 @@ export function viewDef(
updateDirectives: updateDirectives || NOOP,
updateRenderer: updateRenderer || NOOP,
handleEvent: handleEvent || NOOP,
component: componentDef,
bindingCount: viewBindingCount,
disposableCount: viewDisposableCount, lastRootNode
};
@ -222,21 +222,23 @@ function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
// embedded views are seen as siblings to the anchor, so we need
// to get the parent of the anchor and use it as parentIndex.
const view = createView(parent.root, parent, anchorDef, anchorDef.element.template);
const view =
createView(parent.root, parent.renderer, parent, anchorDef, anchorDef.element.template);
initView(view, parent.component, context);
createViewNodes(view);
return view;
}
export function createRootView(root: RootData, def: ViewDefinition, context?: any): ViewData {
const view = createView(root, null, null, def);
const view = createView(root, root.renderer, null, null, def);
initView(view, context, context);
createViewNodes(view);
return view;
}
function createView(
root: RootData, parent: ViewData, parentNodeDef: NodeDef, def: ViewDefinition): ViewData {
root: RootData, renderer: RendererV2, parent: ViewData, parentNodeDef: NodeDef,
def: ViewDefinition): ViewData {
const nodes: NodeData[] = new Array(def.nodes.length);
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
const view: ViewData = {
@ -245,7 +247,7 @@ function createView(
parentNodeDef,
context: undefined,
component: undefined, nodes,
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root,
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer,
oldValues: new Array(def.bindingCount), disposables
};
return view;
@ -259,9 +261,9 @@ function initView(view: ViewData, component: any, context: any) {
function createViewNodes(view: ViewData) {
let renderHost: any;
if (isComponentView(view)) {
renderHost = asElementData(view.parent, viewParentEl(view).index).renderElement;
const hostDef = view.parentNodeDef;
renderHost = asElementData(view.parent, hostDef.parent.index).renderElement;
}
const def = view.def;
const nodes = view.nodes;
for (let i = 0; i < def.nodes.length; i++) {
@ -291,8 +293,16 @@ function createViewNodes(view: ViewData) {
// Components can inject a ChangeDetectorRef that needs a references to
// the component view. Therefore, we create the component view first
// and set the ProviderData in ViewData, and then instantiate the provider.
const componentView = createView(
view.root, view, nodeDef, resolveViewDefinition(nodeDef.provider.component));
const compViewDef = resolveViewDefinition(nodeDef.provider.component);
const compRenderType = nodeDef.provider.componentRenderType;
let compRenderer: RendererV2;
if (!compRenderType) {
compRenderer = view.root.renderer;
} else {
const hostEl = asElementData(view, nodeDef.parent.index).renderElement;
compRenderer = view.root.rendererFactory.createRenderer(hostEl, compRenderType);
}
const componentView = createView(view.root, compRenderer, view, nodeDef, compViewDef);
const providerData = <ProviderData>{componentView, instance: undefined};
nodes[i] = providerData as any;
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
@ -473,9 +483,27 @@ export function destroyView(view: ViewData) {
view.disposables[i]();
}
}
if (view.renderer.destroyNode) {
destroyViewNodes(view);
}
if (view.parentNodeDef && view.parentNodeDef.flags & NodeFlags.HasComponent) {
view.renderer.destroy();
}
view.state |= ViewState.Destroyed;
}
function destroyViewNodes(view: ViewData) {
const len = view.def.nodes.length;
for (let i = 0; i < len; i++) {
const def = view.def.nodes[i];
if (def.type === NodeType.Element) {
view.renderer.destroyNode(asElementData(view, i).renderElement);
} else if (def.type === NodeType.Text) {
view.renderer.destroyNode(asTextData(view, i).renderText);
}
}
}
enum ViewAction {
CreateViewNodes,
CheckNoChanges,

View File

@ -77,15 +77,15 @@ export function moveEmbeddedView(
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) {
const prevRenderNode =
prevView ? renderNode(prevView, prevView.def.lastRootNode) : elementData.renderElement;
const parentNode = view.root.renderer.parentNode(prevRenderNode);
const nextSibling = view.root.renderer.nextSibling(prevRenderNode);
const parentNode = view.renderer.parentNode(prevRenderNode);
const nextSibling = view.renderer.nextSibling(prevRenderNode);
// Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be!
// However, browsers automatically do `appendChild` when there is no `nextSibling`.
visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined);
}
function renderDetachEmbeddedView(elementData: ElementData, view: ViewData) {
const parentNode = view.root.renderer.parentNode(elementData.renderElement);
const parentNode = view.renderer.parentNode(elementData.renderElement);
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, parentNode, null, undefined);
}

View File

@ -9,8 +9,8 @@
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RENDERER_V2_DIRECT, RenderComponentType, Renderer, RendererV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
import {DebugDomRenderer, DebugDomRendererV2} from '@angular/core/src/debug/debug_renderer';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactoryV2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
import {DebugDomRenderer} from '@angular/core/src/debug/debug_renderer';
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -57,6 +57,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
renderLog = TestBed.get(RenderLog);
directiveLog = TestBed.get(DirectiveLog);
elSchema.existingProperties['someProp'] = true;
patchLoggingRendererV2(TestBed.get(RendererFactoryV2), renderLog);
}
function queryDirs(el: DebugElement, dirType: Type<any>): any {
@ -123,11 +124,6 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
RenderLog,
DirectiveLog,
{provide: RootRenderer, useClass: LoggingRootRenderer},
{
provide: RendererV2,
useFactory: (r: RendererV2, log: RenderLog) => new LoggingRendererV2(r, log),
deps: [[new Inject(RENDERER_V2_DIRECT)], [RenderLog]],
},
],
});
});
@ -1244,28 +1240,26 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
expect(renderLog.loggedValues).toEqual(['Tom']);
});
// TODO(tbosch): ViewQueries don't work yet with the view engine...
viewEngine ||
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
});
});
}
@ -1315,18 +1309,32 @@ class DirectiveLogEntry {
constructor(public directiveName: string, public method: string) {}
}
class LoggingRendererV2 extends DebugDomRendererV2 {
constructor(private delegate: RendererV2, private log: RenderLog) { super(delegate); }
setProperty(el: any, name: string, value: any): void {
this.log.setElementProperty(el, name, value);
super.setProperty(el, name, value);
}
setText(node: any, value: string): void {
this.log.setText(node, value);
super.setText(node, value);
function patchLoggingRendererV2(rendererFactory: RendererFactoryV2, log: RenderLog) {
if ((<any>rendererFactory).__patchedForLogging) {
return;
}
(<any>rendererFactory).__patchedForLogging = true;
const origCreateRenderer = rendererFactory.createRenderer;
rendererFactory.createRenderer = function() {
const renderer = origCreateRenderer.apply(this, arguments);
if ((<any>renderer).__patchedForLogging) {
return renderer;
}
(<any>renderer).__patchedForLogging = true;
const origSetProperty = renderer.setProperty;
const origSetValue = renderer.setValue;
renderer.setProperty = function(el: any, name: string, value: any): void {
log.setElementProperty(el, name, value);
origSetProperty.call(this, el, name, value);
};
renderer.setValue = function(node: any, value: string): void {
if (getDOM().isTextNode(node)) {
log.setText(node, value);
}
origSetValue.call(this, node, value);
};
return renderer;
};
}
@Injectable()

View File

@ -1515,7 +1515,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
const fixture = TestBed.createComponent(ParentCmp);
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test$="hello"');
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test_="hello"');
});
it('should reflect property values on template comments', () => {

View File

@ -378,7 +378,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
expect(main.nativeElement).toHaveText('TREE(0:TREE2(1:TREE(2:)))');
});
if (!viewEngine && getDOM().supportsNativeShadowDOM()) {
if (getDOM().supportsNativeShadowDOM()) {
it('should support native content projection and isolate styles per component', () => {
TestBed.configureTestingModule({declarations: [SimpleNative1, SimpleNative2]});
TestBed.overrideComponent(MainComp, {
@ -396,7 +396,7 @@ function createTests({viewEngine}: {viewEngine: boolean}) {
});
}
if (!viewEngine && getDOM().supportsDOMEvents()) {
if (getDOM().supportsDOMEvents()) {
it('should support non emulated styles', () => {
TestBed.configureTestingModule({declarations: [OtherComp]});
TestBed.overrideComponent(MainComp, {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RendererV2, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors';
import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {TestBed, inject, withModule} from '@angular/core/testing';
@ -19,9 +19,7 @@ export function main() {
function compViewDef(
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(
viewFlags, nodes, updateDirectives, updateRenderer, handleEvent, 'someCompId',
ViewEncapsulation.None, []);
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
@ -292,11 +290,25 @@ export function main() {
it('should inject RendererV1', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
directiveDef(
NodeFlags.None, null, 0, SomeService, [Renderer], null, null,
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
]));
expect(instance.dep.createElement).toBeTruthy();
});
it('should inject RendererV2', () => {
createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'span'),
directiveDef(
NodeFlags.None, null, 0, SomeService, [RendererV2], null, null,
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
]));
expect(instance.dep.createElement).toBeTruthy();
});
});
});

View File

@ -7,7 +7,7 @@
*/
import {CommonModule, PlatformLocation} from '@angular/common';
import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RENDERER_V2_DIRECT, RendererV2, RootRenderer, Sanitizer, SkipSelf, Testability, createPlatformFactory, platformCore} from '@angular/core';
import {ApplicationModule, ErrorHandler, NgModule, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactoryV2, RootRenderer, Sanitizer, SkipSelf, Testability, createPlatformFactory, platformCore} from '@angular/core';
import {AnimationDriver} from '../src/dom/animation_driver';
import {WebAnimationsDriver} from '../src/dom/web_animations_driver';
@ -19,7 +19,7 @@ import {BrowserGetTestability} from './browser/testability';
import {Title} from './browser/title';
import {ELEMENT_PROBE_PROVIDERS} from './dom/debug/ng_probe';
import {getDOM} from './dom/dom_adapter';
import {DomRendererV2, DomRootRenderer, DomRootRenderer_} from './dom/dom_renderer';
import {DomRendererFactoryV2, DomRootRenderer, DomRootRenderer_} from './dom/dom_renderer';
import {DOCUMENT} from './dom/dom_tokens';
import {DomEventsPlugin} from './dom/events/dom_events';
import {EVENT_MANAGER_PLUGINS, EventManager} from './dom/events/event_manager';
@ -86,8 +86,8 @@ export function _resolveDefaultAnimationDriver(): AnimationDriver {
{provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig},
{provide: DomRootRenderer, useClass: DomRootRenderer_},
{provide: RootRenderer, useExisting: DomRootRenderer},
{provide: RENDERER_V2_DIRECT, useClass: DomRendererV2},
{provide: RendererV2, useExisting: RENDERER_V2_DIRECT},
DomRendererFactoryV2,
{provide: RendererFactoryV2, useExisting: DomRendererFactoryV2},
{provide: SharedStylesHost, useExisting: DomSharedStylesHost},
{provide: AnimationDriver, useFactory: _resolveDefaultAnimationDriver},
DomSharedStylesHost,

View File

@ -9,9 +9,9 @@
import * as core from '@angular/core';
import {StringMapWrapper} from '../../facade/collection';
import {DebugDomRendererV2, DebugDomRootRenderer} from '../../private_import_core';
import {DebugDomRootRenderer} from '../../private_import_core';
import {getDOM} from '../dom_adapter';
import {DomRootRenderer} from '../dom_renderer';
import {DomRendererFactoryV2, DomRootRenderer} from '../dom_renderer';
const CORE_TOKENS = {
'ApplicationRef': core.ApplicationRef,
@ -58,10 +58,6 @@ function _ngProbeTokensToMap(tokens: NgProbeToken[]): {[name: string]: any} {
return tokens.reduce((prev: any, t: any) => (prev[t.name] = t.token, prev), {});
}
export function _createDebugRendererV2(renderer: core.RendererV2): core.RendererV2 {
return core.isDevMode() ? new DebugDomRendererV2(renderer) : renderer;
}
/**
* Providers which support debugging Angular applications (e.g. via `ng.probe`).
*/
@ -75,9 +71,4 @@ export const ELEMENT_PROBE_PROVIDERS: core.Provider[] = [
[core.NgProbeToken, new core.Optional()],
],
},
{
provide: core.RendererV2,
useFactory: _createDebugRendererV2,
deps: [core.RENDERER_V2_DIRECT],
}
];

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_ID, Inject, Injectable, RenderComponentType, Renderer, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
import {APP_ID, ComponentRenderTypeV2, Inject, Injectable, RenderComponentType, Renderer, RendererFactoryV2, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
import {isPresent, stringify} from '../facade/lang';
import {AnimationKeyframe, AnimationPlayer, AnimationStyles, DirectRenderer, NoOpAnimationPlayer, RenderDebugInfo} from '../private_import_core';
@ -229,13 +229,8 @@ export class DomRenderer implements Renderer {
TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(parsedBindings, null, 2));
} else {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
if (propertyName[propertyName.length - 1] === '$') {
const attrNode: Attr = createAttributeNode(propertyName).cloneNode(true) as Attr;
attrNode.value = propertyValue;
renderElement.setAttributeNode(attrNode);
} else {
this.setElementAttribute(renderElement, propertyName, propertyValue);
}
propertyName = propertyName.replace(/\$/g, '_');
this.setElementAttribute(renderElement, propertyName, propertyValue);
}
}
@ -366,10 +361,51 @@ function createAttributeNode(name: string): Attr {
}
@Injectable()
export class DomRendererV2 implements RendererV2 {
constructor(private eventManager: EventManager){};
export class DomRendererFactoryV2 implements RendererFactoryV2 {
private rendererByCompId = new Map<string, RendererV2>();
private defaultRenderer: RendererV2;
createElement(name: string, namespace?: string, debugInfo?: any): any {
constructor(private eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost) {
this.defaultRenderer = new DefaultDomRendererV2(eventManager);
};
createRenderer(element: any, type: ComponentRenderTypeV2): RendererV2 {
if (!element || !type) {
return this.defaultRenderer;
}
switch (type.encapsulation) {
case ViewEncapsulation.Emulated: {
let renderer = this.rendererByCompId.get(type.id);
if (!renderer) {
renderer = new EmulatedEncapsulationDomRendererV2(
this.eventManager, this.sharedStylesHost, type);
this.rendererByCompId.set(type.id, renderer);
}
(<EmulatedEncapsulationDomRendererV2>renderer).applyToHost(element);
return renderer;
}
case ViewEncapsulation.Native:
return new ShadowDomRenderer(this.eventManager, this.sharedStylesHost, element, type);
default: {
if (!this.rendererByCompId.has(type.id)) {
const styles = flattenStyles(type.id, type.styles, []);
this.sharedStylesHost.addStyles(styles);
this.rendererByCompId.set(type.id, this.defaultRenderer);
}
return this.defaultRenderer;
}
}
}
}
class DefaultDomRendererV2 implements RendererV2 {
constructor(private eventManager: EventManager) {}
destroy(): void {}
destroyNode: null;
createElement(name: string, namespace?: string): any {
if (namespace) {
return document.createElementNS(NAMESPACE_URIS[namespace], name);
}
@ -377,9 +413,9 @@ export class DomRendererV2 implements RendererV2 {
return document.createElement(name);
}
createComment(value: string, debugInfo?: any): any { return document.createComment(value); }
createComment(value: string): any { return document.createComment(value); }
createText(value: string, debugInfo?: any): any { return document.createTextNode(value); }
createText(value: string): any { return document.createTextNode(value); }
appendChild(parent: any, newChild: any): void { parent.appendChild(newChild); }
@ -395,7 +431,7 @@ export class DomRendererV2 implements RendererV2 {
}
}
selectRootElement(selectorOrNode: string|any, debugInfo?: any): any {
selectRootElement(selectorOrNode: string|any): any {
let el: any = typeof selectorOrNode === 'string' ? document.querySelector(selectorOrNode) :
selectorOrNode;
el.textContent = '';
@ -422,43 +458,6 @@ export class DomRendererV2 implements RendererV2 {
}
}
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
if (el.nodeType === Node.COMMENT_NODE) {
const m = el.nodeValue.replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
const obj = m === null ? {} : JSON.parse(m[1]);
obj[propertyName] = propertyValue;
el.nodeValue = TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2));
} else {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
if (propertyName[propertyName.length - 1] === '$') {
const attrNode: Attr = createAttributeNode(propertyName).cloneNode(true) as Attr;
attrNode.value = propertyValue;
el.setAttributeNode(attrNode);
} else {
this.setAttribute(el, propertyName, propertyValue);
}
}
}
removeBindingDebugInfo(el: any, propertyName: string): void {
if (el.nodeType === Node.COMMENT_NODE) {
const m = el.nodeValue.replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
const obj = m === null ? {} : JSON.parse(m[1]);
delete obj[propertyName];
el.nodeValue = TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2));
} else {
// Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
if (propertyName[propertyName.length - 1] === '$') {
const attrNode: Attr = createAttributeNode(propertyName).cloneNode(true) as Attr;
attrNode.value = '';
el.setAttributeNode(attrNode);
} else {
this.removeAttribute(el, propertyName);
}
}
}
addClass(el: any, name: string): void { el.classList.add(name); }
removeClass(el: any, name: string): void { el.classList.remove(name); }
@ -476,7 +475,7 @@ export class DomRendererV2 implements RendererV2 {
setProperty(el: any, name: string, value: any): void { el[name] = value; }
setText(node: any, value: string): void { node.nodeValue = value; }
setValue(node: any, value: string): void { node.nodeValue = value; }
listen(target: 'window'|'document'|'body'|any, event: string, callback: (event: any) => boolean):
() => void {
@ -488,3 +487,62 @@ export class DomRendererV2 implements RendererV2 {
target, event, decoratePreventDefault(callback)) as() => void;
}
}
class EmulatedEncapsulationDomRendererV2 extends DefaultDomRendererV2 {
private contentAttr: string;
private hostAttr: string;
constructor(
eventManager: EventManager, sharedStylesHost: DomSharedStylesHost,
private component: ComponentRenderTypeV2) {
super(eventManager);
const styles = flattenStyles(component.id, component.styles, []);
sharedStylesHost.addStyles(styles);
this.contentAttr = shimContentAttribute(component.id);
this.hostAttr = shimHostAttribute(component.id);
}
applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); }
createElement(parent: any, name: string): Element {
const el = super.createElement(parent, name);
super.setAttribute(el, this.contentAttr, '');
return el;
}
}
class ShadowDomRenderer extends DefaultDomRendererV2 {
private shadowRoot: any;
constructor(
eventManager: EventManager, private sharedStylesHost: DomSharedStylesHost,
private hostEl: any, private component: ComponentRenderTypeV2) {
super(eventManager);
this.shadowRoot = (hostEl as any).createShadowRoot();
this.sharedStylesHost.addHost(this.shadowRoot);
const styles = flattenStyles(component.id, component.styles, []);
for (let i = 0; i < styles.length; i++) {
const styleEl = document.createElement('style');
styleEl.textContent = styles[i];
this.shadowRoot.appendChild(styleEl);
}
}
private nodeOrShadowRoot(node: any): any { return node === this.hostEl ? this.shadowRoot : node; }
destroy() { this.sharedStylesHost.removeHost(this.shadowRoot); }
appendChild(parent: any, newChild: any): void {
return super.appendChild(this.nodeOrShadowRoot(parent), newChild);
}
insertBefore(parent: any, newChild: any, refChild: any): void {
return super.insertBefore(this.nodeOrShadowRoot(parent), newChild, refChild);
}
removeChild(parent: any, oldChild: any): void {
return super.removeChild(this.nodeOrShadowRoot(parent), oldChild);
}
parentNode(node: any): any {
return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(node)));
}
}

View File

@ -16,8 +16,6 @@ export const ReflectionCapabilities: typeof r.ReflectionCapabilities = r.Reflect
export type DebugDomRootRenderer = typeof r._DebugDomRootRenderer;
export const DebugDomRootRenderer: typeof r.DebugDomRootRenderer = r.DebugDomRootRenderer;
export type DebugDomRendererV2 = typeof r._DebugDomRendererV2;
export const DebugDomRendererV2: typeof r.DebugDomRendererV2 = r.DebugDomRendererV2;
export const reflector: typeof r.reflector = r.reflector;

View File

@ -21,8 +21,6 @@ export type RenderDebugInfo = typeof r._RenderDebugInfo;
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
export type DebugDomRootRenderer = typeof r._DebugDomRootRenderer;
export const DebugDomRootRenderer: typeof r.DebugDomRootRenderer = r.DebugDomRootRenderer;
export type DebugDomRendererV2 = typeof r._DebugDomRendererV2;
export const DebugDomRendererV2: typeof r.DebugDomRendererV2 = r.DebugDomRendererV2;
export type ALLOW_MULTIPLE_PLATFORMS = typeof r.ALLOW_MULTIPLE_PLATFORMS;
export const ALLOW_MULTIPLE_PLATFORMS: typeof r.ALLOW_MULTIPLE_PLATFORMS =
r.ALLOW_MULTIPLE_PLATFORMS;

View File

@ -8,7 +8,7 @@
import {PlatformLocation} from '@angular/common';
import {platformCoreDynamic} from '@angular/compiler';
import {APP_BOOTSTRAP_LISTENER, Injectable, InjectionToken, Injector, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RENDERER_V2_DIRECT, RendererV2, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {APP_BOOTSTRAP_LISTENER, Injectable, InjectionToken, Injector, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactoryV2, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {HttpModule} from '@angular/http';
import {BrowserModule, DOCUMENT} from '@angular/platform-browser';
@ -16,9 +16,9 @@ import {SERVER_HTTP_PROVIDERS} from './http';
import {ServerPlatformLocation} from './location';
import {Parse5DomAdapter, parseDocument} from './parse5_adapter';
import {PlatformState} from './platform_state';
import {ALLOW_MULTIPLE_PLATFORMS, DebugDomRendererV2, DebugDomRootRenderer} from './private_import_core';
import {ALLOW_MULTIPLE_PLATFORMS, DebugDomRootRenderer} from './private_import_core';
import {SharedStylesHost, getDOM} from './private_import_platform-browser';
import {ServerRendererV2, ServerRootRenderer} from './server_renderer';
import {ServerRendererFactoryV2, ServerRootRenderer} from './server_renderer';
import {ServerStylesHost} from './styles_host';
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
@ -42,10 +42,6 @@ export function _createConditionalRootRenderer(rootRenderer: any) {
return isDevMode() ? new DebugDomRootRenderer(rootRenderer) : rootRenderer;
}
export function _createDebugRendererV2(renderer: RendererV2): RendererV2 {
return isDevMode() ? new DebugDomRendererV2(renderer) : renderer;
}
export function _addStylesToRootComponentFactory(stylesHost: ServerStylesHost) {
const initializer = () => stylesHost.rootComponentIsReady();
return initializer;
@ -53,9 +49,9 @@ export function _addStylesToRootComponentFactory(stylesHost: ServerStylesHost) {
export const SERVER_RENDER_PROVIDERS: Provider[] = [
ServerRootRenderer,
{provide: RENDERER_V2_DIRECT, useClass: ServerRendererV2},
{provide: RendererV2, useFactory: _createDebugRendererV2, deps: [RENDERER_V2_DIRECT]},
{provide: RootRenderer, useFactory: _createConditionalRootRenderer, deps: [ServerRootRenderer]},
ServerRendererFactoryV2,
{provide: RendererFactoryV2, useExisting: ServerRendererFactoryV2},
ServerStylesHost,
{provide: SharedStylesHost, useExisting: ServerStylesHost},
{

View File

@ -7,7 +7,7 @@
*/
import {DomElementSchemaRegistry} from '@angular/compiler';
import {APP_ID, Inject, Injectable, NgZone, RenderComponentType, Renderer, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
import {APP_ID, ComponentRenderTypeV2, Inject, Injectable, NgZone, RenderComponentType, Renderer, RendererFactoryV2, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
import {AnimationDriver, DOCUMENT} from '@angular/platform-browser';
import {isBlank, isPresent, stringify} from './facade/lang';
@ -204,6 +204,7 @@ export class ServerRenderer implements Renderer {
renderElement,
TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(parsedBindings, null, 2)));
} else {
propertyName = propertyName.replace(/\$/g, '_');
this.setElementAttribute(renderElement, propertyName, propertyValue);
}
}
@ -262,8 +263,51 @@ function appendNodes(parent: any, nodes: any) {
}
@Injectable()
export class ServerRendererV2 implements RendererV2 {
constructor(private ngZone: NgZone, @Inject(DOCUMENT) private document: any) {}
export class ServerRendererFactoryV2 implements RendererFactoryV2 {
private rendererByCompId = new Map<string, RendererV2>();
private defaultRenderer: RendererV2;
constructor(
private ngZone: NgZone, @Inject(DOCUMENT) private document: any,
private sharedStylesHost: SharedStylesHost) {
this.defaultRenderer = new DefaultServerRendererV2(document, ngZone);
};
createRenderer(element: any, type: ComponentRenderTypeV2): RendererV2 {
if (!element || !type) {
return this.defaultRenderer;
}
switch (type.encapsulation) {
case ViewEncapsulation.Emulated: {
let renderer = this.rendererByCompId.get(type.id);
if (!renderer) {
renderer = new EmulatedEncapsulationServerRendererV2(
this.document, this.ngZone, this.sharedStylesHost, type);
this.rendererByCompId.set(type.id, renderer);
}
(<EmulatedEncapsulationServerRendererV2>renderer).applyToHost(element);
return renderer;
}
case ViewEncapsulation.Native:
throw new Error('Native encapsulation is not supported on the server!');
default: {
if (!this.rendererByCompId.has(type.id)) {
const styles = flattenStyles(type.id, type.styles, []);
this.sharedStylesHost.addStyles(styles);
this.rendererByCompId.set(type.id, this.defaultRenderer);
}
return this.defaultRenderer;
}
}
}
}
class DefaultServerRendererV2 implements RendererV2 {
constructor(private document: any, private ngZone: NgZone) {}
destroy(): void {}
destroyNode: null;
createElement(name: string, namespace?: string, debugInfo?: any): any {
if (namespace) {
@ -325,28 +369,6 @@ export class ServerRendererV2 implements RendererV2 {
}
}
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
if (getDOM().isCommentNode(el)) {
const m = getDOM().getText(el).replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
const obj = m === null ? {} : JSON.parse(m[1]);
obj[propertyName] = propertyValue;
getDOM().setText(el, TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2)));
} else {
this.setAttribute(el, propertyName, propertyValue);
}
}
removeBindingDebugInfo(el: any, propertyName: string): void {
if (getDOM().isCommentNode(el)) {
const m = getDOM().getText(el).replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
const obj = m === null ? {} : JSON.parse(m[1]);
delete obj[propertyName];
getDOM().setText(el, TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2)));
} else {
this.removeAttribute(el, propertyName);
}
}
addClass(el: any, name: string): void { getDOM().addClass(el, name); }
removeClass(el: any, name: string): void { getDOM().removeClass(el, name); }
@ -362,7 +384,7 @@ export class ServerRendererV2 implements RendererV2 {
setProperty(el: any, name: string, value: any): void { getDOM().setProperty(el, name, value); }
setText(node: any, value: string): void { getDOM().setText(node, value); }
setValue(node: any, value: string): void { getDOM().setText(node, value); }
listen(
target: 'document'|'window'|'body'|any, eventName: string,
@ -375,3 +397,27 @@ export class ServerRendererV2 implements RendererV2 {
return this.ngZone.runOutsideAngular(() => getDOM().onAndCancel(el, eventName, outsideHandler));
}
}
class EmulatedEncapsulationServerRendererV2 extends DefaultServerRendererV2 {
private contentAttr: string;
private hostAttr: string;
constructor(
document: any, ngZone: NgZone, sharedStylesHost: SharedStylesHost,
private component: ComponentRenderTypeV2) {
super(document, ngZone);
const styles = flattenStyles(component.id, component.styles, []);
sharedStylesHost.addStyles(styles);
this.contentAttr = shimContentAttribute(component.id);
this.hostAttr = shimHostAttribute(component.id);
}
applyToHost(element: any) { super.setAttribute(element, this.hostAttr, ''); }
createElement(parent: any, name: string): Element {
const el = super.createElement(parent, name);
super.setAttribute(el, this.contentAttr, '');
return el;
}
}

View File

@ -7,9 +7,9 @@
*/
import {NgIf} from '@angular/common';
import {ComponentFactory, ComponentRef, Injector, RendererV2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
import {ComponentFactory, ComponentRef, Injector, RendererFactoryV2, RootRenderer, Sanitizer, TemplateRef, ViewContainerRef} from '@angular/core';
import {ArgumentType, BindingType, NodeFlags, ViewDefinition, ViewFlags, anchorDef, createComponentFactory, directiveDef, elementDef, initServicesIfNeeded, textDef, viewDef} from '@angular/core/src/view/index';
import {DomRendererV2} from '@angular/platform-browser/src/dom/dom_renderer';
import {DomRendererFactoryV2} from '@angular/platform-browser/src/dom/dom_renderer';
import {DomSanitizerImpl, SafeStyle} from '@angular/platform-browser/src/security/dom_sanitization_service';
import {TreeNode, emptyTree} from '../util';
@ -89,14 +89,14 @@ function TreeComponent_0(): ViewDefinition {
export class AppModule implements Injector {
private sanitizer: DomSanitizerImpl;
private componentFactory: ComponentFactory<TreeComponent>;
private rendererV2: RendererV2;
private rendererV2: RendererFactoryV2;
componentRef: ComponentRef<TreeComponent>;
constructor() {
initServicesIfNeeded();
this.sanitizer = new DomSanitizerImpl(document);
this.rendererV2 = new DomRendererV2(null);
this.rendererV2 = new DomRendererFactoryV2(null, null);
trustedEmptyColor = this.sanitizer.bypassSecurityTrustStyle('');
trustedGreyColor = this.sanitizer.bypassSecurityTrustStyle('grey');
this.componentFactory = createComponentFactory('#root', TreeComponent, TreeComponent_Host);
@ -104,7 +104,7 @@ export class AppModule implements Injector {
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
switch (token) {
case RendererV2:
case RendererFactoryV2:
return this.rendererV2;
case Sanitizer:
return this.sanitizer;

View File

@ -292,6 +292,16 @@ export declare abstract class ComponentRef<C> {
abstract onDestroy(callback: Function): void;
}
/** @experimental */
export interface ComponentRenderTypeV2 {
data: {
[kind: string]: any[];
};
encapsulation: ViewEncapsulation;
id: string;
styles: (string | any[])[];
}
/** @stable */
export declare const ContentChild: ContentChildDecorator;
@ -848,30 +858,32 @@ export declare abstract class Renderer {
}
/** @experimental */
export declare const RENDERER_V2_DIRECT: InjectionToken<RendererV2>;
export declare abstract class RendererFactoryV2 {
abstract createRenderer(hostElement: any, type: ComponentRenderTypeV2): RendererV2;
}
/** @experimental */
export declare abstract class RendererV2 {
destroyNode: (node: any) => void | null;
abstract addClass(el: any, name: string): void;
abstract appendChild(parent: any, newChild: any): void;
abstract createComment(value: string, debugInfo?: RenderDebugContext): any;
abstract createElement(name: string, namespace?: string, debugInfo?: RenderDebugContext): any;
abstract createText(value: string, debugInfo?: RenderDebugContext): any;
abstract createComment(value: string): any;
abstract createElement(name: string, namespace?: string): any;
abstract createText(value: string): any;
abstract destroy(): void;
abstract insertBefore(parent: any, newChild: any, refChild: any): void;
abstract listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean): () => void;
abstract nextSibling(node: any): any;
abstract parentNode(node: any): any;
abstract removeAttribute(el: any, name: string, namespace?: string): void;
abstract removeBindingDebugInfo(el: any, propertyName: string): void;
abstract removeChild(parent: any, oldChild: any): void;
abstract removeClass(el: any, name: string): void;
abstract removeStyle(el: any, style: string, hasVendorPrefix: boolean): void;
abstract selectRootElement(selectorOrNode: string | any, debugInfo?: RenderDebugContext): any;
abstract selectRootElement(selectorOrNode: string | any): any;
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void;
abstract setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void;
abstract setProperty(el: any, name: string, value: any): void;
abstract setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean): void;
abstract setText(node: any, value: string): void;
abstract setValue(node: any, value: string): void;
}
/** @experimental */