From 0fa3895d5bdbbc73fe44a3202c6ba8d351fad5da Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 16 Feb 2017 13:55:55 -0800 Subject: [PATCH] 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 --- modules/@angular/compiler/src/aot/compiler.ts | 21 +- .../@angular/compiler/src/compile_metadata.ts | 23 +- modules/@angular/compiler/src/identifiers.ts | 8 +- modules/@angular/compiler/src/jit/compiler.ts | 34 +- .../compiler/src/metadata_resolver.ts | 16 +- .../compiler/src/output/output_interpreter.ts | 5 +- .../compiler/src/output/output_jit.ts | 14 +- .../src/view_compiler/compile_view.ts | 4 +- .../src/view_compiler/view_builder.ts | 2 +- .../src/view_compiler/view_compiler.ts | 4 +- .../src/view_compiler_next/view_compiler.ts | 29 +- .../@angular/core/src/core_private_export.ts | 3 - .../@angular/core/src/debug/debug_renderer.ts | 150 +------- modules/@angular/core/src/render.ts | 2 +- modules/@angular/core/src/render/api.ts | 118 +++--- modules/@angular/core/src/view/element.ts | 10 +- modules/@angular/core/src/view/index.ts | 2 +- modules/@angular/core/src/view/provider.ts | 64 ++-- modules/@angular/core/src/view/refs.ts | 5 +- modules/@angular/core/src/view/services.ts | 342 ++++++++++++------ modules/@angular/core/src/view/text.ts | 6 +- modules/@angular/core/src/view/types.ts | 24 +- modules/@angular/core/src/view/util.ts | 24 +- modules/@angular/core/src/view/view.ts | 60 ++- modules/@angular/core/src/view/view_attach.ts | 6 +- .../change_detection_integration_spec.ts | 82 +++-- .../core/test/linker/integration_spec.ts | 2 +- .../linker/projection_integration_spec.ts | 4 +- .../@angular/core/test/view/provider_spec.ts | 22 +- .../@angular/platform-browser/src/browser.ts | 8 +- .../src/dom/debug/ng_probe.ts | 13 +- .../platform-browser/src/dom/dom_renderer.ts | 162 ++++++--- .../src/private_import_core.ts | 2 - .../src/private_import_core.ts | 2 - .../@angular/platform-server/src/server.ts | 14 +- .../platform-server/src/server_renderer.ts | 98 +++-- modules/benchmarks/src/tree/ng2_next/tree.ts | 10 +- tools/public_api_guard/core/index.d.ts | 28 +- 38 files changed, 828 insertions(+), 595 deletions(-) diff --git a/modules/@angular/compiler/src/aot/compiler.ts b/modules/@angular/compiler/src/aot/compiler.ts index 89ed6f4de6..4ee54666cb 100644 --- a/modules/@angular/compiler/src/aot/compiler.ts +++ b/modules/@angular/compiler/src/aot/compiler.ts @@ -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( diff --git a/modules/@angular/compiler/src/compile_metadata.ts b/modules/@angular/compiler/src/compile_metadata.ts index 8a034ad775..64fc05167d 100644 --- a/modules/@angular/compiler/src/compile_metadata.ts +++ b/modules/@angular/compiler/src/compile_metadata.ts @@ -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; } @@ -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, } = {}): 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; 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, } = {}) { 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: {}} }); } diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index 9fa5987bf1..b6987e9fc9 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -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 { diff --git a/modules/@angular/compiler/src/jit/compiler.ts b/modules/@angular/compiler/src/jit/compiler.ts index 4e443c97db..e1d7a5af75 100644 --- a/modules/@angular/compiler/src/jit/compiler.ts +++ b/modules/@angular/compiler/src/jit/compiler.ts @@ -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]; } (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[] { 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; (this.compMeta.componentViewType).setDelegate(viewClass); + for (let prop in componentRenderType) { + (this.compMeta.componentRenderType)[prop] = componentRenderType[prop]; + } this.isCompiled = true; } } diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 3ff3301d38..c628ebdff1 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -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 {}; + } + } + private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory { 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 diff --git a/modules/@angular/compiler/src/output/output_interpreter.ts b/modules/@angular/compiler/src/output/output_interpreter.ts index bfb544a8f4..eddc654119 100644 --- a/modules/@angular/compiler/src/output/output_interpreter.ts +++ b/modules/@angular/compiler/src/output/output_interpreter.ts @@ -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()); const visitor = new StatementInterpreter(); const result = visitor.visitAllStatements(stmtsWithReturn, ctx); diff --git a/modules/@angular/compiler/src/output/output_jit.ts b/modules/@angular/compiler/src/output/output_jit.ts index 0b529618db..cb22b44ec5 100644 --- a/modules/@angular/compiler/src/output/output_jit.ts +++ b/modules/@angular/compiler/src/output/output_jit.ts @@ -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 { diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index 3dbcafdc3b..95246cbb44 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -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(); 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) { diff --git a/modules/@angular/compiler/src/view_compiler/view_builder.ts b/modules/@angular/compiler/src/view_compiler/view_builder.ts index 607fa6f772..0a0d589002 100644 --- a/modules/@angular/compiler/src/view_compiler/view_builder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_builder.ts @@ -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)) { diff --git a/modules/@angular/compiler/src/view_compiler/view_compiler.ts b/modules/@angular/compiler/src/view_compiler/view_compiler.ts index 125f1dfee3..f4d3a4258f 100644 --- a/modules/@angular/compiler/src/view_compiler/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler/view_compiler.ts @@ -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) {} } @@ -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); } } diff --git a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts index 80d096060b..649f9f9211 100644 --- a/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler_next/view_compiler.ts @@ -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; diff --git a/modules/@angular/core/src/core_private_export.ts b/modules/@angular/core/src/core_private_export.ts index 0aaf2a9c1a..af753febd1 100644 --- a/modules/@angular/core/src/core_private_export.ts +++ b/modules/@angular/core/src/core_private_export.ts @@ -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, diff --git a/modules/@angular/core/src/debug/debug_renderer.ts b/modules/@angular/core/src/debug/debug_renderer.ts index 3f09a1bc2d..0d751ee3a0 100644 --- a/modules/@angular/core/src/debug/debug_renderer.ts +++ b/modules/@angular/core/src/debug/debug_renderer.ts @@ -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); - } -} diff --git a/modules/@angular/core/src/render.ts b/modules/@angular/core/src/render.ts index fc8e4f1f7f..d5dd29272f 100644 --- a/modules/@angular/core/src/render.ts +++ b/modules/@angular/core/src/render.ts @@ -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'; diff --git a/modules/@angular/core/src/render/api.ts b/modules/@angular/core/src/render/api.ts index d237eecdc7..0e0048d977 100644 --- a/modules/@angular/core/src/render/api.ts +++ b/modules/@angular/core/src/render/api.ts @@ -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('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; +} diff --git a/modules/@angular/core/src/view/element.ts b/modules/@angular/core/src/view/element.ts index 3066c94223..714cfce819 100644 --- a/modules/@angular/core/src/view/element.ts +++ b/modules/@angular/core/src/view/element.ts @@ -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 = /^:([^:]+):(.+)$/; diff --git a/modules/@angular/core/src/view/index.ts b/modules/@angular/core/src/view/index.ts index 403f1acb41..c8c3e3d144 100644 --- a/modules/@angular/core/src/view/index.ts +++ b/modules/@angular/core/src/view/index.ts @@ -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'; diff --git a/modules/@angular/core/src/view/provider.ts b/modules/@angular/core/src/view/provider.ts index 72a3f8372b..b4a71df864 100644 --- a/modules/@angular/core/src/view/provider.ts +++ b/modules/@angular/core/src/view/provider.ts @@ -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 { diff --git a/modules/@angular/core/src/view/refs.ts b/modules/@angular/core/src/view/refs.ts index 01a1ac7a06..94d744dd33 100644 --- a/modules/@angular/core/src/view/refs.ts +++ b/modules/@angular/core/src/view/refs.ts @@ -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); } } diff --git a/modules/@angular/core/src/view/services.ts b/modules/@angular/core/src/view/services.ts index be3b984521..2353bc8485 100644 --- a/modules/@angular/core/src/view/services.ts +++ b/modules/@angular/core/src/view/services.ts @@ -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 (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); -} \ No newline at end of file +} + + +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); } +} diff --git a/modules/@angular/core/src/view/text.ts b/modules/@angular/core/src/view/text.ts index 53816b2ac6..0d682d4a17 100644 --- a/modules/@angular/core/src/view/text.ts +++ b/modules/@angular/core/src/view/text.ts @@ -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); } } diff --git a/modules/@angular/core/src/view/types.ts b/modules/@angular/core/src/view/types.ts index 083f220c4b..7bfda9c1cd 100644 --- a/modules/@angular/core/src/view/types.ts +++ b/modules/@angular/core/src/view/types.ts @@ -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; } // ------------------------------------- diff --git a/modules/@angular/core/src/view/util.ts b/modules/@angular/core/src/view/util.ts index e74e6177c5..005302c720 100644 --- a/modules/@angular/core/src/view/util.ts +++ b/modules/@angular/core/src/view/util.ts @@ -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); diff --git a/modules/@angular/core/src/view/view.ts b/modules/@angular/core/src/view/view.ts index 5108878ea0..a1d837ffba 100644 --- a/modules/@angular/core/src/view/view.ts +++ b/modules/@angular/core/src/view/view.ts @@ -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 ? {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 = {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, diff --git a/modules/@angular/core/src/view/view_attach.ts b/modules/@angular/core/src/view/view_attach.ts index f8e12dbbba..2cb7239df6 100644 --- a/modules/@angular/core/src/view/view_attach.ts +++ b/modules/@angular/core/src/view/view_attach.ts @@ -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); } diff --git a/modules/@angular/core/test/linker/change_detection_integration_spec.ts b/modules/@angular/core/test/linker/change_detection_integration_spec.ts index 7690476f0e..945acdb7be 100644 --- a/modules/@angular/core/test/linker/change_detection_integration_spec.ts +++ b/modules/@angular/core/test/linker/change_detection_integration_spec.ts @@ -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 { @@ -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: ''}) - class Comp { - name = 'Tom'; - @ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef; - @ViewChild(TemplateRef) template: TemplateRef; - } + it('should recurse into nested view containers even if there are no bindings in the component view', + () => { + @Component({template: ''}) + class Comp { + name = 'Tom'; + @ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef; + @ViewChild(TemplateRef) template: TemplateRef; + } - 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 ((rendererFactory).__patchedForLogging) { + return; } + (rendererFactory).__patchedForLogging = true; + const origCreateRenderer = rendererFactory.createRenderer; + rendererFactory.createRenderer = function() { + const renderer = origCreateRenderer.apply(this, arguments); + if ((renderer).__patchedForLogging) { + return renderer; + } + (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() diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index 5527373114..39e8d3609a 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -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', () => { diff --git a/modules/@angular/core/test/linker/projection_integration_spec.ts b/modules/@angular/core/test/linker/projection_integration_spec.ts index 9fc489d63a..79e49e8290 100644 --- a/modules/@angular/core/test/linker/projection_integration_spec.ts +++ b/modules/@angular/core/test/linker/projection_integration_spec.ts @@ -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, { diff --git a/modules/@angular/core/test/view/provider_spec.ts b/modules/@angular/core/test/view/provider_spec.ts index d1ce2175fc..9148034888 100644 --- a/modules/@angular/core/test/view/provider_spec.ts +++ b/modules/@angular/core/test/view/provider_spec.ts @@ -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(); + }); + }); }); diff --git a/modules/@angular/platform-browser/src/browser.ts b/modules/@angular/platform-browser/src/browser.ts index ad7c8c7e35..d41e025faf 100644 --- a/modules/@angular/platform-browser/src/browser.ts +++ b/modules/@angular/platform-browser/src/browser.ts @@ -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, diff --git a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts index 3e5e92419e..9c83773ba3 100644 --- a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts +++ b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts @@ -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], - } ]; \ No newline at end of file diff --git a/modules/@angular/platform-browser/src/dom/dom_renderer.ts b/modules/@angular/platform-browser/src/dom/dom_renderer.ts index dd8a131cbc..2c1705b106 100644 --- a/modules/@angular/platform-browser/src/dom/dom_renderer.ts +++ b/modules/@angular/platform-browser/src/dom/dom_renderer.ts @@ -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(); + 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); + } + (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))); + } +} \ No newline at end of file diff --git a/modules/@angular/platform-browser/src/private_import_core.ts b/modules/@angular/platform-browser/src/private_import_core.ts index d1fbf84a3f..668b0f8f18 100644 --- a/modules/@angular/platform-browser/src/private_import_core.ts +++ b/modules/@angular/platform-browser/src/private_import_core.ts @@ -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; diff --git a/modules/@angular/platform-server/src/private_import_core.ts b/modules/@angular/platform-server/src/private_import_core.ts index 7419291eb0..c76c5e6fd1 100644 --- a/modules/@angular/platform-server/src/private_import_core.ts +++ b/modules/@angular/platform-server/src/private_import_core.ts @@ -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; diff --git a/modules/@angular/platform-server/src/server.ts b/modules/@angular/platform-server/src/server.ts index 9a1a2ac46f..1795e18975 100644 --- a/modules/@angular/platform-server/src/server.ts +++ b/modules/@angular/platform-server/src/server.ts @@ -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}, { diff --git a/modules/@angular/platform-server/src/server_renderer.ts b/modules/@angular/platform-server/src/server_renderer.ts index c3c2ac0967..35b91a3dad 100644 --- a/modules/@angular/platform-server/src/server_renderer.ts +++ b/modules/@angular/platform-server/src/server_renderer.ts @@ -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(); + 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); + } + (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; + } +} diff --git a/modules/benchmarks/src/tree/ng2_next/tree.ts b/modules/benchmarks/src/tree/ng2_next/tree.ts index 3185753f3a..4263cb2fac 100644 --- a/modules/benchmarks/src/tree/ng2_next/tree.ts +++ b/modules/benchmarks/src/tree/ng2_next/tree.ts @@ -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; - private rendererV2: RendererV2; + private rendererV2: RendererFactoryV2; componentRef: ComponentRef; 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; diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index fa9cae6ef8..ec6610ea32 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -292,6 +292,16 @@ export declare abstract class ComponentRef { 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; +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 */