From 0c600cf6e31b7f69f5a36f7dea959e4884217a4d Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 13 Apr 2016 17:05:17 -0700 Subject: [PATCH] refactor(core): introduce ComponentFactory. Each compile template now exposes a `NgFactory` variable with an instance of a `ComponentFactory`. Calling `ComponentFactory.create` returns a `ComponentRef` that can be used directly. BREAKING CHANGE: - `Compiler` is renamed to `ComponentResolver`, `Compiler.compileInHost` has been renamed to `ComponentResolver.resolveComponent`. - `ComponentRef.dispose` is renamed to `ComponentRef.destroy` - `ViewContainerRef.createHostView` is renamed to `ViewContainerRef.createComponent` - `ComponentFixture_` has been removed, the class `ComponentFixture` can now be created directly as it is no more using private APIs. --- modules/angular2/src/compiler/compiler.ts | 5 +- .../angular2/src/compiler/offline_compiler.ts | 35 ++--- .../src/compiler/output/interpretive_view.ts | 7 +- .../angular2/src/compiler/runtime_compiler.ts | 20 +-- .../src/compiler/view_compiler/test.js | 77 ---------- .../compiler/view_compiler/view_builder.ts | 29 ++-- .../src/core/application_common_providers.ts | 6 +- modules/angular2/src/core/application_ref.ts | 15 +- .../angular2/src/core/debug/debug_renderer.ts | 4 +- modules/angular2/src/core/di/decorators.ts | 25 ++-- modules/angular2/src/core/linker.ts | 6 +- modules/angular2/src/core/linker/compiler.ts | 40 ------ .../src/core/linker/component_factory.ts | 93 ++++++++++++ .../src/core/linker/component_resolver.ts | 33 +++++ .../core/linker/dynamic_component_loader.ts | 133 +++--------------- modules/angular2/src/core/linker/element.ts | 4 +- .../angular2/src/core/linker/element_ref.ts | 9 +- .../angular2/src/core/linker/template_ref.ts | 7 +- modules/angular2/src/core/linker/view.ts | 71 +++++++--- .../src/core/linker/view_container_ref.ts | 58 +++----- .../angular2/src/core/linker/view_manager.ts | 120 ---------------- modules/angular2/src/core/linker/view_ref.ts | 35 ++--- modules/angular2/src/core/metadata.ts | 67 ++++----- modules/angular2/src/core/render/api.ts | 2 +- .../angular2/src/mock/mock_application_ref.ts | 2 +- .../platform/browser/tools/common_tools.ts | 6 +- .../src/platform/browser/tools/tools.dart | 2 +- .../src/platform/browser/tools/tools.ts | 2 +- .../angular2/src/platform/dom/dom_renderer.ts | 16 ++- .../src/router/directives/router_outlet.ts | 2 +- .../src/testing/test_component_builder.ts | 53 +++---- modules/angular2/src/upgrade/constants.ts | 2 +- .../src/upgrade/downgrade_ng2_adapter.ts | 19 ++- .../angular2/src/upgrade/upgrade_adapter.ts | 45 +++--- .../src/web_workers/worker/renderer.ts | 4 +- .../offline_compiler_codegen_typed.ts | 4 +- .../offline_compiler_codegen_untyped.ts | 4 +- .../test/compiler/offline_compiler_spec.ts | 43 ++---- .../test/core/linker/compiler_spec.ts | 51 ------- .../linker/dynamic_component_loader_spec.ts | 13 +- .../test/core/linker/integration_spec.ts | 10 +- .../reflector_component_resolver_spec.ts | 47 +++++++ .../test/platform/browser/bootstrap_spec.ts | 10 +- .../test/platform/browser/tools/spies.dart | 6 +- .../test/platform/browser/tools/spies.ts | 1 - modules/angular2/test/public_api_spec.ts | 5 +- .../test/router/integration/bootstrap_spec.ts | 4 +- .../integration/impl/fixture_components.ts | 9 +- .../router/route_config/route_config_spec.ts | 24 ++-- .../src/compiler/compiler_benchmark.ts | 10 +- modules/benchmarks/src/costs/index.ts | 2 +- .../src/largetable/largetable_benchmark.ts | 2 +- .../src/static_tree/tree_benchmark.ts | 15 +- modules/benchmarks/src/tree/tree_benchmark.ts | 3 +- .../template_compiler/generator.dart | 2 +- .../deferred_files/expected/bar.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../expected/foo.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../expected/bar.template.dart | 2 +- .../two_deps_files/expected/bar.template.dart | 2 +- .../template_compiler/all_tests.dart | 10 +- tools/public_api_guard/public_api_spec.ts | 113 +++++++-------- 66 files changed, 611 insertions(+), 849 deletions(-) delete mode 100644 modules/angular2/src/compiler/view_compiler/test.js delete mode 100644 modules/angular2/src/core/linker/compiler.ts create mode 100644 modules/angular2/src/core/linker/component_factory.ts create mode 100644 modules/angular2/src/core/linker/component_resolver.ts delete mode 100644 modules/angular2/test/core/linker/compiler_spec.ts create mode 100644 modules/angular2/test/core/linker/reflector_component_resolver_spec.ts diff --git a/modules/angular2/src/compiler/compiler.ts b/modules/angular2/src/compiler/compiler.ts index 644c76d969..f063868a61 100644 --- a/modules/angular2/src/compiler/compiler.ts +++ b/modules/angular2/src/compiler/compiler.ts @@ -4,6 +4,7 @@ export {TEMPLATE_TRANSFORMS} from 'angular2/src/compiler/template_parser'; export {CompilerConfig, RenderTypes} from './config'; export * from './compile_metadata'; export * from './offline_compiler'; +export {RuntimeCompiler} from './runtime_compiler'; export * from 'angular2/src/compiler/url_resolver'; export * from 'angular2/src/compiler/xhr'; @@ -20,7 +21,7 @@ import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata'; import {StyleCompiler} from 'angular2/src/compiler/style_compiler'; import {ViewCompiler} from 'angular2/src/compiler/view_compiler/view_compiler'; import {CompilerConfig} from './config'; -import {Compiler} from 'angular2/src/core/linker/compiler'; +import {ComponentResolver} from 'angular2/src/core/linker/component_resolver'; import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler'; import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry'; import {DomElementSchemaRegistry} from 'angular2/src/compiler/schema/dom_element_schema_registry'; @@ -51,7 +52,7 @@ export const COMPILER_PROVIDERS: Array = CONST_EXPR([ ViewCompiler, new Provider(CompilerConfig, {useFactory: _createCompilerConfig, deps: []}), RuntimeCompiler, - new Provider(Compiler, {useExisting: RuntimeCompiler}), + new Provider(ComponentResolver, {useExisting: RuntimeCompiler}), DomElementSchemaRegistry, new Provider(ElementSchemaRegistry, {useExisting: DomElementSchemaRegistry}), UrlResolver, diff --git a/modules/angular2/src/compiler/offline_compiler.ts b/modules/angular2/src/compiler/offline_compiler.ts index a7e14ce292..de6e5c175a 100644 --- a/modules/angular2/src/compiler/offline_compiler.ts +++ b/modules/angular2/src/compiler/offline_compiler.ts @@ -13,16 +13,16 @@ import {TemplateParser} from './template_parser'; import {DirectiveNormalizer} from './directive_normalizer'; import {OutputEmitter} from './output/abstract_emitter'; import * as o from './output/output_ast'; -import {HostViewFactory} from 'angular2/src/core/linker/view'; +import {ComponentFactory} from 'angular2/src/core/linker/component_factory'; import { MODULE_SUFFIX, } from './util'; -var _HOST_VIEW_FACTORY_IDENTIFIER = new CompileIdentifierMetadata({ - name: 'HostViewFactory', - runtime: HostViewFactory, - moduleUrl: `asset:angular2/lib/src/core/linker/view${MODULE_SUFFIX}` +var _COMPONENT_FACTORY_IDENTIFIER = new CompileIdentifierMetadata({ + name: 'ComponentFactory', + runtime: ComponentFactory, + moduleUrl: `asset:angular2/lib/src/core/linker/component_factory${MODULE_SUFFIX}` }); export class SourceModule { @@ -59,17 +59,20 @@ export class OfflineCompiler { exportedVars.push(compViewFactoryVar); var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector); - var compHostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], statements); - var hostViewFactoryVar = `hostViewFactory_${compMeta.type.name}`; - statements.push( - o.variable(hostViewFactoryVar) - .set(o.importExpr(_HOST_VIEW_FACTORY_IDENTIFIER) - .instantiate( - [o.literal(compMeta.selector), o.variable(compHostViewFactoryVar)], - o.importType(_HOST_VIEW_FACTORY_IDENTIFIER, null, - [o.TypeModifier.Const]))) - .toDeclStmt(null, [o.StmtModifier.Final])); - exportedVars.push(hostViewFactoryVar); + var hostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], statements); + var compFactoryVar = `${compMeta.type.name}NgFactory`; + statements.push(o.variable(compFactoryVar) + .set(o.importExpr(_COMPONENT_FACTORY_IDENTIFIER) + .instantiate( + [ + o.literal(compMeta.selector), + o.variable(hostViewFactoryVar), + o.importExpr(compMeta.type) + ], + o.importType(_COMPONENT_FACTORY_IDENTIFIER, null, + [o.TypeModifier.Const]))) + .toDeclStmt(null, [o.StmtModifier.Final])); + exportedVars.push(compFactoryVar); }); return this._codegenSourceModule(moduleUrl, statements, exportedVars); } diff --git a/modules/angular2/src/compiler/output/interpretive_view.ts b/modules/angular2/src/compiler/output/interpretive_view.ts index d74eab72a0..26b61766eb 100644 --- a/modules/angular2/src/compiler/output/interpretive_view.ts +++ b/modules/angular2/src/compiler/output/interpretive_view.ts @@ -1,5 +1,6 @@ import {isPresent} from 'angular2/src/facade/lang'; import {AppView} from 'angular2/src/core/linker/view'; +import {AppElement} from 'angular2/src/core/linker/element'; import {BaseException} from 'angular2/src/facade/exceptions'; import {InstanceFactory, DynamicInstance} from './output_interpreter'; @@ -19,12 +20,12 @@ class _InterpretiveAppView extends AppView implements DynamicInstance { super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); } - createInternal(rootSelector: string): void { + createInternal(rootSelector: string | any): AppElement { var m = this.methods.get('createInternal'); if (isPresent(m)) { - m(rootSelector); + return m(rootSelector); } else { - super.createInternal(rootSelector); + return super.createInternal(rootSelector); } } injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any { diff --git a/modules/angular2/src/compiler/runtime_compiler.ts b/modules/angular2/src/compiler/runtime_compiler.ts index bcb2f42ec0..299d29b82d 100644 --- a/modules/angular2/src/compiler/runtime_compiler.ts +++ b/modules/angular2/src/compiler/runtime_compiler.ts @@ -46,9 +46,11 @@ import {ViewCompiler} from './view_compiler/view_compiler'; import {TemplateParser} from './template_parser'; import {DirectiveNormalizer} from './directive_normalizer'; import {RuntimeMetadataResolver} from './runtime_metadata'; -import {HostViewFactory} from 'angular2/src/core/linker/view'; -import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref'; -import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler'; +import {ComponentFactory} from 'angular2/src/core/linker/component_factory'; +import { + ComponentResolver, + ReflectorComponentResolver +} from 'angular2/src/core/linker/component_resolver'; import {CompilerConfig} from './config'; import * as ir from './output/output_ast'; @@ -64,7 +66,7 @@ import {XHR} from 'angular2/src/compiler/xhr'; * ready for linking into an application. */ @Injectable() -export class RuntimeCompiler extends Compiler_ { +export class RuntimeCompiler implements ComponentResolver { private _styleCache: Map> = new Map>(); private _hostCacheKeys = new Map(); private _compiledTemplateCache = new Map(); @@ -74,11 +76,9 @@ export class RuntimeCompiler extends Compiler_ { private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _xhr: XHR, - private _genConfig: CompilerConfig) { - super(); - } + private _genConfig: CompilerConfig) {} - compileInHost(componentType: Type): Promise { + resolveComponent(componentType: Type): Promise { var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getDirectiveMetadata(componentType); var hostCacheKey = this._hostCacheKeys.get(componentType); @@ -92,8 +92,8 @@ export class RuntimeCompiler extends Compiler_ { this._loadAndCompileComponent(hostCacheKey, hostMeta, [compMeta], [], []); } return this._compiledTemplateDone.get(hostCacheKey) - .then((compiledTemplate: CompiledTemplate) => new HostViewFactoryRef_( - new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory))); + .then((compiledTemplate: CompiledTemplate) => new ComponentFactory( + compMeta.selector, compiledTemplate.viewFactory, componentType)); } clearCache() { diff --git a/modules/angular2/src/compiler/view_compiler/test.js b/modules/angular2/src/compiler/view_compiler/test.js deleted file mode 100644 index 0ec0517202..0000000000 --- a/modules/angular2/src/compiler/view_compiler/test.js +++ /dev/null @@ -1,77 +0,0 @@ -var nodeDebugInfos_MyComp1 = [ - new jit_StaticNodeDebugInfo0([],null,{}), - new jit_StaticNodeDebugInfo0([],null,{}) -] -; -function _View_MyComp1(viewManager,renderer,parentInjector,declarationEl,projectableNodes) { - var self = this; - jit_AppView1.call(this, './MyComp',_View_MyComp1,jit_ViewType_EMBEDDED2,{'some-tmpl': null},renderer,viewManager,parentInjector,projectableNodes,declarationEl,jit_ChangeDetectionStrategy_Default3,nodeDebugInfos_MyComp1); -} -_View_MyComp1.prototype = Object.create(jit_AppView1.prototype); -_View_MyComp1.prototype.createInternal = function(rootSelector) { - var self = this; - self._el_0 = self.renderer.createElement(null,'copy-me',self.debug(0,0,49)); - self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,58)); - self._expr_0 = jit_uninitialized4; - self.init([].concat([self._el_0]),[ - self._el_0, - self._text_1 - ] - ,{},[],[]); -}; -_View_MyComp1.prototype.detectChangesInternal = function(throwOnChange) { - var self = this; - var currVal = null; - self.debug(1,0,58); - currVal = jit_interpolate5(1,'',self.locals['some-tmpl'],''); - if (jit_checkBinding6(throwOnChange,self._expr_0,currVal)) { - self.renderer.setText(self._text_1,currVal); - self._expr_0 = currVal; - } - self.detectContentChildrenChanges(throwOnChange); } - self.detectViewChildrenChanges(throwOnChange); } -}; -function viewFactory_MyComp1(viewManager,parentInjector,declarationEl,projectableNodes,rootSelector) { - projectableNodes = jit_ensureSlotCount7(projectableNodes,0); - var renderer = declarationEl.parentView.renderer; - var view = new _View_MyComp1(viewManager,renderer,parentInjector,declarationEl,projectableNodes); - view.create(rootSelector); - return view; -} - var nodeDebugInfos_MyComp0 = [new jit_StaticNodeDebugInfo0([ - jit_TemplateRef8, - jit_SomeViewport9 - ] -,null,{})]; -var renderType_MyComp = null; -function _View_MyComp0(viewManager,renderer,parentInjector,declarationEl,projectableNodes) { - var self = this; - jit_AppView1.call(this, './MyComp',_View_MyComp0,jit_ViewType_COMPONENT10,{},renderer,viewManager,parentInjector,projectableNodes,declarationEl,jit_ChangeDetectionStrategy_Default3,nodeDebugInfos_MyComp0); -} -_View_MyComp0.prototype = Object.create(jit_AppView1.prototype); -_View_MyComp0.prototype.createInternal = function(rootSelector) { - var self = this; - var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement); - self._anchor_0 = self.renderer.createTemplateAnchor(parentRenderNode,self.debug(0,0,0)); - self.debug(null,null,null); - self._appEl_0 = new jit_AppElement11(0,null,self,self._anchor_0); - self._TemplateRef_0_0 = new jit_TemplateRef_12(self._appEl_0,viewFactory_MyComp1); - self._SomeViewport_0_1 = new jit_SomeViewport9(self._appEl_0.vcRef,self._TemplateRef_0_0); - self.init([],[self._anchor_0],{},[],[]); -}; -_View_MyComp0.prototype.injectorGetInternal = function(token,requestNodeIndex,notFoundResult) { - var self = this; - if (((token === jit_TemplateRef8) && (0 === requestNodeIndex))) { return self._TemplateRef_0_0; } - if (((token === jit_SomeViewport9) && (0 === requestNodeIndex))) { return self._SomeViewport_0_1; } - return notFoundResult; -}; -function viewFactory_MyComp0(viewManager,parentInjector,declarationEl,projectableNodes,rootSelector) { - if ((renderType_MyComp === null)) { (renderType_MyComp = viewManager.createRenderComponentType(jit_ViewType_EMBEDDED2,jit_undefined13)); } - projectableNodes = jit_ensureSlotCount7(projectableNodes,0); - var renderer = viewManager.renderComponent(renderType_MyComp); - var view = new _View_MyComp0(viewManager,renderer,parentInjector,declarationEl,projectableNodes); - view.create(rootSelector); - return view; -} -return viewFactory_MyComp0 -//# sourceURL=MyComp.template.js \ No newline at end of file diff --git a/modules/angular2/src/compiler/view_compiler/view_builder.ts b/modules/angular2/src/compiler/view_compiler/view_builder.ts index 53ba1c41e3..253a28f9c7 100644 --- a/modules/angular2/src/compiler/view_compiler/view_builder.ts +++ b/modules/angular2/src/compiler/view_compiler/view_builder.ts @@ -43,7 +43,6 @@ import {getViewFactoryName, createFlatArray, createDiTokenExpression} from './ut import {ViewType} from 'angular2/src/core/linker/view_type'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; -import {HOST_VIEW_ELEMENT_NAME} from 'angular2/src/core/linker/view'; import { CompileIdentifierMetadata, @@ -185,17 +184,13 @@ class ViewBuilderVisitor implements TemplateAstVisitor { var nodeIndex = this.view.nodes.length; var createRenderNodeExpr; var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast); - var createElementExpr = ViewProperties.renderer.callMethod( - 'createElement', - [this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]); if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) { - createRenderNodeExpr = - rootSelectorVar.identical(o.NULL_EXPR) - .conditional(createElementExpr, - ViewProperties.renderer.callMethod('selectRootElement', - [rootSelectorVar, debugContextExpr])); + createRenderNodeExpr = o.THIS_EXPR.callMethod( + 'selectOrCreateHostElement', [o.literal(ast.name), rootSelectorVar, debugContextExpr]); } else { - createRenderNodeExpr = createElementExpr; + createRenderNodeExpr = ViewProperties.renderer.callMethod( + 'createElement', + [this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]); } var fieldName = `_el_${nodeIndex}`; this.view.fields.push( @@ -342,9 +337,6 @@ function _readHtmlAndDirectiveVariables(elementExportAsVars: VariableAst[], elementExportAsVars.forEach((varAst) => { variables[varAst.name] = isPresent(component) ? identifierToken(component.type) : null; }); - if (viewType === ViewType.HOST) { - variables[HOST_VIEW_ELEMENT_NAME] = null; - } return variables; } @@ -444,7 +436,7 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr, var viewMethods = [ new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)], - generateCreateMethod(view)), + generateCreateMethod(view), o.importType(Identifiers.AppElement)), new o.ClassMethod( 'injectorGetInternal', [ @@ -519,6 +511,12 @@ function generateCreateMethod(view: CompileView): o.Statement[] { .toDeclStmt(o.importType(view.genConfig.renderTypes.renderNode), [o.StmtModifier.Final]) ]; } + var resultExpr: o.Expression; + if (view.viewType === ViewType.HOST) { + resultExpr = (view.nodes[0]).getOrCreateAppElement(); + } else { + resultExpr = o.NULL_EXPR; + } return parentRenderNodeStmts.concat(view.createMethod.finish()) .concat([ o.THIS_EXPR.callMethod('init', @@ -529,7 +527,8 @@ function generateCreateMethod(view: CompileView): o.Statement[] { o.literalArr(view.disposables), o.literalArr(view.subscriptions) ]) - .toStmt() + .toStmt(), + new o.ReturnStatement(resultExpr) ]); } diff --git a/modules/angular2/src/core/application_common_providers.ts b/modules/angular2/src/core/application_common_providers.ts index f96c3d3cec..0785bfaa23 100644 --- a/modules/angular2/src/core/application_common_providers.ts +++ b/modules/angular2/src/core/application_common_providers.ts @@ -13,8 +13,8 @@ import { } from './change_detection/change_detection'; import {AppViewManager} from './linker/view_manager'; import {AppViewManager_} from "./linker/view_manager"; -import {Compiler} from './linker/compiler'; -import {Compiler_} from "./linker/compiler"; +import {ComponentResolver} from './linker/component_resolver'; +import {ReflectorComponentResolver} from "./linker/component_resolver"; import {DynamicComponentLoader} from './linker/dynamic_component_loader'; import {DynamicComponentLoader_} from "./linker/dynamic_component_loader"; @@ -25,7 +25,7 @@ var __unused: Type; // avoid unused import when Type union types are erased * application, regardless of the platform it runs onto. */ export const APPLICATION_COMMON_PROVIDERS: Array = CONST_EXPR([ - new Provider(Compiler, {useClass: Compiler_}), + new Provider(ComponentResolver, {useClass: ReflectorComponentResolver}), APP_ID_RANDOM_PROVIDER, new Provider(AppViewManager, {useClass: AppViewManager_}), new Provider(IterableDiffers, {useValue: defaultIterableDiffers}), diff --git a/modules/angular2/src/core/application_ref.ts b/modules/angular2/src/core/application_ref.ts index 075234b48a..240788a207 100644 --- a/modules/angular2/src/core/application_ref.ts +++ b/modules/angular2/src/core/application_ref.ts @@ -18,10 +18,8 @@ import { import {PromiseWrapper, PromiseCompleter, ObservableWrapper} from 'angular2/src/facade/async'; import {ListWrapper} from 'angular2/src/facade/collection'; import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; -import { - ComponentRef, - DynamicComponentLoader -} from 'angular2/src/core/linker/dynamic_component_loader'; +import {DynamicComponentLoader} from 'angular2/src/core/linker/dynamic_component_loader'; +import {ComponentRef} from 'angular2/src/core/linker/component_factory'; import { BaseException, WrappedException, @@ -32,7 +30,6 @@ import {Console} from 'angular2/src/core/console'; import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile'; import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref'; import {lockMode} from 'angular2/src/facade/lang'; -import {ElementRef_} from 'angular2/src/core/linker/element_ref'; /** * Construct providers specific to an individual root component. @@ -457,8 +454,7 @@ export class ApplicationRef_ extends ApplicationRef { /** @internal */ _loadComponent(componentRef: ComponentRef): void { - var appChangeDetector = (componentRef.location).internalElement.parentView; - this._changeDetectorRefs.push(appChangeDetector.ref); + this._changeDetectorRefs.push(componentRef.changeDetectorRef); this.tick(); this._rootComponents.push(componentRef); this._bootstrapListeners.forEach((listener) => listener(componentRef)); @@ -469,8 +465,7 @@ export class ApplicationRef_ extends ApplicationRef { if (!ListWrapper.contains(this._rootComponents, componentRef)) { return; } - this.unregisterChangeDetector( - (componentRef.location).internalElement.parentView.ref); + this.unregisterChangeDetector(componentRef.changeDetectorRef); ListWrapper.remove(this._rootComponents, componentRef); } @@ -498,7 +493,7 @@ export class ApplicationRef_ extends ApplicationRef { dispose(): void { // TODO(alxhub): Dispose of the NgZone. - ListWrapper.clone(this._rootComponents).forEach((ref) => ref.dispose()); + ListWrapper.clone(this._rootComponents).forEach((ref) => ref.destroy()); this._disposeListeners.forEach((dispose) => dispose()); this._platform._applicationDisposed(this); } diff --git a/modules/angular2/src/core/debug/debug_renderer.ts b/modules/angular2/src/core/debug/debug_renderer.ts index d0f65e4d56..5af73dd7ba 100644 --- a/modules/angular2/src/core/debug/debug_renderer.ts +++ b/modules/angular2/src/core/debug/debug_renderer.ts @@ -25,8 +25,8 @@ export class DebugDomRootRenderer implements RootRenderer { export class DebugDomRenderer implements Renderer { constructor(private _delegate: Renderer) {} - selectRootElement(selector: string, debugInfo: RenderDebugInfo): any { - var nativeEl = this._delegate.selectRootElement(selector, debugInfo); + selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo): any { + var nativeEl = this._delegate.selectRootElement(selectorOrNode, debugInfo); var debugEl = new DebugElement(nativeEl, null, debugInfo); indexDebugNode(debugEl); return nativeEl; diff --git a/modules/angular2/src/core/di/decorators.ts b/modules/angular2/src/core/di/decorators.ts index 01126266b9..483c10ef6e 100644 --- a/modules/angular2/src/core/di/decorators.ts +++ b/modules/angular2/src/core/di/decorators.ts @@ -11,7 +11,7 @@ import {makeDecorator, makeParamDecorator} from '../util/decorators'; /** * Factory for creating {@link InjectMetadata}. */ -export interface InjectFactory { +export interface InjectMetadataFactory { (token: any): any; new (token: any): InjectMetadata; } @@ -19,7 +19,7 @@ export interface InjectFactory { /** * Factory for creating {@link OptionalMetadata}. */ -export interface OptionalFactory { +export interface OptionalMetadataFactory { (): any; new (): OptionalMetadata; } @@ -27,7 +27,7 @@ export interface OptionalFactory { /** * Factory for creating {@link InjectableMetadata}. */ -export interface InjectableFactory { +export interface InjectableMetadataFactory { (): any; new (): InjectableMetadata; } @@ -35,7 +35,7 @@ export interface InjectableFactory { /** * Factory for creating {@link SelfMetadata}. */ -export interface SelfFactory { +export interface SelfMetadataFactory { (): any; new (): SelfMetadata; } @@ -43,7 +43,7 @@ export interface SelfFactory { /** * Factory for creating {@link HostMetadata}. */ -export interface HostFactory { +export interface HostMetadataFactory { (): any; new (): HostMetadata; } @@ -51,7 +51,7 @@ export interface HostFactory { /** * Factory for creating {@link SkipSelfMetadata}. */ -export interface SkipSelfFactory { +export interface SkipSelfMetadataFactory { (): any; new (): SkipSelfMetadata; } @@ -59,29 +59,30 @@ export interface SkipSelfFactory { /** * Factory for creating {@link InjectMetadata}. */ -export var Inject: InjectFactory = makeParamDecorator(InjectMetadata); +export var Inject: InjectMetadataFactory = makeParamDecorator(InjectMetadata); /** * Factory for creating {@link OptionalMetadata}. */ -export var Optional: OptionalFactory = makeParamDecorator(OptionalMetadata); +export var Optional: OptionalMetadataFactory = makeParamDecorator(OptionalMetadata); /** * Factory for creating {@link InjectableMetadata}. */ -export var Injectable: InjectableFactory = makeDecorator(InjectableMetadata); +export var Injectable: InjectableMetadataFactory = + makeDecorator(InjectableMetadata); /** * Factory for creating {@link SelfMetadata}. */ -export var Self: SelfFactory = makeParamDecorator(SelfMetadata); +export var Self: SelfMetadataFactory = makeParamDecorator(SelfMetadata); /** * Factory for creating {@link HostMetadata}. */ -export var Host: HostFactory = makeParamDecorator(HostMetadata); +export var Host: HostMetadataFactory = makeParamDecorator(HostMetadata); /** * Factory for creating {@link SkipSelfMetadata}. */ -export var SkipSelf: SkipSelfFactory = makeParamDecorator(SkipSelfMetadata); \ No newline at end of file +export var SkipSelf: SkipSelfMetadataFactory = makeParamDecorator(SkipSelfMetadata); \ No newline at end of file diff --git a/modules/angular2/src/core/linker.ts b/modules/angular2/src/core/linker.ts index be9fede01c..dba50ae7bb 100644 --- a/modules/angular2/src/core/linker.ts +++ b/modules/angular2/src/core/linker.ts @@ -1,11 +1,11 @@ // Public API for compiler -export {Compiler} from './linker/compiler'; +export {ComponentResolver} from './linker/component_resolver'; export {AppViewManager} from './linker/view_manager'; export {QueryList} from './linker/query_list'; export {DynamicComponentLoader} from './linker/dynamic_component_loader'; export {ElementRef} from './linker/element_ref'; export {TemplateRef} from './linker/template_ref'; -export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref'; +export {EmbeddedViewRef, ViewRef} from './linker/view_ref'; export {ViewContainerRef} from './linker/view_container_ref'; -export {ComponentRef} from './linker/dynamic_component_loader'; +export {ComponentRef, ComponentFactory} from './linker/component_factory'; export {ExpressionChangedAfterItHasBeenCheckedException} from './linker/exceptions'; \ No newline at end of file diff --git a/modules/angular2/src/core/linker/compiler.ts b/modules/angular2/src/core/linker/compiler.ts deleted file mode 100644 index 6eefc88836..0000000000 --- a/modules/angular2/src/core/linker/compiler.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref'; - -import {Injectable} from 'angular2/src/core/di'; -import {Type, isBlank, stringify} from 'angular2/src/facade/lang'; -import {BaseException} from 'angular2/src/facade/exceptions'; -import {PromiseWrapper} from 'angular2/src/facade/async'; -import {reflector} from 'angular2/src/core/reflection/reflection'; -import {HostViewFactory} from 'angular2/src/core/linker/view'; -import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref'; - -/** - * Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which - * can later be used to create and render a Component instance. - * - * Most applications should instead use higher-level {@link DynamicComponentLoader} service, which - * both compiles and instantiates a Component. - */ -export abstract class Compiler { - abstract compileInHost(componentType: Type): Promise; - abstract clearCache(); -} - -function isHostViewFactory(type: any): boolean { - return type instanceof HostViewFactory; -} - -@Injectable() -export class Compiler_ extends Compiler { - compileInHost(componentType: Type): Promise { - var metadatas = reflector.annotations(componentType); - var hostViewFactory = metadatas.find(isHostViewFactory); - - if (isBlank(hostViewFactory)) { - throw new BaseException(`No precompiled component ${stringify(componentType)} found`); - } - return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory)); - } - - clearCache() {} -} diff --git a/modules/angular2/src/core/linker/component_factory.ts b/modules/angular2/src/core/linker/component_factory.ts new file mode 100644 index 0000000000..1f2e37632a --- /dev/null +++ b/modules/angular2/src/core/linker/component_factory.ts @@ -0,0 +1,93 @@ +import {Injector} from 'angular2/src/core/di'; +import {Type, CONST, isPresent, isBlank} from 'angular2/src/facade/lang'; +import {unimplemented} from 'angular2/src/facade/exceptions'; +import {ElementRef, ElementRef_} from './element_ref'; +import {ViewRef, ViewRef_} from './view_ref'; +import {AppElement} from './element'; +import {AppViewManager} from './view_manager'; +import {ChangeDetectorRef} from '../change_detection/change_detection'; + +/** + * Represents an instance of a Component created via a {@link ComponentFactory}. + * + * `ComponentRef` provides access to the Component Instance as well other objects related to this + * Component Instance and allows you to destroy the Component Instance via the {@link #destroy} + * method. + */ +export abstract class ComponentRef { + /** + * Location of the Host Element of this Component Instance. + */ + get location(): ElementRef { return unimplemented(); } + + /** + * The injector on which the component instance exists. + */ + get injector(): Injector { return unimplemented(); } + + /** + * The instance of the Component. + */ + get instance(): any { return unimplemented(); }; + + /** + * The {@link ViewRef} of the Host View of this Component instance. + */ + get hostView(): ViewRef { return unimplemented(); }; + + /** + * The {@link ChangeDetectorRef} of the Component instance. + */ + get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }; + + /** + * The component type. + */ + get componentType(): Type { return unimplemented(); } + + /** + * Destroys the component instance and all of the data structures associated with it. + */ + abstract destroy(): void; + + /** + * Allows to register a callback that will be called when the component is destroyed. + */ + abstract onDestroy(callback: Function): void; +} + +export class ComponentRef_ extends ComponentRef { + constructor(private _location: AppElement, private _componentType: Type) { super(); } + get location(): ElementRef { return this._location.ref; } + get injector(): Injector { return this._location.injector; } + get instance(): any { return this._location.component; }; + get hostView(): ViewRef { return this._location.parentView.ref; }; + get changeDetectorRef(): ChangeDetectorRef { return this.hostView; }; + get componentType(): Type { return this._componentType; } + + destroy(): void { this._location.parentView.destroy(); } + onDestroy(callback: Function): void { this.hostView.onDestroy(callback); } +} + +@CONST() +export class ComponentFactory { + constructor(public selector: string, private _viewFactory: Function, + private _componentType: Type) {} + + get componentType(): Type { return this._componentType; } + + /** + * Creates a new component. + */ + create(injector: Injector, projectableNodes: any[][] = null, + rootSelectorOrNode: string | any = null): ComponentRef { + var vm: AppViewManager = injector.get(AppViewManager); + if (isBlank(projectableNodes)) { + projectableNodes = []; + } + // Note: Host views don't need a declarationAppElement! + var hostView = this._viewFactory(vm, injector, null); + var hostElement = hostView.create(projectableNodes, rootSelectorOrNode); + return new ComponentRef_(hostElement, this._componentType); + } +} diff --git a/modules/angular2/src/core/linker/component_resolver.ts b/modules/angular2/src/core/linker/component_resolver.ts new file mode 100644 index 0000000000..f7f536756d --- /dev/null +++ b/modules/angular2/src/core/linker/component_resolver.ts @@ -0,0 +1,33 @@ +import {Injectable} from 'angular2/src/core/di'; +import {Type, isBlank, stringify} from 'angular2/src/facade/lang'; +import {BaseException} from 'angular2/src/facade/exceptions'; +import {PromiseWrapper} from 'angular2/src/facade/async'; +import {reflector} from 'angular2/src/core/reflection/reflection'; +import {ComponentFactory} from './component_factory'; + +/** + * Low-level service for loading {@link ComponentFactory}s, which + * can later be used to create and render a Component instance. + */ +export abstract class ComponentResolver { + abstract resolveComponent(componentType: Type): Promise; + abstract clearCache(); +} + +function _isComponentFactory(type: any): boolean { + return type instanceof ComponentFactory; +} + +@Injectable() +export class ReflectorComponentResolver extends ComponentResolver { + resolveComponent(componentType: Type): Promise { + var metadatas = reflector.annotations(componentType); + var componentFactory = metadatas.find(_isComponentFactory); + + if (isBlank(componentFactory)) { + throw new BaseException(`No precompiled component ${stringify(componentType)} found`); + } + return PromiseWrapper.resolve(componentFactory); + } + clearCache() {} +} diff --git a/modules/angular2/src/core/linker/dynamic_component_loader.ts b/modules/angular2/src/core/linker/dynamic_component_loader.ts index 3a03fdf155..6d048f1a8d 100644 --- a/modules/angular2/src/core/linker/dynamic_component_loader.ts +++ b/modules/angular2/src/core/linker/dynamic_component_loader.ts @@ -1,91 +1,9 @@ import {Key, Injector, ResolvedProvider, Provider, provide, Injectable} from 'angular2/src/core/di'; -import {Compiler} from './compiler'; +import {ComponentResolver} from './component_resolver'; import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang'; import {AppViewManager} from 'angular2/src/core/linker/view_manager'; import {ElementRef, ElementRef_} from './element_ref'; -import {HostViewRef} from './view_ref'; - -/** - * Represents an instance of a Component created via {@link DynamicComponentLoader}. - * - * `ComponentRef` provides access to the Component Instance as well other objects related to this - * Component Instance and allows you to destroy the Component Instance via the {@link #dispose} - * method. - */ -export abstract class ComponentRef { - /** - * The injector provided {@link DynamicComponentLoader#loadAsRoot}. - * - * TODO(i): this api is useless and should be replaced by an injector retrieved from - * the HostElementRef, which is currently not possible. - */ - injector: Injector; - - /** - * Location of the Host Element of this Component Instance. - */ - location: ElementRef; - - /** - * The instance of the Component. - */ - instance: any; - - /** - * The user defined component type, represented via the constructor function. - * - * - */ - componentType: Type; - - /** - * The {@link ViewRef} of the Host View of this Component instance. - */ - get hostView(): HostViewRef { - return (this.location).internalElement.parentView.ref; - } - - /** - * @internal - * - * The instance of the component. - * - * TODO(i): this api should be removed - */ - get hostComponent(): any { return this.instance; } - - /** - * Destroys the component instance and all of the data structures associated with it. - * - * TODO(i): rename to destroy to be consistent with AppViewManager and ViewContainerRef - */ - abstract dispose(): void; -} - -export class ComponentRef_ extends ComponentRef { - /** - * TODO(i): refactor into public/private fields - */ - constructor(location: ElementRef, instance: any, componentType: Type, injector: Injector, - private _dispose: () => void) { - super(); - this.location = location; - this.instance = instance; - this.componentType = componentType; - this.injector = injector; - } - - /** - * @internal - * - * Returns the type of this Component instance. - * - * TODO(i): this api should be removed - */ - get hostComponentType(): Type { return this.componentType; } - - dispose(): void { this._dispose(); } -} +import {ComponentRef} from './component_factory'; /** * Service for instantiating a Component and attaching it to a View at a specified location. @@ -140,7 +58,7 @@ export abstract class DynamicComponentLoader { * * ``` */ - abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector, + abstract loadAsRoot(type: Type, overrideSelectorOrNode: string | any, injector: Injector, onDispose?: () => void, projectableNodes?: any[][]): Promise; /** @@ -240,23 +158,20 @@ export abstract class DynamicComponentLoader { @Injectable() export class DynamicComponentLoader_ extends DynamicComponentLoader { - constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); } + constructor(private _compiler: ComponentResolver, private _viewManager: AppViewManager) { + super(); + } - loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void, - projectableNodes?: any[][]): Promise { - return this._compiler.compileInHost(type).then(hostProtoViewRef => { - var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, - injector, projectableNodes); - var newLocation = this._viewManager.getHostElement(hostViewRef); - var component = this._viewManager.getComponent(newLocation); - - var dispose = () => { - if (isPresent(onDispose)) { - onDispose(); - } - this._viewManager.destroyRootHostView(hostViewRef); - }; - return new ComponentRef_(newLocation, component, type, injector, dispose); + loadAsRoot(type: Type, overrideSelectorOrNode: string | any, injector: Injector, + onDispose?: () => void, projectableNodes?: any[][]): Promise { + return this._compiler.resolveComponent(type).then(componentFactory => { + var componentRef = componentFactory.create( + injector, projectableNodes, + isPresent(overrideSelectorOrNode) ? overrideSelectorOrNode : componentFactory.selector); + if (isPresent(onDispose)) { + componentRef.onDestroy(onDispose); + } + return componentRef; }); } @@ -270,20 +185,10 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader { loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null, projectableNodes: any[][] = null): Promise { - return this._compiler.compileInHost(type).then(hostProtoViewRef => { + return this._compiler.resolveComponent(type).then(componentFactory => { var viewContainer = this._viewManager.getViewContainer(location); - var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length, - providers, projectableNodes); - var newLocation = this._viewManager.getHostElement(hostViewRef); - var component = this._viewManager.getComponent(newLocation); - - var dispose = () => { - var index = viewContainer.indexOf(hostViewRef); - if (!hostViewRef.destroyed && index !== -1) { - viewContainer.remove(index); - } - }; - return new ComponentRef_(newLocation, component, type, null, dispose); + return viewContainer.createComponent(componentFactory, viewContainer.length, providers, + projectableNodes); }); } } diff --git a/modules/angular2/src/core/linker/element.ts b/modules/angular2/src/core/linker/element.ts index a01361ec92..acc3ccadb9 100644 --- a/modules/angular2/src/core/linker/element.ts +++ b/modules/angular2/src/core/linker/element.ts @@ -81,7 +81,7 @@ export class AppElement { if (isPresent(refRenderNode)) { view.renderer.attachViewAfter(refRenderNode, view.flatRootNodes); } - this.parentView.addRenderContentChild(view); + view.addToContentChildren(this); } detachView(viewIndex: number): AppView { @@ -92,7 +92,7 @@ export class AppElement { view.renderer.detachView(view.flatRootNodes); - view.renderParent.removeContentChild(view); + view.removeFromContentChildren(this); return view; } } diff --git a/modules/angular2/src/core/linker/element_ref.ts b/modules/angular2/src/core/linker/element_ref.ts index ebef223d18..ff74a672a5 100644 --- a/modules/angular2/src/core/linker/element_ref.ts +++ b/modules/angular2/src/core/linker/element_ref.ts @@ -2,15 +2,14 @@ import {unimplemented} from 'angular2/src/facade/exceptions'; import {AppElement} from './element'; /** - * Represents a location in a View that has an injection, change-detection and render context - * associated with it. - * - * An `ElementRef` is created for each element in the Template that contains a Directive, Component - * or data-binding. + * A wrapper around a native element inside of a View. * * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM * element. */ +// Note: We don't expose things like `Injector`, `ViewContainer`, ... here, +// i.e. users have to ask for what they need. With that, we can build better analysis tools +// and could do better codegen in the future. export abstract class ElementRef { /** * The underlying native element or `null` if direct access to native elements is not supported diff --git a/modules/angular2/src/core/linker/template_ref.ts b/modules/angular2/src/core/linker/template_ref.ts index 814e97dd26..731de03f46 100644 --- a/modules/angular2/src/core/linker/template_ref.ts +++ b/modules/angular2/src/core/linker/template_ref.ts @@ -1,6 +1,7 @@ import {ElementRef, ElementRef_} from './element_ref'; import {AppElement} from './element'; import {AppView} from './view'; +import {EmbeddedViewRef} from './view_ref'; /** * Represents an Embedded Template that can be used to instantiate Embedded Views. @@ -28,16 +29,18 @@ export abstract class TemplateRef { */ // TODO(i): rename to anchor or location get elementRef(): ElementRef { return null; } + + abstract createEmbeddedView(): EmbeddedViewRef; } export class TemplateRef_ extends TemplateRef { constructor(private _appElement: AppElement, private _viewFactory: Function) { super(); } - createEmbeddedView(): AppView { + createEmbeddedView(): EmbeddedViewRef { var view: AppView = this._viewFactory(this._appElement.parentView.viewManager, this._appElement.parentInjector, this._appElement); view.create(null, null); - return view; + return view.ref; } get elementRef(): ElementRef { return this._appElement.ref; } diff --git a/modules/angular2/src/core/linker/view.ts b/modules/angular2/src/core/linker/view.ts index 5dfa45eb60..1c0bfe854e 100644 --- a/modules/angular2/src/core/linker/view.ts +++ b/modules/angular2/src/core/linker/view.ts @@ -19,12 +19,13 @@ import { CONST, CONST_EXPR, stringify, - isPrimitive + isPrimitive, + isString } from 'angular2/src/facade/lang'; import {ObservableWrapper} from 'angular2/src/facade/async'; import {Renderer, RootRenderer, RenderComponentType} from 'angular2/src/core/render/api'; -import {ViewRef_, HostViewFactoryRef} from './view_ref'; +import {ViewRef_} from './view_ref'; import {AppViewManager_, AppViewManager} from './view_manager'; import {ViewType} from './view_type'; @@ -50,8 +51,6 @@ import { import {StaticNodeDebugInfo, DebugContext} from './debug_context'; import {ElementInjector} from './element_injector'; -export const HOST_VIEW_ELEMENT_NAME = '$hostViewEl'; - const EMPTY_CONTEXT = CONST_EXPR(new Object()); var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`); @@ -70,6 +69,7 @@ export abstract class AppView { contentChildren: AppView[] = []; viewChildren: AppView[] = []; renderParent: AppView; + viewContainerElement: AppElement = null; private _literalArrayCache: any[][]; private _literalMapCache: Array<{[key: string]: any}>; @@ -92,6 +92,8 @@ export abstract class AppView { private _currentDebugContext: DebugContext = null; + private _hasExternalHostElement: boolean; + constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType, public locals: {[key: string]: any}, public viewManager: AppViewManager_, public parentInjector: Injector, public declarationAppElement: AppElement, @@ -107,7 +109,7 @@ export abstract class AppView { this._literalMapCache = ListWrapper.createFixedSize(literalMapCacheSize); } - create(givenProjectableNodes: Array, rootSelector: string) { + create(givenProjectableNodes: Array, rootSelectorOrNode: string | any): AppElement { var context; var projectableNodes; switch (this.type) { @@ -126,25 +128,26 @@ export abstract class AppView { projectableNodes = givenProjectableNodes; break; } + this._hasExternalHostElement = isPresent(rootSelectorOrNode); this.context = context; this.projectableNodes = projectableNodes; if (this.debugMode) { this._resetDebug(); try { - this.createInternal(rootSelector); + return this.createInternal(rootSelectorOrNode); } catch (e) { this._rethrowWithContext(e, e.stack); throw e; } } else { - this.createInternal(rootSelector); + return this.createInternal(rootSelectorOrNode); } } /** * Overwritten by implementations */ - createInternal(rootSelector: string): void {} + createInternal(rootSelectorOrNode: string | any): AppElement { return null; } init(rootNodesOrAppElements: any[], allNodes: any[], appElements: {[key: string]: AppElement}, disposables: Function[], subscriptions: any[]) { @@ -162,7 +165,16 @@ export abstract class AppView { } } - getHostViewElement(): AppElement { return this.namedAppElements[HOST_VIEW_ELEMENT_NAME]; } + selectOrCreateHostElement(elementName: string, rootSelectorOrNode: string | any, + debugCtx: DebugContext): any { + var hostElement; + if (isPresent(rootSelectorOrNode)) { + hostElement = this.renderer.selectRootElement(rootSelectorOrNode, debugCtx); + } else { + hostElement = this.renderer.createElement(null, elementName, debugCtx); + } + return hostElement; + } injectorGet(token: any, nodeIndex: number, notFoundResult: any): any { if (this.debugMode) { @@ -194,16 +206,25 @@ export abstract class AppView { } destroy() { + if (this._hasExternalHostElement) { + this.renderer.detachView(this.flatRootNodes); + } else if (isPresent(this.viewContainerElement)) { + this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this)); + } + this._destroyRecurse(); + } + + private _destroyRecurse() { if (this.destroyed) { return; } var children = this.contentChildren; for (var i = 0; i < children.length; i++) { - children[i].destroy(); + children[i]._destroyRecurse(); } children = this.viewChildren; for (var i = 0; i < children.length; i++) { - children[i].destroy(); + children[i]._destroyRecurse(); } if (this.debugMode) { this._resetDebug(); @@ -223,7 +244,6 @@ export abstract class AppView { private _destroyLocal() { var hostElement = this.type === ViewType.COMPONENT ? this.declarationAppElement.nativeElement : null; - this.renderer.destroyView(hostElement, this.allNodes); for (var i = 0; i < this.disposables.length; i++) { this.disposables[i](); } @@ -231,8 +251,14 @@ export abstract class AppView { ObservableWrapper.dispose(this.subscriptions[i]); } this.destroyInternal(); - - this.dirtyParentQueriesInternal(); + if (this._hasExternalHostElement) { + this.renderer.detachView(this.flatRootNodes); + } else if (isPresent(this.viewContainerElement)) { + this.viewContainerElement.detachView(this.viewContainerElement.nestedViews.indexOf(this)); + } else { + this.dirtyParentQueriesInternal(); + } + this.renderer.destroyView(hostElement, this.allNodes); } /** @@ -323,6 +349,18 @@ export abstract class AppView { } } + addToContentChildren(renderAppElement: AppElement): void { + renderAppElement.parentView.contentChildren.push(this); + this.viewContainerElement = renderAppElement; + this.dirtyParentQueriesInternal(); + } + + removeFromContentChildren(renderAppElement: AppElement): void { + ListWrapper.remove(renderAppElement.parentView.contentChildren, this); + this.dirtyParentQueriesInternal(); + this.viewContainerElement = null; + } + literalArray(id: number, value: any[]): any[] { var prevValue = this._literalArrayCache[id]; if (isBlank(value)) { @@ -393,11 +431,6 @@ export abstract class AppView { throwDestroyedError(details: string): void { throw new ViewDestroyedException(details); } } -@CONST() -export class HostViewFactory { - constructor(public selector: string, public viewFactory: Function) {} -} - function _findLastRenderNode(node: any): any { var lastNode; if (node instanceof AppElement) { diff --git a/modules/angular2/src/core/linker/view_container_ref.ts b/modules/angular2/src/core/linker/view_container_ref.ts index 93ba394a9c..fec19ee998 100644 --- a/modules/angular2/src/core/linker/view_container_ref.ts +++ b/modules/angular2/src/core/linker/view_container_ref.ts @@ -9,21 +9,14 @@ import {AppElement} from './element'; import {ElementRef, ElementRef_} from './element_ref'; import {TemplateRef, TemplateRef_} from './template_ref'; -import { - EmbeddedViewRef, - HostViewRef, - HostViewFactoryRef, - HostViewFactoryRef_, - ViewRef, - ViewRef_ -} from './view_ref'; -import {AppView} from './view'; +import {EmbeddedViewRef, ViewRef, ViewRef_} from './view_ref'; +import {ComponentFactory, ComponentRef} from './component_factory'; /** * Represents a container where one or more Views can be attached. * * The container can contain two kinds of Views. Host Views, created by instantiating a - * {@link Component} via {@link #createHostView}, and Embedded Views, created by instantiating an + * {@link Component} via {@link #createComponent}, and Embedded Views, created by instantiating an * {@link TemplateRef Embedded Template} via {@link #createEmbeddedView}. * * The location of the View Container within the containing View is specified by the Anchor @@ -83,11 +76,11 @@ export abstract class ViewContainerRef { * You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector} * that will be created for the Host View. * - * Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component. + * Returns the {@link ComponentRef} of the Host View created for the newly instantiated Component. */ - abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number, - dynamicallyCreatedProviders?: ResolvedProvider[], - projectableNodes?: any[][]): HostViewRef; + abstract createComponent(componentFactory: ComponentFactory, index?: number, + dynamicallyCreatedProviders?: ResolvedProvider[], + projectableNodes?: any[][]): ComponentRef; /** * Inserts a View identified by a {@link ViewRef} into the container at the specified `index`. @@ -130,45 +123,32 @@ export class ViewContainerRef_ implements ViewContainerRef { get element(): ElementRef { return this._element.ref; } - /** @internal */ - _createEmbeddedViewInContainerScope: WtfScopeFn = - wtfCreateScope('ViewContainerRef#createEmbeddedView()'); - // TODO(rado): profile and decide whether bounds checks should be added // to the methods below. createEmbeddedView(templateRef: TemplateRef, index: number = -1): EmbeddedViewRef { - var s = this._createEmbeddedViewInContainerScope(); - if (index == -1) index = this.length; - var templateRef_ = (templateRef); - var view: AppView = templateRef_.createEmbeddedView(); - this._element.attachView(view, index); - return wtfLeave(s, view.ref); + var viewRef: EmbeddedViewRef = templateRef.createEmbeddedView(); + this.insert(viewRef, index); + return viewRef; } /** @internal */ - _createHostViewInContainerScope: WtfScopeFn = wtfCreateScope('ViewContainerRef#createHostView()'); + _createComponentInContainerScope: WtfScopeFn = + wtfCreateScope('ViewContainerRef#createComponent()'); - createHostView(hostViewFactoryRef: HostViewFactoryRef, index: number = -1, - dynamicallyCreatedProviders: ResolvedProvider[] = null, - projectableNodes: any[][] = null): HostViewRef { - var s = this._createHostViewInContainerScope(); - if (index == -1) index = this.length; - var contextEl = this._element; + createComponent(componentFactory: ComponentFactory, index: number = -1, + dynamicallyCreatedProviders: ResolvedProvider[] = null, + projectableNodes: any[][] = null): ComponentRef { + var s = this._createComponentInContainerScope(); var contextInjector = this._element.parentInjector; - var hostViewFactory = (hostViewFactoryRef).internalHostViewFactory; - var childInjector = isPresent(dynamicallyCreatedProviders) && dynamicallyCreatedProviders.length > 0 ? new Injector_(ProtoInjector.fromResolvedProviders(dynamicallyCreatedProviders), contextInjector) : contextInjector; - - var view = - hostViewFactory.viewFactory(contextEl.parentView.viewManager, childInjector, contextEl); - view.create(projectableNodes, null); - this._element.attachView(view, index); - return wtfLeave(s, view.ref); + var componentRef = componentFactory.create(childInjector, projectableNodes); + this.insert(componentRef.hostView, index); + return wtfLeave(s, componentRef); } /** @internal */ diff --git a/modules/angular2/src/core/linker/view_manager.ts b/modules/angular2/src/core/linker/view_manager.ts index 4f36e00c68..3197049927 100644 --- a/modules/angular2/src/core/linker/view_manager.ts +++ b/modules/angular2/src/core/linker/view_manager.ts @@ -9,20 +9,10 @@ import { import {isPresent, isBlank, isArray, Type} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; import {ElementRef, ElementRef_} from './element_ref'; -import { - HostViewFactoryRef, - HostViewFactoryRef_, - EmbeddedViewRef, - HostViewRef, - ViewRef, - ViewRef_ -} from './view_ref'; import {ViewContainerRef, ViewContainerRef_} from './view_container_ref'; import {RootRenderer, RenderComponentType, Renderer} from 'angular2/src/core/render/api'; -import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; import {APP_ID} from 'angular2/src/core/application_tokens'; import {ViewEncapsulation} from 'angular2/src/core/metadata/view'; -import {ViewType} from './view_type'; /** * Service exposing low level API for creating, moving and destroying Views. @@ -36,11 +26,6 @@ export abstract class AppViewManager { */ abstract getViewContainer(location: ElementRef): ViewContainerRef; - /** - * Returns the {@link ElementRef} that makes up the specified Host View. - */ - abstract getHostElement(hostViewRef: HostViewRef): ElementRef; - /** * Searches the Component View of the Component specified via `hostLocation` and returns the * {@link ElementRef} for the Element identified via a Variable Name `variableName`. @@ -50,75 +35,6 @@ export abstract class AppViewManager { */ abstract getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef; - - /** - * Returns the component instance for the provided Host Element. - */ - abstract getComponent(hostLocation: ElementRef): any; - - /** - * Creates an instance of a Component and attaches it to the first element in the global View - * (usually DOM Document) that matches the component's selector or `overrideSelector`. - * - * This as a low-level way to bootstrap an application and upgrade an existing Element to a - * Host Element. Most applications should use {@link DynamicComponentLoader#loadAsRoot} instead. - * - * The Component and its View are created based on the `hostProtoComponentRef` which can be - * obtained - * by compiling the component with {@link Compiler#compileInHost}. - * - * Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host - * View. - * - * ### Example - * - * ``` - * @ng.Component({ - * selector: 'child-component' - * }) - * @ng.View({ - * template: 'Child' - * }) - * class ChildComponent { - * - * } - * - * @ng.Component({ - * selector: 'my-app' - * }) - * @ng.View({ - * template: ` - * Parent () - * ` - * }) - * class MyApp implements OnDestroy { - * viewRef: ng.ViewRef; - * - * constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) { - * compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoComponentRef) => { - * this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null); - * }) - * } - * - * ngOnDestroy() { - * this.appViewManager.destroyRootHostView(this.viewRef); - * this.viewRef = null; - * } - * } - * - * ng.bootstrap(MyApp); - * ``` - */ - abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string, - injector: Injector, projectableNodes?: any[][]): HostViewRef; - - /** - * Destroys the Host View created via {@link AppViewManager#createRootHostView}. - * - * Along with the Host View, the Component Instance as well as all nested View and Components are - * destroyed as well. - */ - abstract destroyRootHostView(hostViewRef: HostViewRef); } @Injectable() @@ -131,14 +47,6 @@ export class AppViewManager_ extends AppViewManager { return (location).internalElement.vcRef; } - getHostElement(hostViewRef: ViewRef): ElementRef { - var hostView = (hostViewRef).internalView; - if (hostView.type !== ViewType.HOST) { - throw new BaseException('This operation is only allowed on host views'); - } - return hostView.getHostViewElement().ref; - } - getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef { var appEl = (hostLocation).internalElement; var componentView = appEl.componentView; @@ -152,34 +60,6 @@ export class AppViewManager_ extends AppViewManager { throw new BaseException(`Could not find variable ${variableName}`); } - getComponent(hostLocation: ElementRef): any { - return (hostLocation).internalElement.component; - } - - /** @internal */ - _createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()'); - - createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string, - injector: Injector, projectableNodes: any[][] = null): HostViewRef { - var s = this._createRootHostViewScope(); - var hostViewFactory = (hostViewFactoryRef).internalHostViewFactory; - var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector; - var view = hostViewFactory.viewFactory(this, injector, null); - view.create(projectableNodes, selector); - return wtfLeave(s, view.ref); - } - - /** @internal */ - _destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()'); - - destroyRootHostView(hostViewRef: ViewRef) { - var s = this._destroyRootHostViewScope(); - var hostView = (hostViewRef).internalView; - hostView.renderer.detachView(hostView.flatRootNodes); - hostView.destroy(); - wtfLeave(s); - } - /** * Used by the generated code */ diff --git a/modules/angular2/src/core/linker/view_ref.ts b/modules/angular2/src/core/linker/view_ref.ts index 89dcf7a0ea..8b848aa5c2 100644 --- a/modules/angular2/src/core/linker/view_ref.ts +++ b/modules/angular2/src/core/linker/view_ref.ts @@ -1,6 +1,7 @@ import {unimplemented} from 'angular2/src/facade/exceptions'; +import {isPresent} from 'angular2/src/facade/lang'; import {ChangeDetectorRef} from '../change_detection/change_detector_ref'; -import {AppView, HostViewFactory} from './view'; +import {AppView} from './view'; import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants'; export abstract class ViewRef extends ChangeDetectorRef { @@ -10,19 +11,8 @@ export abstract class ViewRef extends ChangeDetectorRef { get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }; get destroyed(): boolean { return unimplemented(); } -} -/** - * Represents a View containing a single Element that is the Host Element of a {@link Component} - * instance. - * - * A Host View is created for every dynamically created Component that was compiled on its own (as - * opposed to as a part of another Component's Template) via {@link Compiler#compileInHost} or one - * of the higher-level APIs: {@link AppViewManager#createRootHostView}, - * {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}. - */ -export abstract class HostViewRef extends ViewRef { - get rootNodes(): any[] { return unimplemented(); }; + abstract onDestroy(callback: Function); } /** @@ -90,9 +80,14 @@ export abstract class EmbeddedViewRef extends ViewRef { abstract hasLocal(variableName: string): boolean; get rootNodes(): any[] { return unimplemented(); }; + + /** + * Destroys the view and all of the data structures associated with it. + */ + abstract destroy(); } -export class ViewRef_ implements EmbeddedViewRef, HostViewRef { +export class ViewRef_ implements EmbeddedViewRef { constructor(private _view: AppView) { this._view = _view; } get internalView(): AppView { return this._view; } @@ -118,12 +113,8 @@ export class ViewRef_ implements EmbeddedViewRef, HostViewRef { this._view.cdMode = ChangeDetectionStrategy.CheckAlways; this.markForCheck(); } -} - -export abstract class HostViewFactoryRef {} - -export class HostViewFactoryRef_ implements HostViewFactoryRef { - constructor(private _hostViewFactory: HostViewFactory) {} - - get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; } + + onDestroy(callback: Function) { this._view.disposables.push(callback); } + + destroy() { this._view.destroy(); } } diff --git a/modules/angular2/src/core/metadata.ts b/modules/angular2/src/core/metadata.ts index cebc0b15ba..a33e6b816f 100644 --- a/modules/angular2/src/core/metadata.ts +++ b/modules/angular2/src/core/metadata.ts @@ -146,7 +146,7 @@ export interface ViewDecorator extends TypeDecorator { * ] * ``` */ -export interface DirectiveFactory { +export interface DirectiveMetadataFactory { (obj: { selector?: string, inputs?: string[], @@ -204,7 +204,7 @@ export interface DirectiveFactory { * ] * ``` */ -export interface ComponentFactory { +export interface ComponentMetadataFactory { (obj: { selector?: string, inputs?: string[], @@ -298,7 +298,7 @@ export interface ComponentFactory { * ] * ``` */ -export interface ViewFactory { +export interface ViewMetadataFactory { (obj: { templateUrl?: string, template?: string, @@ -353,7 +353,7 @@ export interface ViewFactory { * ] * ``` */ -export interface AttributeFactory { +export interface AttributeMetadataFactory { (name: string): TypeDecorator; new (name: string): AttributeMetadata; } @@ -401,7 +401,7 @@ export interface AttributeFactory { * ] * ``` */ -export interface QueryFactory { +export interface QueryMetadataFactory { (selector: Type | string, {descendants}?: {descendants?: boolean}): ParameterDecorator; new (selector: Type | string, {descendants}?: {descendants?: boolean}): QueryMetadata; } @@ -409,7 +409,7 @@ export interface QueryFactory { /** * Factory for {@link ContentChildren}. */ -export interface ContentChildrenFactory { +export interface ContentChildrenMetadataFactory { (selector: Type | string, {descendants}?: {descendants?: boolean}): any; new (selector: Type | string, {descendants}?: {descendants?: boolean}): ContentChildrenMetadata; } @@ -417,15 +417,15 @@ export interface ContentChildrenFactory { /** * Factory for {@link ContentChild}. */ -export interface ContentChildFactory { +export interface ContentChildMetadataFactory { (selector: Type | string): any; - new (selector: Type | string): ContentChildFactory; + new (selector: Type | string): ContentChildMetadataFactory; } /** * Factory for {@link ViewChildren}. */ -export interface ViewChildrenFactory { +export interface ViewChildrenMetadataFactory { (selector: Type | string): any; new (selector: Type | string): ViewChildrenMetadata; } @@ -433,9 +433,9 @@ export interface ViewChildrenFactory { /** * Factory for {@link ViewChild}. */ -export interface ViewChildFactory { +export interface ViewChildMetadataFactory { (selector: Type | string): any; - new (selector: Type | string): ViewChildFactory; + new (selector: Type | string): ViewChildMetadataFactory; } @@ -446,7 +446,7 @@ export interface ViewChildFactory { * * {@example core/ts/metadata/metadata.ts region='pipe'} */ -export interface PipeFactory { +export interface PipeMetadataFactory { (obj: {name: string, pure?: boolean}): any; new (obj: {name: string, pure?: boolean}): any; } @@ -456,7 +456,7 @@ export interface PipeFactory { * * See {@link InputMetadata}. */ -export interface InputFactory { +export interface InputMetadataFactory { (bindingPropertyName?: string): any; new (bindingPropertyName?: string): any; } @@ -466,7 +466,7 @@ export interface InputFactory { * * See {@link OutputMetadata}. */ -export interface OutputFactory { +export interface OutputMetadataFactory { (bindingPropertyName?: string): any; new (bindingPropertyName?: string): any; } @@ -474,7 +474,7 @@ export interface OutputFactory { /** * {@link HostBindingMetadata} factory function. */ -export interface HostBindingFactory { +export interface HostBindingMetadataFactory { (hostPropertyName?: string): any; new (hostPropertyName?: string): any; } @@ -482,7 +482,7 @@ export interface HostBindingFactory { /** * {@link HostListenerMetadata} factory function. */ -export interface HostListenerFactory { +export interface HostListenerMetadataFactory { (eventName: string, args?: string[]): any; new (eventName: string, args?: string[]): any; } @@ -511,8 +511,8 @@ export interface HostListenerFactory { * * {@example core/ts/metadata/metadata.ts region='component'} */ -export var Component: ComponentFactory = - makeDecorator(ComponentMetadata, (fn: any) => fn.View = View); +export var Component: ComponentMetadataFactory = + makeDecorator(ComponentMetadata, (fn: any) => fn.View = View); // TODO(alexeagle): remove the duplication of this doc. It is copied from DirectiveMetadata. /** @@ -893,7 +893,8 @@ export var Component: ComponentFactory = * the instantiated * view occurs on the second `
  • ` which is a sibling to the `