diff --git a/modules/angular2/src/core/application.js b/modules/angular2/src/core/application.js index fd668ced1e..176689c50f 100644 --- a/modules/angular2/src/core/application.js +++ b/modules/angular2/src/core/application.js @@ -3,7 +3,6 @@ import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print, strin import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {DOM} from 'angular2/src/dom/dom_adapter'; import {Compiler, CompilerCache} from './compiler/compiler'; -import {ProtoView} from './compiler/view'; import {Reflector, reflector} from 'angular2/src/reflection/reflection'; import {Parser, Lexer, ChangeDetection, dynamicChangeDetection, jitChangeDetection} from 'angular2/change_detection'; import {ExceptionHandler} from './exception_handler'; @@ -72,12 +71,11 @@ function _injectorBindings(appComponentType): List { throw new BaseException(`Only Components can be bootstrapped; ` + `Directive of ${stringify(type)} is not a Component`); } - return compiler.compile(appComponentAnnotatedType.type).then( - (protoView) => { - var appProtoView = ProtoView.createRootProtoView(protoView, appElement, - DirectiveBinding.createFromType(appComponentAnnotatedType.type, appComponentAnnotatedType.annotation), - changeDetection.createProtoChangeDetector('root'), - strategy); + return compiler.compileRoot( + appElement, + DirectiveBinding.createFromType(appComponentAnnotatedType.type, appComponentAnnotatedType.annotation) + ).then( + (appProtoView) => { // The light Dom of the app element is not considered part of // the angular application. Thus the context and lightDomInjector are // empty. diff --git a/modules/angular2/src/core/compiler/compiler.js b/modules/angular2/src/core/compiler/compiler.js index 19661492dd..6c382412e9 100644 --- a/modules/angular2/src/core/compiler/compiler.js +++ b/modules/angular2/src/core/compiler/compiler.js @@ -85,6 +85,17 @@ export class NewCompiler { return DirectiveBinding.createFromType(meta.type, meta.annotation); } + // Create a rootView as if the compiler encountered . + // Used for bootstrapping. + compileRoot(elementOrSelector, componentBinding:DirectiveBinding):Promise { + return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => { + return this._compileNestedProtoViews(null, rootRenderPv, [componentBinding], true) + }).then( (rootProtoView) => { + rootProtoView.instantiateInPlace = true; + return rootProtoView; + }); + } + compile(component: Type):Promise { var protoView = this._compile(this._bindDirective(component)); return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView); @@ -96,7 +107,8 @@ export class NewCompiler { var protoView = this._compilerCache.get(component); if (isPresent(protoView)) { // The component has already been compiled into a ProtoView, - // returns a resolved Promise. + // returns a plain ProtoView, not wrapped inside of a Promise. + // Needed for recursive components. return protoView; } @@ -113,31 +125,72 @@ export class NewCompiler { this._flattenDirectives(template), (directive) => this._bindDirective(directive) ); - - pvPromise = this._compileNoRecurse(componentBinding, template, directives).then( (protoView) => { - // Populate the cache before compiling the nested components, - // so that components can reference themselves in their template. - this._compilerCache.set(component, protoView); - MapWrapper.delete(this._compiling, component); - - // Compile all the components from the template - var nestedPVPromises = this._compileNestedComponents(protoView); - if (nestedPVPromises.length > 0) { - // Returns ProtoView Promise when there are any asynchronous nested ProtoViews. - // The promise will resolved after nested ProtoViews are compiled. - return PromiseWrapper.then(PromiseWrapper.all(nestedPVPromises), - (_) => protoView, - (e) => { throw new BaseException(`${e} -> Failed to compile ${stringify(component)}`); } - ); - } - return protoView; + var renderTemplate = this._buildRenderTemplate(component, template, directives); + pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => { + return this._compileNestedProtoViews(componentBinding, renderPv, directives, true); }); + MapWrapper.set(this._compiling, component, pvPromise); return pvPromise; } - _compileNoRecurse(componentBinding, template, directives):Promise { - var component = componentBinding.key.token; + // TODO(tbosch): union type return ProtoView or Promise + _compileNestedProtoViews(componentBinding, renderPv, directives, isComponentRootView) { + var nestedPVPromises = []; + var protoView = this._protoViewFactory.createProtoView(componentBinding, renderPv, directives); + if (isComponentRootView && isPresent(componentBinding)) { + // Populate the cache before compiling the nested components, + // so that components can reference themselves in their template. + var component = componentBinding.key.token; + this._compilerCache.set(component, protoView); + MapWrapper.delete(this._compiling, component); + } + + var binderIndex = 0; + ListWrapper.forEach(protoView.elementBinders, (elementBinder) => { + var nestedComponent = elementBinder.componentDirective; + var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView; + var elementBinderDone = (nestedPv) => { + elementBinder.nestedProtoView = nestedPv; + // Can't set the parentProtoView for components, + // as their ProtoView might be used in multiple other components. + nestedPv.parentProtoView = isPresent(nestedComponent) ? null : protoView; + }; + var nestedCall = null; + if (isPresent(nestedComponent)) { + if (!(nestedComponent.annotation instanceof DynamicComponent)) { + nestedCall = this._compile(nestedComponent); + } + } else if (isPresent(nestedRenderProtoView)) { + nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false); + } + if (PromiseWrapper.isPromise(nestedCall)) { + ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone)); + } else if (isPresent(nestedCall)) { + elementBinderDone(nestedCall); + } + binderIndex++; + }); + + var protoViewDone = (_) => { + var childComponentRenderPvRefs = []; + ListWrapper.forEach(protoView.elementBinders, (eb) => { + if (isPresent(eb.componentDirective)) { + var componentPv = eb.nestedProtoView; + ListWrapper.push(childComponentRenderPvRefs, isPresent(componentPv) ? componentPv.render : null); + } + }); + this._renderer.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs); + return protoView; + }; + if (nestedPVPromises.length > 0) { + return PromiseWrapper.all(nestedPVPromises).then(protoViewDone); + } else { + return protoViewDone(null); + } + } + + _buildRenderTemplate(component, template, directives) { var componentUrl = this._urlResolver.resolve( this._appUrl, this._componentUrlMapper.getUrl(component) ); @@ -150,37 +203,12 @@ export class NewCompiler { // is able to resolve urls in stylesheets. templateAbsUrl = componentUrl; } - var renderTemplate = new renderApi.Template({ + return new renderApi.Template({ componentId: stringify(component), absUrl: templateAbsUrl, inline: template.inline, directives: ListWrapper.map(directives, this._buildRenderDirective) }); - return this._renderer.compile(renderTemplate).then( (renderPv) => { - return this._protoViewFactory.createProtoView(componentBinding.annotation, renderPv, directives); - }); - } - - _compileNestedComponents(protoView, nestedPVPromises = null):List { - if (isBlank(nestedPVPromises)) { - nestedPVPromises = []; - } - ListWrapper.map(protoView.elementBinders, (elementBinder) => { - var nestedComponent = elementBinder.componentDirective; - if (isPresent(nestedComponent) && !(nestedComponent.annotation instanceof DynamicComponent)) { - var nestedCall = this._compile(nestedComponent); - if (PromiseWrapper.isPromise(nestedCall)) { - ListWrapper.push(nestedPVPromises, nestedCall.then( (nestedPv) => { - elementBinder.nestedProtoView = nestedPv; - })); - } else { - elementBinder.nestedProtoView = nestedCall; - } - } else if (isPresent(elementBinder.nestedProtoView)) { - this._compileNestedComponents(elementBinder.nestedProtoView, nestedPVPromises); - } - }); - return nestedPVPromises; } _buildRenderDirective(directiveBinding) { @@ -269,7 +297,7 @@ export class Compiler extends NewCompiler { new DefaultStepFactory(parser, shadowDomStrategy.render), templateLoader ), - null, null + null, shadowDomStrategy.render ), new ProtoViewFactory(changeDetection, shadowDomStrategy) ); diff --git a/modules/angular2/src/core/compiler/proto_view_factory.js b/modules/angular2/src/core/compiler/proto_view_factory.js index 8fc42d2641..9a843ff7d0 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.js +++ b/modules/angular2/src/core/compiler/proto_view_factory.js @@ -20,16 +20,19 @@ export class ProtoViewFactory { this._shadowDomStrategy = shadowDomStrategy; } - createProtoView(componentAnnotation:Component, renderProtoView: renderApi.ProtoView, directives:List):ProtoView { - return this._createProtoView(null, componentAnnotation, renderProtoView, directives); - } - - _createProtoView(parent:ProtoView, componentAnnotation:Component, renderProtoView: renderApi.ProtoView, directives:List):ProtoView { - var protoChangeDetector = this._changeDetection.createProtoChangeDetector('dummy', - componentAnnotation.changeDetection); + createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoView, directives:List):ProtoView { + var protoChangeDetector; + if (isBlank(componentBinding)) { + protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null); + } else { + var componentAnnotation:Component = componentBinding.annotation; + protoChangeDetector = this._changeDetection.createProtoChangeDetector( + 'dummy', componentAnnotation.changeDetection + ); + } var domProtoView = this._getDomProtoView(renderProtoView.render); - var protoView = new ProtoView(domProtoView.element, protoChangeDetector, - this._shadowDomStrategy, parent); + var protoView = new ProtoView(renderProtoView.render, domProtoView.element, protoChangeDetector, + this._shadowDomStrategy, null); for (var i=0; i { protoView.bindVariable(varName, mappedName); diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index 87cdc1dde9..436d0e1941 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -16,6 +16,7 @@ import {Content} from './shadow_dom_emulation/content_tag'; import {ShadowDomStrategy} from './shadow_dom_strategy'; import {ViewPool} from './view_pool'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; +import * as renderApi from 'angular2/src/render/api'; const NG_BINDING_CLASS = 'ng-binding'; const NG_BINDING_CLASS_SELECTOR = '.ng-binding'; @@ -283,11 +284,14 @@ export class ProtoView { _directiveMementosMap:Map; _directiveMementos:List; + render:renderApi.ProtoViewRef; constructor( + render:renderApi.ProtoViewRef, template, protoChangeDetector:ProtoChangeDetector, shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) { + this.render = render; this.element = template; this.elementBinders = []; this.variableBindings = MapWrapper.create(); @@ -642,28 +646,6 @@ export class ProtoView { return MapWrapper.get(this._directiveMementosMap, id); } - - // Create a rootView as if the compiler encountered , - // and the component template is already compiled into protoView. - // Used for bootstrapping. - static createRootProtoView(protoView: ProtoView, - insertionElement, - rootComponentBinding: DirectiveBinding, - protoChangeDetector:ProtoChangeDetector, - shadowDomStrategy: ShadowDomStrategy - ): ProtoView { - - DOM.addClass(insertionElement, NG_BINDING_CLASS); - var cmpType = rootComponentBinding.key.token; - var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy); - rootProtoView.instantiateInPlace = true; - var binder = rootProtoView.bindElement(null, 0, - new ProtoElementInjector(null, 0, [cmpType], true)); - binder.componentDirective = rootComponentBinding; - binder.nestedProtoView = protoView; - shadowDomStrategy.shimAppElement(cmpType, insertionElement); - return rootProtoView; - } } /** diff --git a/modules/angular2/src/render/api.js b/modules/angular2/src/render/api.js index 398aa52d1d..2315bb5dcb 100644 --- a/modules/angular2/src/render/api.js +++ b/modules/angular2/src/render/api.js @@ -141,20 +141,20 @@ export class Renderer { compile(template:Template):Promise { return null; } /** - * Creates a new ProtoView with preset nested components, + * Sets the preset nested components, * which will be instantiated when this protoView is instantiated. + * Note: We can't create new ProtoViewRefs here as we need to support cycles / recursive components. * @param {List} protoViewRefs * ProtoView for every element with a component in this protoView or in a view container's protoView - * @return {List} - * new ProtoViewRef for the given protoView and all of its view container's protoViews */ - mergeChildComponentProtoViews(protoViewRef:ProtoViewRef, protoViewRefs:List):List { return null; } + mergeChildComponentProtoViews(protoViewRef:ProtoViewRef, componentProtoViewRefs:List) { return null; } /** * Creats a ProtoView that will create a root view for the given element, * i.e. it will not clone the element but only attach other proto views to it. + * Contains a single nested component with the given componentId. */ - createRootProtoView(selectorOrElement):ProtoViewRef { return null; } + createRootProtoView(selectorOrElement, componentId):Promise { return null; } /** * Creates a view and all of its nested child components. diff --git a/modules/angular2/src/render/dom/direct_dom_renderer.js b/modules/angular2/src/render/dom/direct_dom_renderer.js index 3451a95fb3..fe2dbe8f84 100644 --- a/modules/angular2/src/render/dom/direct_dom_renderer.js +++ b/modules/angular2/src/render/dom/direct_dom_renderer.js @@ -1,4 +1,4 @@ -import {Promise} from 'angular2/src/facade/async'; +import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {isBlank, isPresent} from 'angular2/src/facade/lang'; @@ -26,10 +26,6 @@ function _wrapView(view:View) { return new _DirectDomViewRef(view); } -function _wrapProtoView(protoView:ProtoView) { - return new DirectDomProtoViewRef(protoView); -} - function _collectComponentChildViewRefs(view, target = null) { if (isBlank(target)) { target = []; @@ -83,22 +79,22 @@ export class DirectDomRenderer extends api.Renderer { return this._compiler.compile(template); } - mergeChildComponentProtoViews(protoViewRef:api.ProtoViewRef, protoViewRefs:List):List { - var protoViews = []; + mergeChildComponentProtoViews(protoViewRef:api.ProtoViewRef, protoViewRefs:List) { _resolveProtoView(protoViewRef).mergeChildComponentProtoViews( - ListWrapper.map(protoViewRefs, _resolveProtoView), - protoViews + ListWrapper.map(protoViewRefs, _resolveProtoView) ); - return ListWrapper.map(protoViews, _wrapProtoView); } - createRootProtoView(selectorOrElement):api.ProtoViewRef { + createRootProtoView(selectorOrElement, componentId):Promise { var element = selectorOrElement; // TODO: select the element if it is not a real element... var rootProtoViewBuilder = new ProtoViewBuilder(element); rootProtoViewBuilder.setIsRootView(true); - rootProtoViewBuilder.bindElement(element, 'root element').setComponentId('root'); - this._shadowDomStrategy.processElement(null, 'root', element); - return rootProtoViewBuilder.build().render; + var elBinder = rootProtoViewBuilder.bindElement(element, 'root element'); + elBinder.setComponentId(componentId); + elBinder.bindDirective(0); + + this._shadowDomStrategy.processElement(null, componentId, element); + return PromiseWrapper.resolve(rootProtoViewBuilder.build()); } createView(protoViewRef:api.ProtoViewRef):List { diff --git a/modules/angular2/src/render/dom/view/element_binder.js b/modules/angular2/src/render/dom/view/element_binder.js index 9d11c6d2ce..5e1c445e75 100644 --- a/modules/angular2/src/render/dom/view/element_binder.js +++ b/modules/angular2/src/render/dom/view/element_binder.js @@ -1,12 +1,8 @@ import {AST} from 'angular2/change_detection'; import {SetterFn} from 'angular2/src/reflection/types'; -import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import * as protoViewModule from './proto_view'; -/** - * Note: Code that uses this class assumes that is immutable! - */ export class ElementBinder { contentTagSelector: string; textNodeIndices: List; @@ -39,24 +35,4 @@ export class ElementBinder { this.distanceToParent = distanceToParent; this.propertySetters = propertySetters; } - - mergeChildComponentProtoViews(protoViews:List, target:List):ElementBinder { - var nestedProtoView; - if (isPresent(this.componentId)) { - nestedProtoView = ListWrapper.removeAt(protoViews, 0); - } else if (isPresent(this.nestedProtoView)) { - nestedProtoView = this.nestedProtoView.mergeChildComponentProtoViews(protoViews, target); - } - return new ElementBinder({ - parentIndex: this.parentIndex, - textNodeIndices: this.textNodeIndices, - contentTagSelector: this.contentTagSelector, - nestedProtoView: nestedProtoView, - componentId: this.componentId, - eventLocals: this.eventLocals, - eventNames: this.eventNames, - distanceToParent: this.distanceToParent, - propertySetters: this.propertySetters - }); - } } diff --git a/modules/angular2/src/render/dom/view/proto_view.js b/modules/angular2/src/render/dom/view/proto_view.js index 0ffb08231a..95ddd36ab3 100644 --- a/modules/angular2/src/render/dom/view/proto_view.js +++ b/modules/angular2/src/render/dom/view/proto_view.js @@ -6,9 +6,6 @@ import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection import {ElementBinder} from './element_binder'; import {NG_BINDING_CLASS} from '../util'; -/** - * Note: Code that uses this class assumes that is immutable! - */ export class ProtoView { element; elementBinders:List; @@ -28,22 +25,14 @@ export class ProtoView { this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0; } - mergeChildComponentProtoViews(protoViews:List, target:List):ProtoView { - var elementBinders = ListWrapper.createFixedSize(this.elementBinders.length); + mergeChildComponentProtoViews(componentProtoViews:List) { + var componentProtoViewIndex = 0; for (var i=0; i { + it('should pass the component binding', inject([AsyncTestCompleter], (async) => { tplResolver.setTemplate(MainComponent, new Template({inline: '
'})); var compiler = createCompiler([createRenderProtoView()], [createProtoView()]); compiler.compile(MainComponent).then( (protoView) => { var request = protoViewFactory.requests[0]; - expect(request[0]).toEqual(new Component({ - selector: 'main-comp' - })); + expect(request[0].key.token).toBe(MainComponent); async.done(); }); })); @@ -255,7 +253,7 @@ export function main() { }); - it('should load nested components in root ProtoView', inject([AsyncTestCompleter], (async) => { + it('should load nested components', inject([AsyncTestCompleter], (async) => { tplResolver.setTemplate(MainComponent, new Template({inline: '
'})); tplResolver.setTemplate(NestedComponent, new Template({inline: '
'})); var mainProtoView = createProtoView([ @@ -263,34 +261,54 @@ export function main() { ]); var nestedProtoView = createProtoView(); var compiler = createCompiler( - [createRenderProtoView(), createRenderProtoView()], + [ + createRenderProtoView([createRenderComponentElementBinder(0)]), + createRenderProtoView() + ], [mainProtoView, nestedProtoView] ); compiler.compile(MainComponent).then( (protoView) => { expect(protoView).toBe(mainProtoView); expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView); + // parentProtoView of nested components has to be null as components can + // be used by multiple other components. + expect(nestedProtoView.parentProtoView).toBe(null); async.done(); }); })); - it('should load nested components in viewport ProtoView', inject([AsyncTestCompleter], (async) => { + it('should load nested components in viewport', inject([AsyncTestCompleter], (async) => { tplResolver.setTemplate(MainComponent, new Template({inline: '
'})); tplResolver.setTemplate(NestedComponent, new Template({inline: '
'})); var mainProtoView = createProtoView([ - createViewportElementBinder(createProtoView([ - createComponentElementBinder(reader, NestedComponent) - ])) + createViewportElementBinder(null) + ]); + var viewportProtoView = createProtoView([ + createComponentElementBinder(reader, NestedComponent) ]); var nestedProtoView = createProtoView(); var compiler = createCompiler( - [createRenderProtoView(), createRenderProtoView()], - [mainProtoView, nestedProtoView] + [ + createRenderProtoView([ + createRenderViewportElementBinder( + createRenderProtoView([ + createRenderComponentElementBinder(0) + ]) + ) + ]), + createRenderProtoView() + ], + [mainProtoView, viewportProtoView, nestedProtoView] ); compiler.compile(MainComponent).then( (protoView) => { expect(protoView).toBe(mainProtoView); - expect( - mainProtoView.elementBinders[0].nestedProtoView.elementBinders[0].nestedProtoView - ).toBe(nestedProtoView); + expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(viewportProtoView); + expect(viewportProtoView.parentProtoView).toBe(mainProtoView); + expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView); + // parentProtoView of nested components has to be null as components can + // be used by multiple other components. + expect(nestedProtoView.parentProtoView).toBe(null); + async.done(); }); })); @@ -331,7 +349,9 @@ export function main() { createComponentElementBinder(reader, MainComponent) ]); var compiler = createCompiler( - [createRenderProtoView()], + [createRenderProtoView([ + createRenderComponentElementBinder(0) + ])], [mainProtoView] ); compiler.compile(MainComponent).then( (protoView) => { @@ -340,20 +360,44 @@ export function main() { async.done(); }); })); + + it('should create root proto views', inject([AsyncTestCompleter], (async) => { + tplResolver.setTemplate(MainComponent, new Template({inline: '
'})); + var rootProtoView = createProtoView([ + createComponentElementBinder(reader, MainComponent) + ]); + var mainProtoView = createProtoView(); + var compiler = createCompiler( + [ + createRenderProtoView() + ], + [rootProtoView, mainProtoView] + ); + compiler.compileRoot(null, createDirectiveBinding(reader, MainComponent)).then( (protoView) => { + expect(protoView).toBe(rootProtoView); + expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView); + async.done(); + }); + })); }); } +function createDirectiveBinding(reader, type) { + var meta = reader.read(type); + return DirectiveBinding.createFromType(meta.type, meta.annotation); +} + function createProtoView(elementBinders = null) { - var pv = new ProtoView(null, null, null, null); - if (isPresent(elementBinders)) { - pv.elementBinders = elementBinders; + var pv = new ProtoView(null, null, null, null, null); + if (isBlank(elementBinders)) { + elementBinders = []; } + pv.elementBinders = elementBinders; return pv; } function createComponentElementBinder(reader, type) { - var meta = reader.read(type); - var binding = DirectiveBinding.createFromType(meta.type, meta.annotation); + var binding = createDirectiveBinding(reader, type); return new ElementBinder( 0, null, 0, null, binding, @@ -371,8 +415,27 @@ function createViewportElementBinder(nestedProtoView) { return elBinder; } -function createRenderProtoView() { - return new renderApi.ProtoView(); +function createRenderProtoView(elementBinders = null) { + if (isBlank(elementBinders)) { + elementBinders = []; + } + return new renderApi.ProtoView({ + elementBinders: elementBinders + }); +} + +function createRenderComponentElementBinder(directiveIndex) { + return new renderApi.ElementBinder({ + directives: [new renderApi.DirectiveBinder({ + directiveIndex: directiveIndex + })] + }); +} + +function createRenderViewportElementBinder(nestedProtoView) { + return new renderApi.ElementBinder({ + nestedProtoView: nestedProtoView + }); } @Component({ @@ -433,6 +496,12 @@ class FakeRenderer extends renderApi.Renderer { ListWrapper.push(this.requests, template); return PromiseWrapper.resolve(ListWrapper.removeAt(this._results, 0)); } + + createRootProtoView(elementOrSelector, componentId):Promise { + return PromiseWrapper.resolve( + createRenderProtoView([createRenderComponentElementBinder(0)]) + ); + } } class FakeUrlResolver extends UrlResolver { @@ -481,8 +550,8 @@ class FakeProtoViewFactory extends ProtoViewFactory { this._results = results; } - createProtoView(componentAnnotation:Component, renderProtoView: renderApi.ProtoView, directives:List):ProtoView { - ListWrapper.push(this.requests, [componentAnnotation, renderProtoView, directives]); + createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoView, directives:List):ProtoView { + ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]); return ListWrapper.removeAt(this._results, 0); } } \ No newline at end of file diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js index 9998ef71ac..30378450e6 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.js @@ -577,7 +577,7 @@ export function main() { function createpreBuildObject(eventName, eventHandler) { var handlers = StringMapWrapper.create(); StringMapWrapper.set(handlers, eventName, eventHandler); - var pv = new ProtoView(null, null, null); + var pv = new ProtoView(null, null, null, null); pv.eventHandlers = [handlers]; var view = new View(pv, null, MapWrapper.create()); return new PreBuiltObjects(view, null, null, null); diff --git a/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js b/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js index 8c6d6d03b2..7638a58d45 100644 --- a/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js +++ b/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js @@ -43,7 +43,7 @@ export function main() { it('should attach the view nodes to the shadow root', () => { var host = el('
'); var nodes = el('
view
'); - var pv = new ProtoView(nodes, new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); var view = pv.instantiate(null, null); strategy.attachTemplate(host, view); @@ -68,7 +68,7 @@ export function main() { it('should attach the view nodes as child of the host element', () => { var host = el('
original content
'); var nodes = el('
view
'); - var pv = new ProtoView(nodes, new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); var view = pv.instantiate(null, null); strategy.attachTemplate(host, view); @@ -92,7 +92,7 @@ export function main() { it('should attach the view nodes as child of the host element', () => { var host = el('
original content
'); var nodes = el('
view
'); - var pv = new ProtoView(nodes, new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); var view = pv.instantiate(null, null); strategy.attachTemplate(host, view); diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js index dc0f3231a5..ee8b88980c 100644 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ b/modules/angular2/test/core/compiler/view_container_spec.js @@ -71,7 +71,7 @@ export function main() { dom = el(`
`); var insertionElement = dom.childNodes[1]; parentView = createView([dom.childNodes[0]]); - protoView = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(null, null), + protoView = new ProtoView(null, el('
hi
'), new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); elementInjector = new ElementInjector(null, null, null); viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector, @@ -216,7 +216,7 @@ export function main() { var parser = new Parser(new Lexer()); viewContainer.hydrate(new Injector([]), null, null); - var pv = new ProtoView(el('
{{}}
'), + var pv = new ProtoView(null, el('
{{}}
'), new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); pv.bindTextNode(0, parser.parseBinding('foo', null)); diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js index 173e226cb1..b8c523fa66 100644 --- a/modules/angular2/test/core/compiler/view_spec.js +++ b/modules/angular2/test/core/compiler/view_spec.js @@ -63,7 +63,7 @@ export function main() { describe('instantiated from protoView', () => { var view; beforeEach(() => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); view = pv.instantiate(null, null); }); @@ -90,7 +90,7 @@ export function main() { }); it('should use the view pool to reuse views', () => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); var fakeView = new FakeView(); pv.returnToPool(fakeView); @@ -101,7 +101,7 @@ export function main() { describe('with locals', function() { var view; beforeEach(() => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null, null), null); + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindVariable('context-foo', 'template-foo'); view = createView(pv); }); @@ -137,7 +137,7 @@ export function main() { } it('should collect the root node in the ProtoView element', () => { - var pv = new ProtoView(templateAwareCreateElement('
'), + var pv = new ProtoView(null, templateAwareCreateElement('
'), new DynamicProtoChangeDetector(null, null), null); var view = pv.instantiate(null, null); view.hydrate(null, null, null, null, null); @@ -148,7 +148,7 @@ export function main() { describe('collect elements with property bindings', () => { it('should collect property bindings on the root element if it has the ng-binding class', () => { - var pv = new ProtoView(templateAwareCreateElement('
'), + var pv = new ProtoView(null, templateAwareCreateElement('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop')); @@ -160,7 +160,7 @@ export function main() { }); it('should collect property bindings on child elements with ng-binding class', () => { - var pv = new ProtoView(templateAwareCreateElement('
'), + var pv = new ProtoView(null, templateAwareCreateElement('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a')); @@ -176,7 +176,7 @@ export function main() { describe('collect text nodes with bindings', () => { it('should collect text nodes under the root element', () => { - var pv = new ProtoView(templateAwareCreateElement('
{{}}{{}}
'), + var pv = new ProtoView(null, templateAwareCreateElement('
{{}}{{}}
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindTextNode(0, parser.parseBinding('a', null)); @@ -190,7 +190,7 @@ export function main() { }); it('should collect text nodes with bindings on child elements with ng-binding class', () => { - var pv = new ProtoView(templateAwareCreateElement('
{{}}
'), + var pv = new ProtoView(null, templateAwareCreateElement('
{{}}
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindTextNode(0, parser.parseBinding('b', null)); @@ -207,7 +207,7 @@ export function main() { describe('inplace instantiation', () => { it('should be supported.', () => { var template = el('
'); - var pv = new ProtoView(template, new DynamicProtoChangeDetector(null, null), + var pv = new ProtoView(null, template, new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); pv.instantiateInPlace = true; var view = pv.instantiate(null, null); @@ -217,7 +217,7 @@ export function main() { it('should be off by default.', () => { var template = el('
') - var pv = new ProtoView(template, new DynamicProtoChangeDetector(null, null), + var pv = new ProtoView(null, template, new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)) var view = pv.instantiate(null, null); view.hydrate(null, null, null, null, null); @@ -235,7 +235,7 @@ export function main() { describe('create ElementInjectors', () => { it('should use the directives of the ProtoElementInjector', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); @@ -246,7 +246,7 @@ export function main() { }); it('should use the correct parent', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(null, 0, protoParent); @@ -260,7 +260,7 @@ export function main() { }); it('should not pass the host injector when a parent injector exists', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(null, 0, protoParent); @@ -276,7 +276,7 @@ export function main() { }); it('should pass the host injector when there is no parent injector', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirective])); var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]); @@ -293,7 +293,7 @@ export function main() { describe('collect root element injectors', () => { it('should collect a single root element injector', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(null, 0, protoParent); @@ -306,7 +306,7 @@ export function main() { }); it('should collect multiple root element injectors', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); pv.bindElement(null, 0, new ProtoElementInjector(null, 2, [AnotherDirective])); @@ -324,7 +324,7 @@ export function main() { var ctx; function createComponentWithSubPV(subProtoView) { - var pv = new ProtoView(el(''), + var pv = new ProtoView(null, el(''), new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeComponent], true)); binder.componentDirective = someComponentDirective; @@ -340,7 +340,7 @@ export function main() { } it('should expose component services to the component', () => { - var subpv = new ProtoView(el(''), new DynamicProtoChangeDetector(null, null), null); + var subpv = new ProtoView(null, el(''), new DynamicProtoChangeDetector(null, null), null); var pv = createComponentWithSubPV(subpv); var view = createNestedView(pv); @@ -351,7 +351,7 @@ export function main() { it('should expose component services and component instance to directives in the shadow Dom', () => { - var subpv = new ProtoView( + var subpv = new ProtoView(null, el('
hello shadow dom
'), new DynamicProtoChangeDetector(null, null), null); @@ -376,7 +376,7 @@ export function main() { } it('dehydration should dehydrate child component views too', () => { - var subpv = new ProtoView( + var subpv = new ProtoView(null, el('
hello shadow dom
'), new DynamicProtoChangeDetector(null, null), null); @@ -394,7 +394,7 @@ export function main() { }); it('should create shadow dom (Native Strategy)', () => { - var subpv = new ProtoView(el('hello shadow dom'), + var subpv = new ProtoView(null, el('hello shadow dom'), new DynamicProtoChangeDetector(null, null), null); var pv = createComponentWithSubPV(subpv); @@ -405,10 +405,10 @@ export function main() { }); it('should emulate shadow dom (Emulated Strategy)', () => { - var subpv = new ProtoView(el('hello shadow dom'), + var subpv = new ProtoView(null, el('hello shadow dom'), new DynamicProtoChangeDetector(null, null), null); - var pv = new ProtoView(el(''), + var pv = new ProtoView(null, el(''), new DynamicProtoChangeDetector(null, null), new EmulatedScopedShadowDomStrategy(null, null, null)); var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeComponent], true)); binder.componentDirective = readDirectiveBinding(SomeComponent); @@ -422,9 +422,9 @@ export function main() { describe('with template views', () => { function createViewWithViewport() { - var templateProtoView = new ProtoView( + var templateProtoView = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); - var pv = new ProtoView(el(''), + var pv = new ProtoView(null, el(''), new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeViewport])); binder.viewportDirective = someViewportDirective; @@ -470,7 +470,7 @@ export function main() { } function createProtoView() { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, [])); pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); @@ -505,7 +505,7 @@ export function main() { }); it('should support custom event emitters', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, [EventEmitterDirective])); pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); @@ -526,7 +526,7 @@ export function main() { }); it('should bind to directive events', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirectiveWithEventHandler])); pv.bindEvent('click', parser.parseAction('onEvent($event)', null), 0); @@ -551,7 +551,7 @@ export function main() { } it('should consume text node changes', () => { - var pv = new ProtoView(el('
{{}}
'), + var pv = new ProtoView(null, el('
{{}}
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindTextNode(0, parser.parseBinding('foo', null)); @@ -563,7 +563,7 @@ export function main() { }); it('should consume element binding changes', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, null); pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id')); @@ -575,7 +575,7 @@ export function main() { }); it('should consume directive watch expression change', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirective])); pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop')); @@ -587,7 +587,7 @@ export function main() { }); it('should notify a directive about changes after all its properties have been set', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ @@ -607,7 +607,7 @@ export function main() { }); it('should provide a map of updated properties using onChange callback', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ @@ -634,7 +634,7 @@ export function main() { }); it('should invoke the onAllChangesDone callback', () => { - var pv = new ProtoView(el('
'), + var pv = new ProtoView(null, el('
'), new DynamicProtoChangeDetector(null, null), null); pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ @@ -651,32 +651,6 @@ export function main() { }); }); - describe('protoView createRootProtoView', () => { - var element, pv; - beforeEach(() => { - element = DOM.createElement('div'); - pv = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(null, null), - new NativeShadowDomStrategy(null)); - }); - - it('should create the root component when instantiated', () => { - var rootProtoView = ProtoView.createRootProtoView(pv, element, - someComponentDirective, new DynamicProtoChangeDetector(null, null), - new NativeShadowDomStrategy(null)); - var view = rootProtoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, null, null); - expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null); - }); - - it('should inject the protoView into the shadowDom', () => { - var rootProtoView = ProtoView.createRootProtoView(pv, element, - someComponentDirective, new DynamicProtoChangeDetector(null, null), - new NativeShadowDomStrategy(null)); - var view = rootProtoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, null, null); - expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi'); - }); - }); }); } diff --git a/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js b/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js index a2fd6ba8a2..d7a7dd3da1 100644 --- a/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js +++ b/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js @@ -21,7 +21,11 @@ import {IntegrationTestbed, LoggingEventDispatcher, FakeEvent} from './integrati export function main() { describe('DirectDomRenderer integration', () => { - var testbed, renderer, rootEl, rootProtoViewRef, eventPlugin, compile; + var testbed, renderer, eventPlugin, compile, rootEl; + + beforeEach(() => { + rootEl = el('
'); + }); function createRenderer({urlData, viewCacheCapacity, shadowDomStrategy, templates}={}) { testbed = new IntegrationTestbed({ @@ -31,54 +35,66 @@ export function main() { templates: templates }); renderer = testbed.renderer; - rootEl = testbed.rootEl; - rootProtoViewRef = testbed.rootProtoViewRef; eventPlugin = testbed.eventPlugin; - compile = (template, directives) => testbed.compile(template, directives); + compile = (rootEl, componentId) => testbed.compile(rootEl, componentId); } - it('should create root views while using the given elements in place', () => { + it('should create root views while using the given elements in place', inject([AsyncTestCompleter], (async) => { createRenderer(); - var viewRefs = renderer.createView(rootProtoViewRef); - expect(viewRefs.length).toBe(1); - expect(viewRefs[0].delegate.rootNodes[0]).toEqual(rootEl); - }); + renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + expect(rootProtoView.elementBinders[0].directives[0].directiveIndex).toBe(0); + var viewRefs = renderer.createView(rootProtoView.render); + expect(viewRefs.length).toBe(1); + expect(viewRefs[0].delegate.rootNodes[0]).toEqual(rootEl); + async.done(); + }); + })); it('should add a static component', inject([AsyncTestCompleter], (async) => { createRenderer(); - var template = new Template({ - componentId: 'someComponent', - inline: 'hello', - directives: [] - }); - renderer.compile(template).then( (pv) => { - var mergedProtoViewRefs = renderer.mergeChildComponentProtoViews(rootProtoViewRef, [pv.render]); - renderer.createView(mergedProtoViewRefs[0]); - expect(rootEl).toHaveText('hello'); - async.done(); + renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + var template = new Template({ + componentId: 'someComponent', + inline: 'hello', + directives: [] + }); + renderer.compile(template).then( (pv) => { + renderer.mergeChildComponentProtoViews(rootProtoView.render, [pv.render]); + renderer.createView(rootProtoView.render); + expect(rootEl).toHaveText('hello'); + async.done(); + }); }); })); it('should add a a dynamic component', inject([AsyncTestCompleter], (async) => { createRenderer(); - var template = new Template({ - componentId: 'someComponent', - inline: 'hello', - directives: [] - }); - renderer.compile(template).then( (pv) => { - var rootViewRef = renderer.createView(rootProtoViewRef)[0]; - var childComponentViewRef = renderer.createView(pv.render)[0]; - renderer.setDynamicComponentView(rootViewRef, 0, childComponentViewRef); - expect(rootEl).toHaveText('hello'); - async.done(); + renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + var template = new Template({ + componentId: 'someComponent', + inline: 'hello', + directives: [] + }); + renderer.compile(template).then( (pv) => { + var rootViewRef = renderer.createView(rootProtoView.render)[0]; + var childComponentViewRef = renderer.createView(pv.render)[0]; + renderer.setDynamicComponentView(rootViewRef, 0, childComponentViewRef); + expect(rootEl).toHaveText('hello'); + async.done(); + }); }); })); it('should update text nodes', inject([AsyncTestCompleter], (async) => { - createRenderer(); - compile('{{a}}', [someComponent]).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'someComponent', + inline: '{{a}}', + directives: [] + })] + }); + compile(rootEl, 'someComponent').then( (rootProtoView) => { + var viewRefs = renderer.createView(rootProtoView.render); renderer.setText(viewRefs[1], 0, 'hello'); expect(rootEl).toHaveText('hello'); async.done(); @@ -86,9 +102,15 @@ export function main() { })); it('should update element properties', inject([AsyncTestCompleter], (async) => { - createRenderer(); - compile('', []).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'someComponent', + inline: '', + directives: [] + })] + }); + compile(rootEl, 'someComponent').then( (rootProtoView) => { + var viewRefs = renderer.createView(rootProtoView.render); renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello'); expect(DOM.childNodes(rootEl)[0].value).toEqual('hello'); async.done(); @@ -96,10 +118,17 @@ export function main() { })); it('should add and remove views to and from containers', inject([AsyncTestCompleter], (async) => { - createRenderer(); - compile('', []).then( (pvRefs) => { - var viewRef = renderer.createView(pvRefs[0])[1]; - var vcProtoViewRef = pvRefs[2]; + createRenderer({ + templates: [new Template({ + componentId: 'someComponent', + inline: '', + directives: [] + })] + }); + compile(rootEl, 'someComponent').then( (rootProtoView) => { + var viewRef = renderer.createView(rootProtoView.render)[1]; + var vcProtoViewRef = rootProtoView.elementBinders[0] + .nestedProtoView.elementBinders[0].nestedProtoView.render; var vcRef = new ViewContainerRef(viewRef, 0); var childViewRef = renderer.createView(vcProtoViewRef)[0]; @@ -115,10 +144,17 @@ export function main() { it('should cache views', inject([AsyncTestCompleter], (async) => { createRenderer({ + templates: [new Template({ + componentId: 'someComponent', + inline: '', + directives: [] + })], viewCacheCapacity: 2 }); - compile('', []).then( (pvRefs) => { - var vcProtoViewRef = pvRefs[2]; + compile(rootEl, 'someComponent').then( (rootProtoView) => { + var vcProtoViewRef = rootProtoView.elementBinders[0] + .nestedProtoView.elementBinders[0].nestedProtoView.render; + var viewRef1 = renderer.createView(vcProtoViewRef)[0]; renderer.destroyView(viewRef1); var viewRef2 = renderer.createView(vcProtoViewRef)[0]; @@ -133,9 +169,15 @@ export function main() { // TODO(tbosch): This is not working yet as we commented out // the event expression processing... xit('should handle events', inject([AsyncTestCompleter], (async) => { - createRenderer(); - compile('', []).then( (pvRefs) => { - var viewRef = renderer.createView(pvRefs[0])[1]; + createRenderer({ + templates: [new Template({ + componentId: 'someComponent', + inline: '', + directives: [] + })] + }); + compile(rootEl, 'someComponent').then( (rootProtoView) => { + var viewRef = renderer.createView(rootProtoView.render)[1]; var dispatcher = new LoggingEventDispatcher(); renderer.setEventDispatcher(viewRef, dispatcher); var inputEl = DOM.childNodes(rootEl)[0]; diff --git a/modules/angular2/test/render/dom/integration_testbed.js b/modules/angular2/test/render/dom/integration_testbed.js index dd81306e61..eb34d2e042 100644 --- a/modules/angular2/test/render/dom/integration_testbed.js +++ b/modules/angular2/test/render/dom/integration_testbed.js @@ -1,7 +1,3 @@ -import { - el -} from 'angular2/test_lib'; - import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang'; import {MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; @@ -23,11 +19,8 @@ import {ViewFactory} from 'angular2/src/render/dom/view/view_factory'; export class IntegrationTestbed { renderer; parser; - rootEl; - rootProtoViewRef; eventPlugin; _templates:Map; - _compileCache:Map>; constructor({urlData, viewCacheCapacity, shadowDomStrategy, templates}) { this._templates = MapWrapper.create(); @@ -36,7 +29,6 @@ export class IntegrationTestbed { MapWrapper.set(this._templates, template.componentId, template); }); } - this._compileCache = MapWrapper.create(); var parser = new Parser(new Lexer()); var urlResolver = new UrlResolver(); if (isBlank(shadowDomStrategy)) { @@ -54,90 +46,66 @@ export class IntegrationTestbed { var eventManager = new EventManager([this.eventPlugin], new FakeVmTurnZone()); var viewFactory = new ViewFactory(viewCacheCapacity, eventManager, shadowDomStrategy); this.renderer = new DirectDomRenderer(compiler, viewFactory, shadowDomStrategy); - - this.rootEl = el('
'); - this.rootProtoViewRef = this.renderer.createRootProtoView(this.rootEl); } - compile(templateHtml, directives):Promise> { - return this._compileRecurse(new Template({ - componentId: 'root', - inline: templateHtml, - directives: directives - })).then( (protoViewRefs) => { - return this._flattenList([ - this.renderer.mergeChildComponentProtoViews(this.rootProtoViewRef, [protoViewRefs[0]]), - protoViewRefs + compile(rootEl, componentId):Promise { + return this.renderer.createRootProtoView(rootEl, componentId).then( (rootProtoView) => { + return this._compileNestedProtoViews(rootProtoView, [ + new DirectiveMetadata({ + type: DirectiveMetadata.COMPONENT_TYPE, + id: componentId + }) ]); }); } - _compileRecurse(template):Promise> { - var result = MapWrapper.get(this._compileCache, template.componentId); - if (isPresent(result)) { - return result; - } - result = this.renderer.compile(template).then( (pv) => { - var childComponentPromises = ListWrapper.map( - this._findNestedComponentIds(template, pv), - (componentId) => { - var childTemplate = MapWrapper.get(this._templates, componentId); - if (isBlank(childTemplate)) { - throw new BaseException(`Could not find template for ${componentId}!`); - } - return this._compileRecurse(childTemplate); - } - ); - return PromiseWrapper.all(childComponentPromises).then( - (protoViewRefsWithChildren) => { - var protoViewRefs = - ListWrapper.map(protoViewRefsWithChildren, (arr) => arr[0]); - return this._flattenList([ - this.renderer.mergeChildComponentProtoViews(pv.render, protoViewRefs), - protoViewRefsWithChildren - ]); - } - ); + _compile(template):Promise { + return this.renderer.compile(template).then( (protoView) => { + return this._compileNestedProtoViews(protoView, template.directives); }); - MapWrapper.set(this._compileCache, template.componentId, result); - return result; } - _findNestedComponentIds(template, pv, target = null):List { - if (isBlank(target)) { - target = []; - } - for (var binderIdx=0; binderIdx { - var meta = template.directives[db.directiveIndex]; - if (meta.type === DirectiveMetadata.COMPONENT_TYPE) { - componentDirective = meta; + _compileNestedProtoViews(protoView, directives):Promise { + var childComponentRenderPvRefs = []; + var nestedPVPromises = []; + ListWrapper.forEach(protoView.elementBinders, (elementBinder) => { + var nestedComponentId = null; + ListWrapper.forEach(elementBinder.directives, (db) => { + var directiveMeta = directives[db.directiveIndex]; + if (directiveMeta.type === DirectiveMetadata.COMPONENT_TYPE) { + nestedComponentId = directiveMeta.id; } }); - if (isPresent(componentDirective)) { - ListWrapper.push(target, componentDirective.id); - } else if (isPresent(eb.nestedProtoView)) { - this._findNestedComponentIds(template, eb.nestedProtoView, target); + var nestedCall; + if (isPresent(nestedComponentId)) { + var childTemplate = MapWrapper.get(this._templates, nestedComponentId); + if (isBlank(childTemplate)) { + throw new BaseException(`Could not find template for ${nestedComponentId}!`); + } + nestedCall = this._compile(childTemplate); + } else if (isPresent(elementBinder.nestedProtoView)) { + nestedCall = this._compileNestedProtoViews(elementBinder.nestedProtoView, directives); } - } - return target; - } - - _flattenList(tree:List, out:List = null):List { - if (isBlank(out)) { - out = []; - } - for (var i = 0; i < tree.length; i++) { - var item = tree[i]; - if (ListWrapper.isList(item)) { - this._flattenList(item, out); - } else { - ListWrapper.push(out, item); + if (isPresent(nestedCall)) { + ListWrapper.push( + nestedPVPromises, + nestedCall.then( (nestedPv) => { + elementBinder.nestedProtoView = nestedPv; + if (isPresent(nestedComponentId)) { + ListWrapper.push(childComponentRenderPvRefs, nestedPv.render); + } + }) + ); } + }); + if (nestedPVPromises.length > 0) { + return PromiseWrapper.all(nestedPVPromises).then((_) => { + this.renderer.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs); + return protoView; + }); + } else { + return PromiseWrapper.resolve(protoView); } - return out; } } diff --git a/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js b/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js index 8635fe646e..fe57ce4e3a 100644 --- a/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js @@ -40,34 +40,45 @@ export function main() { StringMapWrapper.set(strategies, "native", () => new NativeShadowDomStrategy(styleUrlResolver)); } + beforeEach( () => { + urlResolver = new UrlResolver(); + styleUrlResolver = new StyleUrlResolver(urlResolver); + styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver); + }); + + StringMapWrapper.forEach(strategies, (strategyFactory, name) => { describe(`${name} shadow dom strategy`, () => { - var testbed, renderer, rootEl, compile, strategy; + var testbed, renderer, rootEl, compile; - beforeEach( () => { - urlResolver = new UrlResolver(); - styleUrlResolver = new StyleUrlResolver(urlResolver); - styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver); - strategy = strategyFactory(); + function createRenderer({templates}) { testbed = new IntegrationTestbed({ - shadowDomStrategy: strategy, - templates: templates + shadowDomStrategy: strategyFactory(), + templates: ListWrapper.concat(templates, componentTemplates) }); renderer = testbed.renderer; - rootEl = testbed.rootEl; - compile = (template, directives) => testbed.compile(template, directives); + compile = (rootEl, componentId) => testbed.compile(rootEl, componentId); + } + + beforeEach( () => { + rootEl = el('
'); }); it('should support simple components', inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
'; - - compile(temp, [simple]).then( (pvRefs) => { - renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
', + directives: [simple] + })] + }); + compile(rootEl, 'main').then( (pv) => { + renderer.createView(pv.render); expect(rootEl).toHaveText('SIMPLE(A)'); @@ -76,14 +87,19 @@ export function main() { })); it('should support multiple content tags', inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
B
' + - '
C
' + - '
A
' + - '
'; - - compile(temp, [multipleContentTagsComponent]).then( (pvRefs) => { - renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
B
' + + '
C
' + + '
A
' + + '
', + directives: [multipleContentTagsComponent] + })] + }); + compile(rootEl, 'main').then( (pv) => { + renderer.createView(pv.render); expect(rootEl).toHaveText('(A, BC)'); @@ -92,13 +108,18 @@ export function main() { })); it('should redistribute only direct children', inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
B
A
' + - '
C
' + - '
'; - - compile(temp, [multipleContentTagsComponent]).then( (pvRefs) => { - renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
B
A
' + + '
C
' + + '
', + directives: [multipleContentTagsComponent] + })] + }); + compile(rootEl, 'main').then( (pv) => { + renderer.createView(pv.render); expect(rootEl).toHaveText('(, BAC)'); @@ -107,15 +128,21 @@ export function main() { })); it("should redistribute direct child viewcontainers when the light dom changes", inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
B
' + - '
'; - - compile(temp, [multipleContentTagsComponent, manualViewportDirective]).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
B
' + + '
', + directives: [multipleContentTagsComponent, manualViewportDirective] + })] + }); + compile(rootEl, 'main').then( (pv) => { + var viewRefs = renderer.createView(pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); - var vcProtoViewRef = pvRefs[2]; + var vcProtoViewRef = pv.elementBinders[0].nestedProtoView + .elementBinders[1].nestedProtoView.render; var childViewRef = renderer.createView(vcProtoViewRef)[0]; expect(rootEl).toHaveText('(, B)'); @@ -133,15 +160,21 @@ export function main() { })); it("should redistribute when the light dom changes", inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
B
' + - '
'; - - compile(temp, [multipleContentTagsComponent, manualViewportDirective]).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
B
' + + '
', + directives: [multipleContentTagsComponent, manualViewportDirective] + })] + }); + compile(rootEl, 'main').then( (pv) => { + var viewRefs = renderer.createView(pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); - var vcProtoViewRef = pvRefs[2]; + var vcProtoViewRef = pv.elementBinders[0].nestedProtoView + .elementBinders[1].nestedProtoView.render; var childViewRef = renderer.createView(vcProtoViewRef)[0]; expect(rootEl).toHaveText('(, B)'); @@ -159,13 +192,18 @@ export function main() { })); it("should support nested components", inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
B
' + - '
'; - - compile(temp, [outerWithIndirectNestedComponent]).then( (pvRefs) => { - renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
B
' + + '
', + directives: [outerWithIndirectNestedComponent] + })] + }); + compile(rootEl, 'main').then( (pv) => { + renderer.createView(pv.render); expect(rootEl).toHaveText('OUTER(SIMPLE(AB))'); @@ -174,16 +212,22 @@ export function main() { })); it("should support nesting with content being direct child of a nested component", inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
B
' + - '
C
' + - '
'; - - compile(temp, [outerComponent, manualViewportDirective]).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
B
' + + '
C
' + + '
', + directives: [outerComponent, manualViewportDirective] + })] + }); + compile(rootEl, 'main').then( (pv) => { + var viewRefs = renderer.createView(pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); - var vcProtoViewRef = pvRefs[2]; + var vcProtoViewRef = pv.elementBinders[0].nestedProtoView + .elementBinders[1].nestedProtoView.render; var childViewRef = renderer.createView(vcProtoViewRef)[0]; expect(rootEl).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); @@ -196,16 +240,23 @@ export function main() { })); it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter], (async) => { - var temp = '' + - '
A
' + - '
B
' + - '
C
' + - '
'; - - compile(temp, [conditionalContentComponent, autoViewportDirective]).then( (pvRefs) => { - var viewRefs = renderer.createView(pvRefs[0]); + createRenderer({ + templates: [new Template({ + componentId: 'main', + inline: '' + + '
A
' + + '
B
' + + '
C
' + + '
', + directives: [conditionalContentComponent] + })] + }); + compile(rootEl, 'main').then( (pv) => { + var viewRefs = renderer.createView(pv.render); var vcRef = new ViewContainerRef(viewRefs[2], 0); - var vcProtoViewRef = pvRefs[3]; + var vcProtoViewRef = pv.elementBinders[0].nestedProtoView + .elementBinders[0].nestedProtoView + .elementBinders[0].nestedProtoView.render; var childViewRef = renderer.createView(vcProtoViewRef)[0]; expect(rootEl).toHaveText('(, ABC)'); @@ -299,7 +350,7 @@ var autoViewportDirective = new DirectiveMetadata({ type: DirectiveMetadata.VIEWPORT_TYPE }); -var templates = [ +var componentTemplates = [ new Template({ componentId: 'simple', inline: 'SIMPLE()',