diff --git a/karma-dart.conf.js b/karma-dart.conf.js index 1ffe1b8257..1756386e65 100644 --- a/karma-dart.conf.js +++ b/karma-dart.conf.js @@ -8,6 +8,8 @@ module.exports = function(config) { frameworks: ['dart-unittest'], files: [ + // Init and configure guiness. + {pattern: 'test-init.dart', included: true}, // Unit test files needs to be included. // Karma-dart generates `__adapter_unittest.dart` that imports these files. {pattern: 'modules/*/test/**/*_spec.js', included: true}, diff --git a/modules/angular2/core.js b/modules/angular2/core.js index cad9b91e40..3a6f970937 100644 --- a/modules/angular2/core.js +++ b/modules/angular2/core.js @@ -14,5 +14,5 @@ export * from './src/core/compiler/private_component_location'; export * from './src/core/compiler/view'; export * from './src/core/compiler/view_container'; -export * from './src/core/dom/element'; +export * from './src/core/compiler/ng_element'; diff --git a/modules/angular2/src/core/application.js b/modules/angular2/src/core/application.js index 176689c50f..920b2f3cbf 100644 --- a/modules/angular2/src/core/application.js +++ b/modules/angular2/src/core/application.js @@ -9,12 +9,12 @@ import {ExceptionHandler} from './exception_handler'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from './compiler/template_resolver'; import {DirectiveMetadataReader} from './compiler/directive_metadata_reader'; -import {DirectiveBinding} from './compiler/element_injector'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone'; import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; -import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {XHR} from 'angular2/src/services/xhr'; import {XHRImpl} from 'angular2/src/services/xhr_impl'; import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager'; @@ -27,6 +27,13 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; import {Component} from 'angular2/src/core/annotations/annotations'; import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'; import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; + import { appViewToken, appChangeDetectorToken, @@ -60,7 +67,7 @@ function _injectorBindings(appComponentType): List<Binding> { return element; }, [appComponentAnnotatedTypeToken, appDocumentToken]), bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement, - appComponentAnnotatedType, strategy, eventManager, testability, registry) => { + appComponentAnnotatedType, testability, registry, viewFactory) => { // We need to do this here to ensure that we create Testability and // it's ready on the window for users. @@ -73,18 +80,18 @@ function _injectorBindings(appComponentType): List<Binding> { } return compiler.compileRoot( appElement, - DirectiveBinding.createFromType(appComponentAnnotatedType.type, appComponentAnnotatedType.annotation) + appComponentAnnotatedType.type ).then( (appProtoView) => { // The light Dom of the app element is not considered part of // the angular application. Thus the context and lightDomInjector are // empty. - var view = appProtoView.instantiate(null, eventManager); - view.hydrate(injector, null, null, new Object(), null); + var view = viewFactory.getView(appProtoView); + view.hydrate(injector, null, new Object(), null); return view; }); }, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken, - ShadowDomStrategy, EventManager, Testability, TestabilityRegistry]), + Testability, TestabilityRegistry, ViewFactory]), bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector, [appViewToken]), @@ -98,6 +105,23 @@ function _injectorBindings(appComponentType): List<Binding> { bind(ShadowDomStrategy).toFactory( (styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head), [StyleUrlResolver, appDocumentToken]), + bind(Renderer).toClass(DirectDomRenderer), + bind(rc.Compiler).toClass(rc.DefaultCompiler), + // TODO(tbosch): We need an explicit factory here, as + // we are getting errors in dart2js with mirrors... + bind(rvf.ViewFactory).toFactory( + (capacity, eventManager, shadowDomStrategy) => new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy), + [rvf.VIEW_POOL_CAPACITY, EventManager, ShadowDomStrategy] + ), + bind(rvf.VIEW_POOL_CAPACITY).toValue(100000), + ProtoViewFactory, + // TODO(tbosch): We need an explicit factory here, as + // we are getting errors in dart2js with mirrors... + bind(ViewFactory).toFactory( + (capacity) => new ViewFactory(capacity), + [VIEW_POOL_CAPACITY] + ), + bind(VIEW_POOL_CAPACITY).toValue(100000), Compiler, CompilerCache, TemplateResolver, diff --git a/modules/angular2/src/core/compiler/compiler.js b/modules/angular2/src/core/compiler/compiler.js index 6c382412e9..ed27b82e02 100644 --- a/modules/angular2/src/core/compiler/compiler.js +++ b/modules/angular2/src/core/compiler/compiler.js @@ -3,24 +3,16 @@ import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection'; -import {ChangeDetection, Parser} from 'angular2/change_detection'; - import {DirectiveMetadataReader} from './directive_metadata_reader'; import {Component, Viewport, DynamicComponent, Decorator} from '../annotations/annotations'; import {ProtoView} from './view'; import {DirectiveBinding} from './element_injector'; import {TemplateResolver} from './template_resolver'; import {Template} from '../annotations/template'; -import {ShadowDomStrategy} from './shadow_dom_strategy'; import {ComponentUrlMapper} from './component_url_mapper'; import {ProtoViewFactory} from './proto_view_factory'; import {UrlResolver} from 'angular2/src/services/url_resolver'; -import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; -import {DefaultStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory'; -import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; - -import * as rc from 'angular2/src/render/dom/compiler/compiler'; import * as renderApi from 'angular2/src/render/api'; /** @@ -49,9 +41,7 @@ export class CompilerCache { } -// TODO(tbosch): rename this class to Compiler -// and remove the current Compiler when core uses the render views. -export class NewCompiler { +export class Compiler { _reader: DirectiveMetadataReader; _compilerCache:CompilerCache; _compiling:Map<Type, Promise>; @@ -80,19 +70,19 @@ export class NewCompiler { this._protoViewFactory = protoViewFactory; } - _bindDirective(directive) { - var meta = this._reader.read(directive); + _bindDirective(directiveTypeOrBinding) { + if (directiveTypeOrBinding instanceof DirectiveBinding) { + return directiveTypeOrBinding; + } + var meta = this._reader.read(directiveTypeOrBinding); return DirectiveBinding.createFromType(meta.type, meta.annotation); } // Create a rootView as if the compiler encountered <rootcmp></rootcmp>. // Used for bootstrapping. - compileRoot(elementOrSelector, componentBinding:DirectiveBinding):Promise<ProtoView> { + compileRoot(elementOrSelector, componentTypeOrBinding:any):Promise<ProtoView> { return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => { - return this._compileNestedProtoViews(null, rootRenderPv, [componentBinding], true) - }).then( (rootProtoView) => { - rootProtoView.instantiateInPlace = true; - return rootProtoView; + return this._compileNestedProtoViews(null, rootRenderPv, [this._bindDirective(componentTypeOrBinding)], true); }); } @@ -266,40 +256,3 @@ export class NewCompiler { } } - -// TODO(tbosch): delete this class once we use the render views -/** - * The compiler loads and translates the html templates of components into - * nested ProtoViews. To decompose its functionality it uses - * the render compiler. - * - * @publicModule angular2/template - */ -@Injectable() -export class Compiler extends NewCompiler { - constructor(changeDetection:ChangeDetection, - templateLoader:TemplateLoader, - reader: DirectiveMetadataReader, - parser:Parser, - cache:CompilerCache, - shadowDomStrategy: ShadowDomStrategy, - templateResolver: TemplateResolver, - componentUrlMapper: ComponentUrlMapper, - urlResolver: UrlResolver) { - super( - reader, - cache, - templateResolver, - componentUrlMapper, - urlResolver, - new DirectDomRenderer( - new rc.Compiler( - new DefaultStepFactory(parser, shadowDomStrategy.render), - templateLoader - ), - null, shadowDomStrategy.render - ), - new ProtoViewFactory(changeDetection, shadowDomStrategy) - ); - } -} diff --git a/modules/angular2/src/core/compiler/element_binder.js b/modules/angular2/src/core/compiler/element_binder.js index 3c8e925cb6..dc82640c3f 100644 --- a/modules/angular2/src/core/compiler/element_binder.js +++ b/modules/angular2/src/core/compiler/element_binder.js @@ -8,11 +8,8 @@ export class ElementBinder { protoElementInjector:eiModule.ProtoElementInjector; componentDirective:DirectiveBinding; viewportDirective:DirectiveBinding; - textNodeIndices:List<int>; - hasElementPropertyBindings:boolean; nestedProtoView: viewModule.ProtoView; events:StringMap; - contentTagSelector:string; parent:ElementBinder; index:int; distanceToParent:int; @@ -32,13 +29,7 @@ export class ElementBinder { this.distanceToParent = distanceToParent; // updated later when events are bound this.events = null; - // updated later when text nodes are bound - this.textNodeIndices = null; - // updated later when element properties are bound - this.hasElementPropertyBindings = false; // updated later, so we are able to resolve cycles this.nestedProtoView = null; - // updated later in the compilation pipeline - this.contentTagSelector = null; } } diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js index 900728753f..1566d3a3cb 100644 --- a/modules/angular2/src/core/compiler/element_injector.js +++ b/modules/angular2/src/core/compiler/element_injector.js @@ -6,11 +6,10 @@ import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility'; import {EventEmitter, PropertySetter, Attribute} from 'angular2/src/core/annotations/di'; import * as viewModule from 'angular2/src/core/compiler/view'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; -import {NgElement} from 'angular2/src/core/dom/element'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations'; import {BindingPropagationConfig} from 'angular2/change_detection'; import * as pclModule from 'angular2/src/core/compiler/private_component_location'; -import {setterFactory} from 'angular2/src/render/dom/view/property_setter_factory'; var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10; @@ -268,8 +267,8 @@ export class ProtoElementInjector { } } - instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector { - return new ElementInjector(this, parent, host); + instantiate(parent:ElementInjector):ElementInjector { + return new ElementInjector(this, parent); } directParent(): ProtoElementInjector { @@ -339,21 +338,12 @@ export class ElementInjector extends TreeNode { _privateComponent; _privateComponentBinding:DirectiveBinding; - constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) { + constructor(proto:ProtoElementInjector, parent:ElementInjector) { super(parent); - if (isPresent(parent) && isPresent(host)) { - throw new BaseException('Only either parent or host is allowed'); - } - this._host = null; // needed to satisfy Dart - if (isPresent(parent)) { - this._host = parent._host; - } else { - this._host = host; - } - this._proto = proto; //we cannot call clearDirectives because fields won't be detected + this._host = null; this._preBuiltObjects = null; this._lightDomAppInjector = null; this._shadowDomAppInjector = null; @@ -371,6 +361,7 @@ export class ElementInjector extends TreeNode { } clearDirectives() { + this._host = null; this._preBuiltObjects = null; this._lightDomAppInjector = null; this._shadowDomAppInjector = null; @@ -406,7 +397,8 @@ export class ElementInjector extends TreeNode { this._constructionCounter = 0; } - instantiateDirectives(lightDomAppInjector:Injector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) { + instantiateDirectives(lightDomAppInjector:Injector, host:ElementInjector, shadowDomAppInjector:Injector, preBuiltObjects:PreBuiltObjects) { + this._host = host; this._checkShadowDomAppInjector(shadowDomAppInjector); this._preBuiltObjects = preBuiltObjects; @@ -456,10 +448,6 @@ export class ElementInjector extends TreeNode { return pb !== _undefined && isPresent(pb); } - forElement(el):boolean { - return this._preBuiltObjects.element.domElement === el; - } - /** Gets the NgElement associated with this ElementInjector */ getNgElement() { return this._preBuiltObjects.element; @@ -538,6 +526,10 @@ export class ElementInjector extends TreeNode { return obj; } + getBoundElementIndex() { + return this._proto.index; + } + _getByDependency(dep:DirectiveDependency, requestor:Key) { if (isPresent(dep.eventEmitterName)) return this._buildEventEmitter(dep); if (isPresent(dep.propSetterName)) return this._buildPropSetter(dep); @@ -553,10 +545,12 @@ export class ElementInjector extends TreeNode { } _buildPropSetter(dep) { - var ngElement = this._getPreBuiltObjectByKeyId(StaticKeys.instance().ngElementId); - var domElement = ngElement.domElement; - var setter = setterFactory(dep.propSetterName); - return function(v) { setter(domElement, v) }; + var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId); + var renderer = view.proto.renderer; + var index = this._proto.index; + return function(v) { + renderer.setElementProperty(view.render, index, dep.propSetterName, v); + }; } _buildAttribute(dep): string { @@ -582,7 +576,6 @@ export class ElementInjector extends TreeNode { */ _getByKey(key:Key, depth:number, optional:boolean, requestor:Key) { var ei = this; - if (! this._shouldIncludeSelf(depth)) { depth -= ei._proto.distanceToParent; ei = ei._parent; @@ -631,7 +624,7 @@ export class ElementInjector extends TreeNode { if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig; if (keyId === staticKeys.privateComponentLocationId) { - return new pclModule.PrivateComponentLocation(this, this._preBuiltObjects.element, this._preBuiltObjects.view); + return new pclModule.PrivateComponentLocation(this, this._preBuiltObjects.view); } //TODO add other objects as needed diff --git a/modules/angular2/src/core/compiler/ng_element.js b/modules/angular2/src/core/compiler/ng_element.js new file mode 100644 index 0000000000..af496c0d26 --- /dev/null +++ b/modules/angular2/src/core/compiler/ng_element.js @@ -0,0 +1,34 @@ +import {DOM} from 'angular2/src/dom/dom_adapter'; +import {normalizeBlank} from 'angular2/src/facade/lang'; +import * as viewModule from '../compiler/view'; +import {DirectDomViewRef} from 'angular2/src/render/dom/direct_dom_renderer'; + +/** + * Allows direct access to the underlying DOM element. + * + * Attention: NgElement will be replaced by a different concept + * for accessing an element in a way that is compatible with the render layer. + * + * @publicModule angular2/angular2 + */ +export class NgElement { + _view:viewModule.View; + _boundElementIndex:number; + + constructor(view, boundElementIndex) { + this._view = view; + this._boundElementIndex = boundElementIndex; + } + + // TODO(tbosch): Here we expose the real DOM element. + // We need a more general way to read/write to the DOM element + // via a proper abstraction in the render layer + get domElement() { + var domViewRef:DirectDomViewRef = this._view.render; + return domViewRef.delegate.boundElements[this._boundElementIndex]; + } + + getAttribute(name:string) { + return normalizeBlank(DOM.getAttribute(this.domElement, name)); + } +} \ No newline at end of file diff --git a/modules/angular2/src/core/compiler/private_component_loader.js b/modules/angular2/src/core/compiler/private_component_loader.js index 7bb69157ab..49da82bd18 100644 --- a/modules/angular2/src/core/compiler/private_component_loader.js +++ b/modules/angular2/src/core/compiler/private_component_loader.js @@ -1,7 +1,6 @@ import {Compiler} from './compiler'; -import {ShadowDomStrategy} from './shadow_dom_strategy'; +import {ViewFactory} from './view_factory'; import {Injectable} from 'angular2/di'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {Component} from 'angular2/src/core/annotations/annotations'; import {PrivateComponentLocation} from './private_component_location'; @@ -11,17 +10,14 @@ import {Type, stringify, BaseException} from 'angular2/src/facade/lang'; @Injectable() export class PrivateComponentLoader { compiler:Compiler; - shadowDomStrategy:ShadowDomStrategy; - eventManager:EventManager; directiveMetadataReader:DirectiveMetadataReader; + viewFactory:ViewFactory; - constructor(compiler:Compiler, shadowDomStrategy:ShadowDomStrategy, - eventManager:EventManager, directiveMetadataReader:DirectiveMetadataReader) { + constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader, viewFactory:ViewFactory) { this.compiler = compiler; - this.shadowDomStrategy = shadowDomStrategy; - this.eventManager = eventManager; this.directiveMetadataReader = directiveMetadataReader; + this.viewFactory = viewFactory; } load(type:Type, location:PrivateComponentLocation) { @@ -33,10 +29,10 @@ export class PrivateComponentLoader { return this.compiler.compile(type).then((componentProtoView) => { location.createComponent( + this.viewFactory, type, annotation, - componentProtoView, - this.eventManager, - this.shadowDomStrategy); + componentProtoView + ); }); } } diff --git a/modules/angular2/src/core/compiler/private_component_location.js b/modules/angular2/src/core/compiler/private_component_location.js index 86b7c01ff6..e8282be9da 100644 --- a/modules/angular2/src/core/compiler/private_component_location.js +++ b/modules/angular2/src/core/compiler/private_component_location.js @@ -1,33 +1,28 @@ import {Directive} from 'angular2/src/core/annotations/annotations' -import {NgElement} from 'angular2/src/core/dom/element'; import * as viewModule from './view'; import * as eiModule from './element_injector'; -import {ShadowDomStrategy} from './shadow_dom_strategy'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import {ListWrapper} from 'angular2/src/facade/collection'; import {Type} from 'angular2/src/facade/lang'; - +import * as vfModule from './view_factory'; export class PrivateComponentLocation { _elementInjector:eiModule.ElementInjector; - _elt:NgElement; _view:viewModule.View; - constructor(elementInjector:eiModule.ElementInjector, elt:NgElement, view:viewModule.View){ + constructor(elementInjector:eiModule.ElementInjector, view:viewModule.View){ this._elementInjector = elementInjector; - this._elt = elt; this._view = view; } - createComponent(type:Type, annotation:Directive, componentProtoView:viewModule.ProtoView, - eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) { + createComponent(viewFactory: vfModule.ViewFactory, type:Type, annotation:Directive, componentProtoView:viewModule.ProtoView) { var context = this._elementInjector.createPrivateComponent(type, annotation); - var view = componentProtoView.instantiate(this._elementInjector, eventManager); - view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, null, context, null); - - shadowDomStrategy.attachTemplate(this._elt.domElement, view); + var view = viewFactory.getView(componentProtoView); + view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, context, null); + this._view.proto.renderer.setDynamicComponentView( + this._view.render, this._elementInjector.getBoundElementIndex(), view.render + ); ListWrapper.push(this._view.componentChildViews, view); this._view.changeDetector.addChild(view.changeDetector); } diff --git a/modules/angular2/src/core/compiler/proto_view_factory.js b/modules/angular2/src/core/compiler/proto_view_factory.js index 9a843ff7d0..a7a4a3ecdc 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.js +++ b/modules/angular2/src/core/compiler/proto_view_factory.js @@ -1,23 +1,23 @@ +import {Injectable} from 'angular2/di'; import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; import {isPresent, isBlank} from 'angular2/src/facade/lang'; import {reflector} from 'angular2/src/reflection/reflection'; import {ChangeDetection} from 'angular2/change_detection'; -import {ShadowDomStrategy} from './shadow_dom_strategy'; import {Component, Viewport, DynamicComponent} from '../annotations/annotations'; import * as renderApi from 'angular2/src/render/api'; -import {DirectDomProtoViewRef} from 'angular2/src/render/dom/direct_dom_renderer'; import {ProtoView} from './view'; import {ProtoElementInjector, DirectiveBinding} from './element_injector'; +@Injectable() export class ProtoViewFactory { _changeDetection:ChangeDetection; - _shadowDomStrategy:ShadowDomStrategy; + _renderer:renderApi.Renderer; - constructor(changeDetection, shadowDomStrategy) { + constructor(changeDetection:ChangeDetection, renderer:renderApi.Renderer) { this._changeDetection = changeDetection; - this._shadowDomStrategy = shadowDomStrategy; + this._renderer = renderer; } createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoView, directives:List<DirectiveBinding>):ProtoView { @@ -30,13 +30,10 @@ export class ProtoViewFactory { 'dummy', componentAnnotation.changeDetection ); } - var domProtoView = this._getDomProtoView(renderProtoView.render); - var protoView = new ProtoView(renderProtoView.render, domProtoView.element, protoChangeDetector, - this._shadowDomStrategy, null); + var protoView = new ProtoView(this._renderer, renderProtoView.render, protoChangeDetector); for (var i=0; i<renderProtoView.elementBinders.length; i++) { var renderElementBinder = renderProtoView.elementBinders[i]; - var domElementBinder = domProtoView.elementBinders[i]; var sortedDirectives = new SortedDirectives(renderElementBinder.directives, directives); var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance( i, protoView.elementBinders, renderProtoView.elementBinders @@ -46,7 +43,7 @@ export class ProtoViewFactory { sortedDirectives, renderElementBinder ); this._createElementBinder( - protoView, renderElementBinder, domElementBinder, protoElementInjector, sortedDirectives + protoView, renderElementBinder, protoElementInjector, sortedDirectives ); this._createDirectiveBinders(protoView, sortedDirectives); } @@ -56,11 +53,6 @@ export class ProtoViewFactory { return protoView; } - // This method is needed to make DartAnalyzer happy - _getDomProtoView(protoViewRef: DirectDomProtoViewRef) { - return protoViewRef.delegate; - } - _findParentProtoElementInjectorWithDistance(binderIndex, elementBinders, renderElementBinders) { var distance = 0; do { @@ -105,7 +97,7 @@ export class ProtoViewFactory { return protoElementInjector; } - _createElementBinder(protoView, renderElementBinder, domElementBinder, protoElementInjector, sortedDirectives) { + _createElementBinder(protoView, renderElementBinder, protoElementInjector, sortedDirectives) { var parent = null; if (renderElementBinder.parentIndex !== -1) { parent = protoView.elementBinders[renderElementBinder.parentIndex]; @@ -117,14 +109,13 @@ export class ProtoViewFactory { sortedDirectives.componentDirective, sortedDirectives.viewportDirective ); - elBinder.contentTagSelector = domElementBinder.contentTagSelector; // text nodes for (var i=0; i<renderElementBinder.textBindings.length; i++) { - protoView.bindTextNode(domElementBinder.textNodeIndices[i], renderElementBinder.textBindings[i].ast); + protoView.bindTextNode(renderElementBinder.textBindings[i].ast); } // element properties MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => { - protoView.bindElementProperty(astWithSource.ast, propertyName, MapWrapper.get(domElementBinder.propertySetters, propertyName)); + protoView.bindElementProperty(astWithSource.ast, propertyName); }); // events MapWrapper.forEach(renderElementBinder.eventBindings, (astWithSource, eventName) => { diff --git a/modules/angular2/src/core/compiler/shadow_dom_emulation/content_tag.js b/modules/angular2/src/core/compiler/shadow_dom_emulation/content_tag.js deleted file mode 100644 index 0cb1210176..0000000000 --- a/modules/angular2/src/core/compiler/shadow_dom_emulation/content_tag.js +++ /dev/null @@ -1,88 +0,0 @@ -import * as ldModule from './light_dom'; -import {Inject, Injectable} from 'angular2/di'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {isPresent} from 'angular2/src/facade/lang'; -import {List, ListWrapper} from 'angular2/src/facade/collection'; - -class ContentStrategy { - nodes:List; - insert(nodes:List){} -} - -/** - * An implementation of the content tag that is used by transcluding components. - * It is used when the content tag is not a direct child of another component, - * and thus does not affect redistribution. - */ -@Injectable() -class RenderedContent extends ContentStrategy { - beginScript; - endScript; - - constructor(contentEl) { - super(); - this.beginScript = contentEl; - this.endScript = DOM.nextSibling(this.beginScript); - this.nodes = []; - } - - // Inserts the nodes in between the start and end scripts. - // Previous content is removed. - insert(nodes:List) { - this.nodes = nodes; - DOM.insertAllBefore(this.endScript, nodes); - this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this.endScript : nodes[0]); - } - - _removeNodesUntil(node) { - var p = DOM.parentElement(this.beginScript); - for (var next = DOM.nextSibling(this.beginScript); - next !== node; - next = DOM.nextSibling(this.beginScript)) { - DOM.removeChild(p, next); - } - } -} - -/** - * An implementation of the content tag that is used by transcluding components. - * It is used when the content tag is a direct child of another component, - * and thus does not get rendered but only affect the distribution of its parent component. - */ -class IntermediateContent extends ContentStrategy { - destinationLightDom:ldModule.LightDom; - - constructor(destinationLightDom:ldModule.LightDom) { - super(); - this.destinationLightDom = destinationLightDom; - this.nodes = []; - } - - insert(nodes:List) { - this.nodes = nodes; - this.destinationLightDom.redistribute(); - } -} - - -export class Content { - select:string; - _strategy:ContentStrategy; - contentStartElement; - - constructor(destinationLightDom:ldModule.LightDom, contentStartEl, selector:string) { - this.select = selector; - this.contentStartElement = contentStartEl; - this._strategy = isPresent(destinationLightDom) ? - new IntermediateContent(destinationLightDom) : - new RenderedContent(contentStartEl); - } - - nodes():List { - return this._strategy.nodes; - } - - insert(nodes:List) { - this._strategy.insert(nodes); - } -} diff --git a/modules/angular2/src/core/compiler/shadow_dom_emulation/light_dom.js b/modules/angular2/src/core/compiler/shadow_dom_emulation/light_dom.js deleted file mode 100644 index 82a915ae4e..0000000000 --- a/modules/angular2/src/core/compiler/shadow_dom_emulation/light_dom.js +++ /dev/null @@ -1,140 +0,0 @@ -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {List, ListWrapper} from 'angular2/src/facade/collection'; -import {isBlank, isPresent} from 'angular2/src/facade/lang'; - -import * as viewModule from '../view'; -import {Content} from './content_tag'; - -export class DestinationLightDom {} - - -class _Root { - node; - viewContainer; - content; - - constructor(node, viewContainer, content) { - this.node = node; - this.viewContainer = viewContainer; - this.content = content; - } -} - -// TODO: LightDom should implement DestinationLightDom -// once interfaces are supported -export class LightDom { - // The light DOM of the element is enclosed inside the lightDomView - lightDomView:viewModule.View; - // The shadow DOM - shadowDomView:viewModule.View; - // The nodes of the light DOM - nodes:List; - roots:List<_Root>; - - constructor(lightDomView:viewModule.View, shadowDomView:viewModule.View, element) { - this.lightDomView = lightDomView; - this.shadowDomView = shadowDomView; - this.nodes = DOM.childNodesAsList(element); - - this.roots = null; - } - - redistribute() { - var tags = this.contentTags(); - if (tags.length > 0) { - redistributeNodes(tags, this.expandedDomNodes()); - } - } - - contentTags(): List<Content> { - return this._collectAllContentTags(this.shadowDomView, []); - } - - // Collects the Content directives from the view and all its child views - _collectAllContentTags(view: viewModule.View, acc:List<Content>):List<Content> { - var contentTags = view.contentTags; - var vcs = view.viewContainers; - for (var i=0; i<vcs.length; i++) { - var vc = vcs[i]; - var contentTag = contentTags[i]; - if (isPresent(contentTag)) { - ListWrapper.push(acc, contentTag); - } - if (isPresent(vc)) { - ListWrapper.forEach(vc.contentTagContainers(), (view) => { - this._collectAllContentTags(view, acc); - }); - } - } - return acc; - } - - // Collects the nodes of the light DOM by merging: - // - nodes from enclosed ViewContainers, - // - nodes from enclosed content tags, - // - plain DOM nodes - expandedDomNodes():List { - var res = []; - - var roots = this._roots(); - for (var i = 0; i < roots.length; ++i) { - - var root = roots[i]; - - if (isPresent(root.viewContainer)) { - res = ListWrapper.concat(res, root.viewContainer.nodes()); - } else if (isPresent(root.content)) { - res = ListWrapper.concat(res, root.content.nodes()); - } else { - ListWrapper.push(res, root.node); - } - } - return res; - } - - // Returns a list of Roots for all the nodes of the light DOM. - // The Root object contains the DOM node and its corresponding injector (could be null). - _roots() { - if (isPresent(this.roots)) return this.roots; - - var viewContainers = this.lightDomView.viewContainers; - var contentTags = this.lightDomView.contentTags; - - this.roots = ListWrapper.map(this.nodes, (n) => { - var foundVc = null; - var foundContentTag = null; - for (var i=0; i<viewContainers.length; i++) { - var vc = viewContainers[i]; - var contentTag = contentTags[i]; - if (isPresent(vc) && vc.templateElement === n) { - foundVc = vc; - } - if (isPresent(contentTag) && contentTag.contentStartElement === n) { - foundContentTag = contentTag; - } - } - return new _Root(n, foundVc, foundContentTag); - }); - - return this.roots; - } -} - -// Projects the light DOM into the shadow DOM -function redistributeNodes(contents:List<Content>, nodes:List) { - for (var i = 0; i < contents.length; ++i) { - var content = contents[i]; - var select = content.select; - var matchSelector = (n) => DOM.elementMatches(n, select); - - // Empty selector is identical to <content/> - if (select.length === 0) { - content.insert(nodes); - ListWrapper.clear(nodes); - } else { - var matchingNodes = ListWrapper.filter(nodes, matchSelector); - content.insert(matchingNodes); - ListWrapper.removeAll(nodes, matchingNodes); - } - } -} diff --git a/modules/angular2/src/core/compiler/shadow_dom_strategy.js b/modules/angular2/src/core/compiler/shadow_dom_strategy.js deleted file mode 100644 index 72cca0a9a6..0000000000 --- a/modules/angular2/src/core/compiler/shadow_dom_strategy.js +++ /dev/null @@ -1,114 +0,0 @@ -import {Injectable} from 'angular2/di'; - -import {stringify} from 'angular2/src/facade/lang'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import * as viewModule from './view'; -import {LightDom} from './shadow_dom_emulation/light_dom'; -import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; -import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; - -// temporal import while we migrated the views over -import * as sds from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; -import * as nsds from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; -import * as eusds from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; -import * as essds from 'angular2/src/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy'; - -/** - * @publicModule angular2/template - */ -export class ShadowDomStrategy { - render: sds.ShadowDomStrategy; - - attachTemplate(el, view:viewModule.View) {} - constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { return null; } - - shimAppElement(componentType, insertionElement) { - this.render.processElement(null, stringify(componentType), insertionElement); - } -} - -/** - * This strategy emulates the Shadow DOM for the templates, styles **excluded**: - * - components templates are added as children of their component element, - * - styles are moved from the templates to the styleHost (i.e. the document head). - * - * Notes: - * - styles are **not** scoped to their component and will apply to the whole document, - * - you can **not** use shadow DOM specific selectors in the styles - * - * @publicModule angular2/template - */ -@Injectable() -export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy { - - constructor(styleUrlResolver: StyleUrlResolver, styleHost) { - super(); - this.render = new eusds.EmulatedUnscopedShadowDomStrategy(styleUrlResolver, styleHost); - } - - attachTemplate(el, view:viewModule.View) { - DOM.clearNodes(el); - _moveViewNodesIntoParent(el, view); - } - - constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { - return new LightDom(lightDomView, shadowDomView, el); - } -} - -/** - * This strategy emulates the Shadow DOM for the templates, styles **included**: - * - components templates are added as children of their component element, - * - both the template and the styles are modified so that styles are scoped to the component - * they belong to, - * - styles are moved from the templates to the styleHost (i.e. the document head). - * - * Notes: - * - styles are scoped to their component and will apply only to it, - * - a common subset of shadow DOM selectors are supported, - * - see `ShadowCss` for more information and limitations. - * - * @publicModule angular2/template - */ -@Injectable() -export class EmulatedScopedShadowDomStrategy extends ShadowDomStrategy { - - constructor(styleInliner: StyleInliner, styleUrlResolver: StyleUrlResolver, styleHost) { - super(); - this.render = new essds.EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, styleHost); - } - - attachTemplate(el, view:viewModule.View) { - DOM.clearNodes(el); - _moveViewNodesIntoParent(el, view); - } - - constructLightDom(lightDomView:viewModule.View, shadowDomView:viewModule.View, el): LightDom { - return new LightDom(lightDomView, shadowDomView, el); - } -} - -/** - * This strategies uses the native Shadow DOM support. - * - * The templates for the component are inserted in a Shadow Root created on the component element. - * Hence they are strictly isolated. - */ -@Injectable() -export class NativeShadowDomStrategy extends ShadowDomStrategy { - - constructor(styleUrlResolver: StyleUrlResolver) { - super(); - this.render = new nsds.NativeShadowDomStrategy(styleUrlResolver); - } - - attachTemplate(el, view:viewModule.View){ - _moveViewNodesIntoParent(DOM.createShadowRoot(el), view); - } -} - -function _moveViewNodesIntoParent(parent, view) { - for (var i = 0; i < view.nodes.length; ++i) { - DOM.appendChild(parent, view.nodes[i]); - } -} diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index 436d0e1941..daefe68cee 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -1,5 +1,3 @@ -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {Promise} from 'angular2/src/facade/async'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, ChangeRecord, BindingRecord, BindingPropagationConfig, uninitialized} from 'angular2/change_detection'; @@ -9,76 +7,51 @@ import {ElementBinder} from './element_binder'; import {SetterFn} from 'angular2/src/reflection/types'; import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {Injector} from 'angular2/di'; -import {NgElement} from 'angular2/src/core/dom/element'; import {ViewContainer} from './view_container'; -import {LightDom} from './shadow_dom_emulation/light_dom'; -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'; - -// TODO(rado): make this configurable/smarter. -var VIEW_POOL_CAPACITY = 10000; -var VIEW_POOL_PREFILL = 0; - /** * Const of making objects: http://jsperf.com/instantiate-size-of-object * * @publicModule angular2/template */ @IMPLEMENTS(ChangeDispatcher) +// TODO(tbosch): this is not supported in dart2js (no '.' is allowed) +// @IMPLEMENTS(renderApi.EventDispatcher) export class View { + render:renderApi.ViewRef; /// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector rootElementInjectors:List<ElementInjector>; elementInjectors:List<ElementInjector>; - bindElements:List; - textNodes:List; changeDetector:ChangeDetector; - /// When the view is part of render tree, the DocumentFragment is empty, which is why we need - /// to keep track of the nodes. - nodes:List; componentChildViews: List<View>; viewContainers: List<ViewContainer>; - contentTags: List<Content>; preBuiltObjects: List<PreBuiltObjects>; - lightDoms: List<LightDom>; proto: ProtoView; context: any; locals:Locals; - constructor(proto:ProtoView, nodes:List, protoLocals:Map) { + constructor(proto:ProtoView, protoLocals:Map) { + this.render = null; this.proto = proto; - this.nodes = nodes; this.changeDetector = null; this.elementInjectors = null; this.rootElementInjectors = null; - this.textNodes = null; - this.bindElements = null; this.componentChildViews = null; this.viewContainers = null; - this.contentTags = null; this.preBuiltObjects = null; - this.lightDoms = null; this.context = null; this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this } - init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List, - viewContainers:List, contentTags:List, preBuiltObjects:List, componentChildViews:List, lightDoms:List<LightDom>) { + init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, + viewContainers:List, preBuiltObjects:List, componentChildViews:List) { this.changeDetector = changeDetector; this.elementInjectors = elementInjectors; this.rootElementInjectors = rootElementInjectors; - this.textNodes = textNodes; - this.bindElements = bindElements; this.viewContainers = viewContainers; - this.contentTags = contentTags; this.preBuiltObjects = preBuiltObjects; this.componentChildViews = componentChildViews; - this.lightDoms = lightDoms; } setLocal(contextName: string, value) { @@ -124,16 +97,33 @@ export class View { * A call to hydrate/dehydrate does not attach/detach the view from the view * tree. */ - hydrate(appInjector: Injector, hostElementInjector: ElementInjector, hostLightDom: LightDom, + hydrate(appInjector: Injector, hostElementInjector: ElementInjector, context: Object, locals:Locals) { + var renderComponentViewRefs = this.proto.renderer.createView(this.proto.render); + this.internalHydrateRecurse(renderComponentViewRefs, 0, appInjector, hostElementInjector, context, locals); + } + + dehydrate() { + var render = this.render; + this.internalDehydrateRecurse(); + this.proto.renderer.destroyView(render); + } + + internalHydrateRecurse( + renderComponentViewRefs:List<renderApi.ViewRef>, + renderComponentIndex:number, + appInjector: Injector, hostElementInjector: ElementInjector, + context: Object, locals:Locals):number { if (this.hydrated()) throw new BaseException('The view is already hydrated.'); + + this.render = renderComponentViewRefs[renderComponentIndex++]; this._hydrateContext(context, locals); // viewContainers for (var i = 0; i < this.viewContainers.length; i++) { var vc = this.viewContainers[i]; if (isPresent(vc)) { - vc.hydrate(appInjector, hostElementInjector, hostLightDom); + vc.internalHydrateRecurse(new renderApi.ViewContainerRef(this.render, i), appInjector, hostElementInjector); } } @@ -158,7 +148,7 @@ export class View { // elementInjectors var elementInjector = this.elementInjectors[i]; if (isPresent(elementInjector)) { - elementInjector.instantiateDirectives(appInjector, shadowDomAppInjector, this.preBuiltObjects[i]); + elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, this.preBuiltObjects[i]); // The exporting of $implicit is a special case. Since multiple elements will all export // the different values as $implicit, directly assign $implicit bindings to the variable @@ -167,30 +157,32 @@ export class View { if (elementInjector.isExportingComponent()) { this.locals.set(exportImplicitName, elementInjector.getComponent()); } else if (elementInjector.isExportingElement()) { - this.locals.set(exportImplicitName, elementInjector.getNgElement().domElement); + this.locals.set(exportImplicitName, elementInjector.getNgElement()); } } if (isPresent(binders[i].nestedProtoView) && isPresent(componentDirective)) { - this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector, - elementInjector, this.lightDoms[i], elementInjector.getComponent(), null); - } - } - - for (var i = 0; i < this.lightDoms.length; ++i) { - var lightDom = this.lightDoms[i]; - if (isPresent(lightDom)) { - lightDom.redistribute(); + renderComponentIndex = this.componentChildViews[componentChildViewIndex].internalHydrateRecurse( + renderComponentViewRefs, + renderComponentIndex, + shadowDomAppInjector, + elementInjector, + elementInjector.getComponent(), + null + ); + componentChildViewIndex++; } } + this.proto.renderer.setEventDispatcher(this.render, this); + return renderComponentIndex; } - dehydrate() { + internalDehydrateRecurse() { // Note: preserve the opposite order of the hydration process. // componentChildViews for (var i = 0; i < this.componentChildViews.length; i++) { - this.componentChildViews[i].dehydrate(); + this.componentChildViews[i].internalDehydrateRecurse(); } // elementInjectors @@ -205,11 +197,13 @@ export class View { for (var i = 0; i < this.viewContainers.length; i++) { var vc = this.viewContainers[i]; if (isPresent(vc)) { - vc.dehydrate(); + vc.internalDehydrateRecurse(); } } } + this.render = null; + this._dehydrateContext(); } @@ -223,11 +217,9 @@ export class View { * @param {int} binderIndex */ triggerEventHandlers(eventName: string, eventObj, binderIndex: int) { - var handlers = this.proto.eventHandlers[binderIndex]; - if (isBlank(handlers)) return; - var handler = StringMapWrapper.get(handlers, eventName); - if (isBlank(handler)) return; - handler(eventObj, this); + var locals = MapWrapper.create(); + MapWrapper.set(locals, '$event', eventObj); + this.dispatchEvent(binderIndex, eventName, locals); } onAllChangesDone(directiveMemento:DirectiveMemento) { @@ -245,15 +237,39 @@ export class View { if (memento instanceof DirectiveBindingMemento) { var directiveMemento:DirectiveBindingMemento = memento; directiveMemento.invoke(currentValue, this.elementInjectors); - } else if (memento instanceof ElementBindingMemento) { var elementMemento:ElementBindingMemento = memento; - elementMemento.invoke(currentValue, this.bindElements); - + this.proto.renderer.setElementProperty( + this.render, elementMemento.elementIndex, elementMemento.propertyName, currentValue + ); } else { // we know it refers to _textNodes. var textNodeIndex:number = memento; - DOM.setText(this.textNodes[textNodeIndex], currentValue); + this.proto.renderer.setText(this.render, textNodeIndex, currentValue); + } + } + + // implementation of EventDispatcher#dispatchEvent + dispatchEvent( + elementIndex:number, eventName:string, locals:Map<string, any> + ):void { + // Most of the time the event will be fired only when the view is in the live document. + // However, in a rare circumstance the view might get dehydrated, in between the event + // queuing up and firing. + if (this.hydrated()) { + var elBinder = this.proto.elementBinders[elementIndex]; + if (isBlank(elBinder.events)) return; + var eventMap = elBinder.events[eventName]; + if (isBlank(eventName)) return; + MapWrapper.forEach(eventMap, (expr, directiveIndex) => { + var context; + if (directiveIndex === -1) { + context = this.context; + } else { + context = this.elementInjectors[elementIndex].getDirectiveAtIndex(directiveIndex); + } + expr.eval(context, new Locals(this.locals, locals)); + }); } } } @@ -263,21 +279,11 @@ export class View { * @publicModule angular2/template */ export class ProtoView { - element; elementBinders:List<ElementBinder>; protoChangeDetector:ProtoChangeDetector; variableBindings: Map; protoLocals:Map; textNodesWithBindingCount:int; - elementsWithBindingCount:int; - instantiateInPlace:boolean; - rootBindingOffset:int; - isTemplateElement:boolean; - shadowDomStrategy: ShadowDomStrategy; - _viewPool: ViewPool; - stylePromises: List<Promise>; - // List<Map<eventName, handler>>, indexed by binder index - eventHandlers:List; bindingRecords:List; parentProtoView:ProtoView; _variableBindings:List; @@ -285,58 +291,36 @@ export class ProtoView { _directiveMementosMap:Map; _directiveMementos:List; render:renderApi.ProtoViewRef; + renderer:renderApi.Renderer; constructor( + renderer:renderApi.Renderer, render:renderApi.ProtoViewRef, - template, - protoChangeDetector:ProtoChangeDetector, - shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) { + protoChangeDetector:ProtoChangeDetector) { + this.renderer = renderer; this.render = render; - this.element = template; this.elementBinders = []; this.variableBindings = MapWrapper.create(); this.protoLocals = MapWrapper.create(); this.protoChangeDetector = protoChangeDetector; - this.parentProtoView = parentProtoView; + this.parentProtoView = null; this.textNodesWithBindingCount = 0; - this.elementsWithBindingCount = 0; - this.instantiateInPlace = false; - this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) - ? 1 : 0; - this.isTemplateElement = DOM.isTemplateElement(this.element); - this.shadowDomStrategy = shadowDomStrategy; - this._viewPool = new ViewPool(VIEW_POOL_CAPACITY); - this.stylePromises = []; - this.eventHandlers = []; this.bindingRecords = []; this._directiveMementosMap = MapWrapper.create(); this._variableBindings = null; this._directiveMementos = null; } - // TODO(rado): hostElementInjector should be moved to hydrate phase. - instantiate(hostElementInjector: ElementInjector, eventManager: EventManager):View { - if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector, eventManager); - var view = this._viewPool.pop(); - return isPresent(view) ? view : this._instantiate(hostElementInjector, eventManager); - } - - _preFillPool(hostElementInjector: ElementInjector, eventManager: EventManager) { - for (var i = 0; i < VIEW_POOL_PREFILL; i++) { - this._viewPool.push(this._instantiate(hostElementInjector, eventManager)); - } - } - //TODO: Tobias or Victor. Moving it into the constructor. // this work should be done the constructor of ProtoView once we separate // ProtoView and ProtoViewBuilder - _getVariableBindings() { + getVariableBindings() { if (isPresent(this._variableBindings)) { return this._variableBindings; } this._variableBindings = isPresent(this.parentProtoView) ? - ListWrapper.clone(this.parentProtoView._getVariableBindings()) : []; + ListWrapper.clone(this.parentProtoView.getVariableBindings()) : []; MapWrapper.forEach(this.protoLocals, (v, local) => { ListWrapper.push(this._variableBindings, local); @@ -348,7 +332,7 @@ export class ProtoView { //TODO: Tobias or Victor. Moving it into the constructor. // this work should be done the constructor of ProtoView once we separate // ProtoView and ProtoViewBuilder - _getDirectiveMementos() { + getDirectiveMementos() { if (isPresent(this._directiveMementos)) { return this._directiveMementos; } @@ -367,185 +351,6 @@ export class ProtoView { return this._directiveMementos; } - _instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View { - var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element); - var elementsWithBindingsDynamic; - if (this.isTemplateElement) { - elementsWithBindingsDynamic = DOM.querySelectorAll(DOM.content(rootElementClone), NG_BINDING_CLASS_SELECTOR); - } else { - elementsWithBindingsDynamic= DOM.getElementsByClassName(rootElementClone, NG_BINDING_CLASS); - } - - var elementsWithBindings = ListWrapper.createFixedSize(elementsWithBindingsDynamic.length); - for (var binderIdx = 0; binderIdx < elementsWithBindingsDynamic.length; ++binderIdx) { - elementsWithBindings[binderIdx] = elementsWithBindingsDynamic[binderIdx]; - } - - var viewNodes; - if (this.isTemplateElement) { - var childNode = DOM.firstChild(DOM.content(rootElementClone)); - viewNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in ProtoView - // Note: An explicit loop is the fastest way to convert a DOM array into a JS array! - while(childNode != null) { - ListWrapper.push(viewNodes, childNode); - childNode = DOM.nextSibling(childNode); - } - } else { - viewNodes = [rootElementClone]; - } - - var view = new View(this, viewNodes, this.protoLocals); - var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords, - this._getVariableBindings(), this._getDirectiveMementos()); - - var binders = this.elementBinders; - var elementInjectors = ListWrapper.createFixedSize(binders.length); - var eventHandlers = ListWrapper.createFixedSize(binders.length); - var rootElementInjectors = []; - var textNodes = []; - var elementsWithPropertyBindings = []; - var preBuiltObjects = ListWrapper.createFixedSize(binders.length); - var viewContainers = ListWrapper.createFixedSize(binders.length); - var contentTags = ListWrapper.createFixedSize(binders.length); - var componentChildViews = []; - var lightDoms = ListWrapper.createFixedSize(binders.length); - - for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { - var binder = binders[binderIdx]; - var element; - if (binderIdx === 0 && this.rootBindingOffset === 1) { - element = rootElementClone; - } else { - element = elementsWithBindings[binderIdx - this.rootBindingOffset]; - } - var elementInjector = null; - - // elementInjectors and rootElementInjectors - var protoElementInjector = binder.protoElementInjector; - if (isPresent(protoElementInjector)) { - if (isPresent(protoElementInjector.parent)) { - var parentElementInjector = elementInjectors[protoElementInjector.parent.index]; - elementInjector = protoElementInjector.instantiate(parentElementInjector, null); - } else { - elementInjector = protoElementInjector.instantiate(null, hostElementInjector); - ListWrapper.push(rootElementInjectors, elementInjector); - } - } - elementInjectors[binderIdx] = elementInjector; - - if (binder.hasElementPropertyBindings) { - ListWrapper.push(elementsWithPropertyBindings, element); - } - - // textNodes - var textNodeIndices = binder.textNodeIndices; - if (isPresent(textNodeIndices)) { - var childNode = DOM.firstChild(DOM.templateAwareRoot(element)); - for (var j = 0, k = 0; j < textNodeIndices.length; j++) { - for(var index = textNodeIndices[j]; k < index; k++) { - childNode = DOM.nextSibling(childNode); - } - ListWrapper.push(textNodes, childNode); - } - } - - // componentChildViews - var lightDom = null; - var bindingPropagationConfig = null; - if (isPresent(binder.nestedProtoView) && isPresent(binder.componentDirective)) { - var strategy = this.shadowDomStrategy; - var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager); - changeDetector.addChild(childView.changeDetector); - - lightDom = strategy.constructLightDom(view, childView, element); - strategy.attachTemplate(element, childView); - - bindingPropagationConfig = new BindingPropagationConfig(childView.changeDetector); - - ListWrapper.push(componentChildViews, childView); - } - lightDoms[binderIdx] = lightDom; - - var destLightDom = null; - if (isPresent(binder.parent) && binder.distanceToParent === 1) { - destLightDom = lightDoms[binder.parent.index]; - } - - // viewContainers - var viewContainer = null; - if (isPresent(binder.viewportDirective)) { - viewContainer = new ViewContainer(view, element, binder.nestedProtoView, elementInjector, - eventManager, destLightDom); - } - viewContainers[binderIdx] = viewContainer; - - // contentTags - var contentTag = null; - if (isPresent(binder.contentTagSelector)) { - contentTag = new Content(destLightDom, element, binder.contentTagSelector); - } - contentTags[binderIdx] = contentTag; - - // preBuiltObjects - if (isPresent(elementInjector)) { - preBuiltObjects[binderIdx] = new PreBuiltObjects(view, new NgElement(element), viewContainer, - bindingPropagationConfig); - } - - // events - if (isPresent(binder.events)) { - eventHandlers[binderIdx] = StringMapWrapper.create(); - StringMapWrapper.forEach(binder.events, (eventMap, eventName) => { - var handler = ProtoView.buildEventHandler(eventMap, binderIdx); - StringMapWrapper.set(eventHandlers[binderIdx], eventName, handler); - if (isBlank(elementInjector) || !elementInjector.hasEventEmitter(eventName)) { - eventManager.addEventListener(element, eventName, - (event) => { handler(event, view); }); - } - }); - } - } - - this.eventHandlers = eventHandlers; - - view.init(changeDetector, elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings, - viewContainers, contentTags, preBuiltObjects, componentChildViews, lightDoms); - - return view; - } - - returnToPool(view: View) { - this._viewPool.push(view); - } - - /** - * Creates an event handler. - * - * @param {Map} eventMap Map directiveIndexes to expressions - * @param {int} injectorIdx - * @returns {Function} - */ - static buildEventHandler(eventMap: Map, injectorIdx: int) { - var locals = MapWrapper.create(); - return (event, view) => { - // Most of the time the event will be fired only when the view is in the live document. - // However, in a rare circumstance the view might get dehydrated, in between the event - // queuing up and firing. - if (view.hydrated()) { - MapWrapper.set(locals, '$event', event); - MapWrapper.forEach(eventMap, (expr, directiveIndex) => { - var context; - if (directiveIndex === -1) { - context = view.context; - } else { - context = view.elementInjectors[injectorIdx].getDirectiveAtIndex(directiveIndex); - } - expr.eval(context, new Locals(view.locals, locals)); - }); - } - } - } - bindVariable(contextName:string, templateName:string) { MapWrapper.set(this.variableBindings, contextName, templateName); MapWrapper.set(this.protoLocals, templateName, null); @@ -562,12 +367,7 @@ export class ProtoView { /** * Adds a text node binding for the last created ElementBinder via bindElement */ - bindTextNode(indexInParent:int, expression:AST) { - var elBinder = this.elementBinders[this.elementBinders.length-1]; - if (isBlank(elBinder.textNodeIndices)) { - elBinder.textNodeIndices = ListWrapper.create(); - } - ListWrapper.push(elBinder.textNodeIndices, indexInParent); + bindTextNode(expression:AST) { var memento = this.textNodesWithBindingCount++; ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); } @@ -575,13 +375,8 @@ export class ProtoView { /** * Adds an element property binding for the last created ElementBinder via bindElement */ - bindElementProperty(expression:AST, setterName:string, setter:SetterFn) { - var elBinder = this.elementBinders[this.elementBinders.length-1]; - if (!elBinder.hasElementPropertyBindings) { - elBinder.hasElementPropertyBindings = true; - this.elementsWithBindingCount++; - } - var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setter); + bindElementProperty(expression:AST, setterName:string) { + var memento = new ElementBindingMemento(this.elementBinders.length-1, setterName); ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); } @@ -651,16 +446,12 @@ export class ProtoView { /** */ export class ElementBindingMemento { - _elementIndex:int; - _setter:SetterFn; - constructor(elementIndex:int, setter:SetterFn) { - this._elementIndex = elementIndex; - this._setter = setter; - } + elementIndex:int; + propertyName:string; - invoke(currentValue:any, bindElements:List) { - var element = bindElements[this._elementIndex]; - this._setter(element, currentValue); + constructor(elementIndex:int, propertyName:string) { + this.elementIndex = elementIndex; + this.propertyName = propertyName; } } diff --git a/modules/angular2/src/core/compiler/view_container.js b/modules/angular2/src/core/compiler/view_container.js index 3d49624973..3d11a85c7f 100644 --- a/modules/angular2/src/core/compiler/view_container.js +++ b/modules/angular2/src/core/compiler/view_container.js @@ -1,59 +1,64 @@ -import * as viewModule from './view'; -import {DOM} from 'angular2/src/dom/dom_adapter'; import {ListWrapper, MapWrapper, List} from 'angular2/src/facade/collection'; import {BaseException} from 'angular2/src/facade/lang'; import {Injector} from 'angular2/di'; import * as eiModule from 'angular2/src/core/compiler/element_injector'; import {isPresent, isBlank} from 'angular2/src/facade/lang'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; -import {LightDom} from './shadow_dom_emulation/light_dom'; + +import * as renderApi from 'angular2/src/render/api'; +import * as viewModule from './view'; +import * as vfModule from './view_factory'; /** * @publicModule angular2/template */ export class ViewContainer { + render:renderApi.ViewContainerRef; + viewFactory: vfModule.ViewFactory; parentView: viewModule.View; - templateElement; defaultProtoView: viewModule.ProtoView; _views: List<viewModule.View>; - _lightDom: LightDom; - _eventManager: EventManager; elementInjector: eiModule.ElementInjector; appInjector: Injector; hostElementInjector: eiModule.ElementInjector; - hostLightDom: LightDom; - constructor(parentView: viewModule.View, - templateElement, + constructor(viewFactory:vfModule.ViewFactory, + parentView: viewModule.View, defaultProtoView: viewModule.ProtoView, - elementInjector: eiModule.ElementInjector, - eventManager: EventManager, - lightDom = null) { + elementInjector: eiModule.ElementInjector) { + this.viewFactory = viewFactory; + this.render = null; this.parentView = parentView; - this.templateElement = templateElement; this.defaultProtoView = defaultProtoView; this.elementInjector = elementInjector; - this._lightDom = lightDom; // The order in this list matches the DOM order. this._views = []; this.appInjector = null; this.hostElementInjector = null; - this.hostLightDom = null; - this._eventManager = eventManager; } - hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector, hostLightDom: LightDom) { + internalHydrateRecurse(render:renderApi.ViewContainerRef, appInjector: Injector, hostElementInjector: eiModule.ElementInjector) { + this.render = render; this.appInjector = appInjector; this.hostElementInjector = hostElementInjector; - this.hostLightDom = hostLightDom; } - dehydrate() { + internalDehydrateRecurse() { this.appInjector = null; this.hostElementInjector = null; - this.hostLightDom = null; - this.clear(); + this.render = null; + // Note: We don't call clear here, + // as we don't want to change the render side + // (i.e. don't deattach views on the render side), + // as the render side does its own recursion. + for (var i = this._views.length - 1; i >= 0; i--) { + var view = this._views[i]; + view.changeDetector.remove(); + this._unlinkElementInjectors(view); + view.internalDehydrateRecurse(); + this.viewFactory.returnView(view); + } + this._views = []; } clear() { @@ -70,11 +75,6 @@ export class ViewContainer { return this._views.length; } - _siblingToInsertAfter(index: number) { - if (index == 0) return this.templateElement; - return ListWrapper.last(this._views[index - 1].nodes); - } - hydrated() { return isPresent(this.appInjector); } @@ -84,28 +84,27 @@ export class ViewContainer { create(atIndex=-1): viewModule.View { if (!this.hydrated()) throw new BaseException( 'Cannot create views on a dehydrated ViewContainer'); - // TODO(rado): replace with viewFactory. - var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager); + var newView = this.viewFactory.getView(this.defaultProtoView); // insertion must come before hydration so that element injector trees are attached. - this.insert(newView, atIndex); - newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom, + this._insertWithoutRender(newView, atIndex); + // hydration must come before changing the render side, + // as it acquires the render views. + newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context, this.parentView.locals); + this.defaultProtoView.renderer.insertViewIntoContainer(this.render, newView.render, atIndex); - // new content tags might have appeared, we need to redistrubute. - if (isPresent(this.hostLightDom)) { - this.hostLightDom.redistribute(); - } return newView; } insert(view, atIndex=-1): viewModule.View { + this._insertWithoutRender(view, atIndex); + this.defaultProtoView.renderer.insertViewIntoContainer(this.render, view.render, atIndex); + return view; + } + + _insertWithoutRender(view, atIndex=-1): viewModule.View { if (atIndex == -1) atIndex = this._views.length; ListWrapper.insert(this._views, atIndex, view); - if (isBlank(this._lightDom)) { - ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view); - } else { - this._lightDom.redistribute(); - } this.parentView.changeDetector.addChild(view.changeDetector); this._linkElementInjectors(view); @@ -116,8 +115,7 @@ export class ViewContainer { if (atIndex == -1) atIndex = this._views.length - 1; var view = this.detach(atIndex); view.dehydrate(); - // TODO(rado): this needs to be delayed until after any pending animations. - this.defaultProtoView.returnToPool(view); + this.viewFactory.returnView(view); // view is intentionally not returned to the client. } @@ -129,32 +127,12 @@ export class ViewContainer { if (atIndex == -1) atIndex = this._views.length - 1; var detachedView = this.get(atIndex); ListWrapper.removeAt(this._views, atIndex); - if (isBlank(this._lightDom)) { - ViewContainer.removeViewNodes(detachedView); - } else { - this._lightDom.redistribute(); - } - // content tags might have disappeared we need to do redistribution. - if (isPresent(this.hostLightDom)) { - this.hostLightDom.redistribute(); - } + this.defaultProtoView.renderer.detachViewFromContainer(this.render, atIndex); detachedView.changeDetector.remove(); this._unlinkElementInjectors(detachedView); return detachedView; } - contentTagContainers() { - return this._views; - } - - nodes():List { - var r = []; - for (var i = 0; i < this._views.length; ++i) { - r = ListWrapper.concat(r, this._views[i].nodes); - } - return r; - } - _linkElementInjectors(view) { for (var i = 0; i < view.rootElementInjectors.length; ++i) { view.rootElementInjectors[i].parent = this.elementInjector; @@ -166,19 +144,4 @@ export class ViewContainer { view.rootElementInjectors[i].parent = null; } } - - static moveViewNodesAfterSibling(sibling, view) { - for (var i = view.nodes.length - 1; i >= 0; --i) { - DOM.insertAfter(sibling, view.nodes[i]); - } - } - - static removeViewNodes(view) { - var len = view.nodes.length; - if (len == 0) return; - var parent = view.nodes[0].parentNode; - for (var i = len - 1; i >= 0; --i) { - DOM.removeChild(parent, view.nodes[i]); - } - } } diff --git a/modules/angular2/src/core/compiler/view_factory.js b/modules/angular2/src/core/compiler/view_factory.js new file mode 100644 index 0000000000..083d7b02b8 --- /dev/null +++ b/modules/angular2/src/core/compiler/view_factory.js @@ -0,0 +1,109 @@ +import {Injectable, Inject, OpaqueToken} from 'angular2/di'; +import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; +import * as eli from './element_injector'; +import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; +import * as vcModule from './view_container'; +import * as viewModule from './view'; +import {BindingPropagationConfig} from 'angular2/change_detection'; + +// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this! +export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity'; + +@Injectable() +export class ViewFactory { + _poolCapacity:number; + _pooledViews:List<viewModule.View>; + + constructor(@Inject(VIEW_POOL_CAPACITY) capacity) { + this._poolCapacity = capacity; + this._pooledViews = ListWrapper.create(); + } + + getView(protoView:viewModule.ProtoView):viewModule.View { + // TODO(tbosch): benchmark this scanning of views and maybe + // replace it with a fancy LRU Map/List combination... + var view; + for (var i=this._pooledViews.length-1; i>=0; i--) { + var pooledView = this._pooledViews[i]; + if (pooledView.proto === protoView) { + view = ListWrapper.removeAt(this._pooledViews, i); + } + } + if (isBlank(view)) { + view = this._createView(protoView); + } + return view; + } + + returnView(view:viewModule.View) { + if (view.hydrated()) { + throw new BaseException('Only dehydrated Views can be put back into the pool!'); + } + ListWrapper.push(this._pooledViews, view); + while (this._pooledViews.length > this._poolCapacity) { + ListWrapper.removeAt(this._pooledViews, 0); + } + } + + _createView(protoView:viewModule.ProtoView): viewModule.View { + var view = new viewModule.View(protoView, protoView.protoLocals); + var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindingRecords, + protoView.getVariableBindings(), protoView.getDirectiveMementos()); + + var binders = protoView.elementBinders; + var elementInjectors = ListWrapper.createFixedSize(binders.length); + var rootElementInjectors = []; + var preBuiltObjects = ListWrapper.createFixedSize(binders.length); + var viewContainers = ListWrapper.createFixedSize(binders.length); + var componentChildViews = []; + + for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { + var binder = binders[binderIdx]; + var elementInjector = null; + + // elementInjectors and rootElementInjectors + var protoElementInjector = binder.protoElementInjector; + if (isPresent(protoElementInjector)) { + if (isPresent(protoElementInjector.parent)) { + var parentElementInjector = elementInjectors[protoElementInjector.parent.index]; + elementInjector = protoElementInjector.instantiate(parentElementInjector); + } else { + elementInjector = protoElementInjector.instantiate(null); + ListWrapper.push(rootElementInjectors, elementInjector); + } + } + elementInjectors[binderIdx] = elementInjector; + + // componentChildViews + var bindingPropagationConfig = null; + if (isPresent(binder.nestedProtoView) && isPresent(binder.componentDirective)) { + var childView = this._createView(binder.nestedProtoView); + changeDetector.addChild(childView.changeDetector); + + bindingPropagationConfig = new BindingPropagationConfig(childView.changeDetector); + + ListWrapper.push(componentChildViews, childView); + } + + // viewContainers + var viewContainer = null; + if (isPresent(binder.viewportDirective)) { + viewContainer = new vcModule.ViewContainer(this, view, binder.nestedProtoView, elementInjector); + } + viewContainers[binderIdx] = viewContainer; + + // preBuiltObjects + if (isPresent(elementInjector)) { + preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), viewContainer, + bindingPropagationConfig); + } + } + + view.init(changeDetector, elementInjectors, rootElementInjectors, + viewContainers, preBuiltObjects, componentChildViews); + + return view; + } + +} \ No newline at end of file diff --git a/modules/angular2/src/core/compiler/view_pool.js b/modules/angular2/src/core/compiler/view_pool.js deleted file mode 100644 index ef6d42d67e..0000000000 --- a/modules/angular2/src/core/compiler/view_pool.js +++ /dev/null @@ -1,26 +0,0 @@ -import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'angular2/src/facade/collection'; -import * as viewModule from './view'; - -export class ViewPool { - _views: List<viewModule.View>; - _capacity: number; - constructor(capacity: number) { - this._views = []; - this._capacity = capacity; - } - - pop(): viewModule.View { - return ListWrapper.isEmpty(this._views) ? null : ListWrapper.removeLast(this._views); - } - - push(view: viewModule.View) { - if (this._views.length < this._capacity) { - ListWrapper.push(this._views, view); - } - } - - length() { - return this._views.length; - } -} - diff --git a/modules/angular2/src/core/dom/element.js b/modules/angular2/src/core/dom/element.js deleted file mode 100644 index bb9c98fb6b..0000000000 --- a/modules/angular2/src/core/dom/element.js +++ /dev/null @@ -1,16 +0,0 @@ -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {normalizeBlank} from 'angular2/src/facade/lang'; - -/** - * @publicModule angular2/angular2 - */ -export class NgElement { - domElement; - constructor(domElement) { - this.domElement = domElement; - } - - getAttribute(name:string) { - return normalizeBlank(DOM.getAttribute(this.domElement, name)); - } -} \ No newline at end of file diff --git a/modules/angular2/src/directives/class.js b/modules/angular2/src/directives/class.js index 69d8375cac..9a986ae904 100644 --- a/modules/angular2/src/directives/class.js +++ b/modules/angular2/src/directives/class.js @@ -1,7 +1,7 @@ import {Decorator} from 'angular2/src/core/annotations/annotations'; import {isPresent} from 'angular2/src/facade/lang'; import {DOM} from 'angular2/src/dom/dom_adapter'; -import {NgElement} from 'angular2/src/core/dom/element'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; @Decorator({ selector: '[class]', diff --git a/modules/angular2/src/directives/switch.js b/modules/angular2/src/directives/switch.js index b71d163c1e..56fdca4850 100644 --- a/modules/angular2/src/directives/switch.js +++ b/modules/angular2/src/directives/switch.js @@ -1,6 +1,5 @@ import {Decorator, Viewport} from 'angular2/src/core/annotations/annotations'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; -import {NgElement} from 'angular2/src/core/dom/element'; import {isPresent, isBlank, normalizeBlank} from 'angular2/src/facade/lang'; import {ListWrapper, List, MapWrapper, Map} from 'angular2/src/facade/collection'; import {Parent} from 'angular2/src/core/annotations/visibility'; @@ -156,7 +155,7 @@ export class SwitchWhen { _switch: Switch; _viewContainer: ViewContainer; - constructor(el: NgElement, viewContainer: ViewContainer, @Parent() sswitch: Switch) { + constructor(viewContainer: ViewContainer, @Parent() sswitch: Switch) { // `_whenDefault` is used as a marker for a not yet initialized value this._value = _whenDefault; this._switch = sswitch; diff --git a/modules/angular2/src/render/api.js b/modules/angular2/src/render/api.js index 2315bb5dcb..03e556d98c 100644 --- a/modules/angular2/src/render/api.js +++ b/modules/angular2/src/render/api.js @@ -203,7 +203,7 @@ export class Renderer { * Sets the dispatcher for all events that have been defined in the template or in directives * in the given view. */ - setEventDispatcher(viewRef:ViewRef, dispatcher:EventDispatcher):void {} + setEventDispatcher(viewRef:ViewRef, dispatcher:any/*EventDispatcher*/):void {} /** * To be called at the end of the VmTurn so the API can buffer calls @@ -218,10 +218,10 @@ export class Renderer { export class EventDispatcher { /** * Called when an event was triggered for a on-* attribute on an element. - * @param {List<any>} locals Locals to be used to evaluate the + * @param {Map<string, any>} locals Locals to be used to evaluate the * event expressions */ dispatchEvent( - elementIndex:number, eventName:string, locals:List<any> + elementIndex:number, eventName:string, locals:Map<string, any> ):void {} } diff --git a/modules/angular2/src/render/dom/compiler/compiler.js b/modules/angular2/src/render/dom/compiler/compiler.js index 628c7a30ce..5d33843351 100644 --- a/modules/angular2/src/render/dom/compiler/compiler.js +++ b/modules/angular2/src/render/dom/compiler/compiler.js @@ -1,10 +1,14 @@ +import {Injectable} from 'angular2/di'; + import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {BaseException} from 'angular2/src/facade/lang'; import {Template, ProtoView} from '../../api'; import {CompilePipeline} from './compile_pipeline'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; -import {CompileStepFactory} from './compile_step_factory'; +import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory'; +import {Parser} from 'angular2/change_detection'; +import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; /** * The compiler loads and translates the html templates of components into @@ -43,4 +47,11 @@ export class Compiler { return PromiseWrapper.resolve(protoView); } } +} + +@Injectable() +export class DefaultCompiler extends Compiler { + constructor(parser:Parser, shadowDomStrategy:ShadowDomStrategy, templateLoader: TemplateLoader) { + super(new DefaultStepFactory(parser, shadowDomStrategy), templateLoader); + } } \ No newline at end of file diff --git a/modules/angular2/src/render/dom/direct_dom_renderer.js b/modules/angular2/src/render/dom/direct_dom_renderer.js index fe2dbe8f84..4f0c963626 100644 --- a/modules/angular2/src/render/dom/direct_dom_renderer.js +++ b/modules/angular2/src/render/dom/direct_dom_renderer.js @@ -1,3 +1,4 @@ +import {Injectable} from 'angular2/di'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {isBlank, isPresent} from 'angular2/src/facade/lang'; @@ -14,7 +15,7 @@ function _resolveViewContainer(vc:api.ViewContainerRef) { return _resolveView(vc.view).viewContainers[vc.elementIndex]; } -function _resolveView(viewRef:_DirectDomViewRef) { +function _resolveView(viewRef:DirectDomViewRef) { return isPresent(viewRef) ? viewRef.delegate : null; } @@ -23,7 +24,7 @@ function _resolveProtoView(protoViewRef:DirectDomProtoViewRef) { } function _wrapView(view:View) { - return new _DirectDomViewRef(view); + return new DirectDomViewRef(view); } function _collectComponentChildViewRefs(view, target = null) { @@ -51,7 +52,7 @@ export class DirectDomProtoViewRef extends api.ProtoViewRef { } } -class _DirectDomViewRef extends api.ViewRef { +export class DirectDomViewRef extends api.ViewRef { delegate:View; constructor(delegate:View) { @@ -60,6 +61,7 @@ class _DirectDomViewRef extends api.ViewRef { } } +@Injectable() export class DirectDomRenderer extends api.Renderer { _compiler: Compiler; _viewFactory: ViewFactory; @@ -131,7 +133,7 @@ export class DirectDomRenderer extends api.Renderer { _resolveView(viewRef).setText(textNodeIndex, text); } - setEventDispatcher(viewRef:api.ViewRef, dispatcher:api.EventDispatcher) { + setEventDispatcher(viewRef:api.ViewRef, dispatcher:any/*api.EventDispatcher*/):void { _resolveView(viewRef).setEventDispatcher(dispatcher); } } diff --git a/modules/angular2/src/render/dom/view/proto_view_builder.js b/modules/angular2/src/render/dom/view/proto_view_builder.js index 37fa03ddd0..7e3e20810a 100644 --- a/modules/angular2/src/render/dom/view/proto_view_builder.js +++ b/modules/angular2/src/render/dom/view/proto_view_builder.js @@ -242,19 +242,19 @@ export class EventLocalsAstSplitter extends AstTransformer { } splitEventAstIntoLocals(eventBindings:Map<string, ASTWithSource>):Map<string, ASTWithSource> { - // TODO(tbosch): reenable this when we are using - // the render views - return eventBindings; - // if (isPresent(eventBindings)) { - // var result = MapWrapper.create(); - // MapWrapper.forEach(eventBindings, (astWithSource, eventName) => { - // var adjustedAst = astWithSource.ast.visit(this); - // MapWrapper.set(result, eventName, new ASTWithSource(adjustedAst, astWithSource.source, '')); - // ListWrapper.push(this.eventNames, eventName); - // }); - // return result; - // } - // return null; + if (isPresent(eventBindings)) { + var result = MapWrapper.create(); + MapWrapper.forEach(eventBindings, (astWithSource, eventName) => { + // TODO(tbosch): reenable this when we are parsing element properties + // out of action expressions + // var adjustedAst = astWithSource.ast.visit(this); + var adjustedAst = astWithSource.ast; + MapWrapper.set(result, eventName, new ASTWithSource(adjustedAst, astWithSource.source, '')); + ListWrapper.push(this.eventNames, eventName); + }); + return result; + } + return null; } visitAccessMember(ast:AccessMember) { diff --git a/modules/angular2/src/render/dom/view/view.js b/modules/angular2/src/render/dom/view/view.js index b486332840..4e8a2a435e 100644 --- a/modules/angular2/src/render/dom/view/view.js +++ b/modules/angular2/src/render/dom/view/view.js @@ -1,7 +1,6 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; -import {Locals} from 'angular2/change_detection'; import {ViewContainer} from './view_container'; import {ProtoView} from './proto_view'; @@ -10,7 +9,7 @@ import {Content} from '../shadow_dom/content_tag'; import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; -import {EventDispatcher} from '../../api'; +// import {EventDispatcher} from '../../api'; const NG_BINDING_CLASS = 'ng-binding'; @@ -31,7 +30,7 @@ export class View { lightDoms: List<LightDom>; proto: ProtoView; _hydrated: boolean; - _eventDispatcher: EventDispatcher; + _eventDispatcher: any/*EventDispatcher*/; constructor( proto:ProtoView, rootNodes:List, @@ -43,6 +42,7 @@ export class View { this.viewContainers = viewContainers; this.contentTags = contentTags; this.lightDoms = ListWrapper.createFixedSize(boundElements.length); + ListWrapper.fill(this.lightDoms, null); this.componentChildViews = ListWrapper.createFixedSize(boundElements.length); this._hydrated = false; } @@ -134,7 +134,10 @@ export class View { // componentChildViews for (var i = 0; i < this.componentChildViews.length; i++) { - this.componentChildViews[i].dehydrate(); + var cv = this.componentChildViews[i]; + if (isPresent(cv)) { + cv.dehydrate(); + } } // viewContainers and content tags @@ -150,10 +153,11 @@ export class View { } } } + this._eventDispatcher = null; this._hydrated = false; } - setEventDispatcher(dispatcher:EventDispatcher) { + setEventDispatcher(dispatcher:any/*EventDispatcher*/) { this._eventDispatcher = dispatcher; } @@ -161,8 +165,11 @@ export class View { if (isPresent(this._eventDispatcher)) { var evalLocals = MapWrapper.create(); MapWrapper.set(evalLocals, '$event', event); - var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new Locals(null, evalLocals)); - this._eventDispatcher.dispatchEvent(elementIndex, eventName, localValues); + // TODO(tbosch): reenable this when we are parsing element properties + // out of action expressions + // var localValues = this.proto.elementBinders[elementIndex].eventLocals.eval(null, new Locals(null, evalLocals)); + // this._eventDispatcher.dispatchEvent(elementIndex, eventName, localValues); + this._eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals); } } } diff --git a/modules/angular2/src/render/dom/view/view_container.js b/modules/angular2/src/render/dom/view/view_container.js index 4df3216085..570fceb1c2 100644 --- a/modules/angular2/src/render/dom/view/view_container.js +++ b/modules/angular2/src/render/dom/view/view_container.js @@ -35,7 +35,7 @@ export class ViewContainer { if (isBlank(this._lightDom)) { for (var i = this._views.length - 1; i >= 0; i--) { var view = this._views[i]; - ViewContainer.removeViewNodesFromParent(this.templateElement.parentNode, view); + ViewContainer.removeViewNodes(view); this._viewFactory.returnView(view); } this._views = []; @@ -72,12 +72,11 @@ export class ViewContainer { } insert(view, atIndex=-1): viewModule.View { - this._checkHydrated(); - if (atIndex == -1) atIndex = this._views.length; - ListWrapper.insert(this._views, atIndex, view); if (!view.hydrated()) { view.hydrate(this._hostLightDom); } + if (atIndex == -1) atIndex = this._views.length; + ListWrapper.insert(this._views, atIndex, view); if (isBlank(this._lightDom)) { ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view); @@ -100,7 +99,7 @@ export class ViewContainer { var detachedView = this.get(atIndex); ListWrapper.removeAt(this._views, atIndex); if (isBlank(this._lightDom)) { - ViewContainer.removeViewNodesFromParent(this.templateElement.parentNode, detachedView); + ViewContainer.removeViewNodes(detachedView); } else { this._lightDom.redistribute(); } @@ -129,8 +128,11 @@ export class ViewContainer { } } - static removeViewNodesFromParent(parent, view) { - for (var i = view.rootNodes.length - 1; i >= 0; --i) { + static removeViewNodes(view) { + var len = view.rootNodes.length; + if (len == 0) return; + var parent = view.rootNodes[0].parentNode; + for (var i = len - 1; i >= 0; --i) { DOM.removeChild(parent, view.rootNodes[i]); } } diff --git a/modules/angular2/src/render/dom/view/view_factory.js b/modules/angular2/src/render/dom/view/view_factory.js index 077bd7e624..2883dedd0a 100644 --- a/modules/angular2/src/render/dom/view/view_factory.js +++ b/modules/angular2/src/render/dom/view/view_factory.js @@ -1,4 +1,4 @@ -import {OpaqueToken} from 'angular2/di'; +import {OpaqueToken, Inject, Injectable} from 'angular2/di'; import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; @@ -8,32 +8,33 @@ import {Content} from '../shadow_dom/content_tag'; import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; -import {ViewContainer} from './view_container'; -import {ProtoView} from './proto_view'; -import {View} from './view'; +import * as vcModule from './view_container'; +import * as pvModule from './proto_view'; +import * as viewModule from './view'; import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util'; -export var VIEW_POOL_CAPACITY = new OpaqueToken('ViewFactory.viewPoolCapacity'); - +// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this! +export const VIEW_POOL_CAPACITY = 'render.ViewFactory.viewPoolCapacity'; +@Injectable() export class ViewFactory { _poolCapacity:number; - _pooledViews:List<View>; + _pooledViews:List<viewModule.View>; _eventManager:EventManager; _shadowDomStrategy:ShadowDomStrategy; - constructor(capacity, eventManager, shadowDomStrategy) { + constructor(@Inject(VIEW_POOL_CAPACITY) capacity, eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) { this._poolCapacity = capacity; this._pooledViews = ListWrapper.create(); this._eventManager = eventManager; this._shadowDomStrategy = shadowDomStrategy; } - getView(protoView:ProtoView):View { + getView(protoView:pvModule.ProtoView):viewModule.View { // TODO(tbosch): benchmark this scanning of views and maybe // replace it with a fancy LRU Map/List combination... var view; - for (var i=0; i<this._pooledViews.length; i++) { + for (var i=this._pooledViews.length-1; i>=0; i--) { var pooledView = this._pooledViews[i]; if (pooledView.proto === protoView) { view = ListWrapper.removeAt(this._pooledViews, i); @@ -45,7 +46,7 @@ export class ViewFactory { return view; } - returnView(view:View) { + returnView(view:viewModule.View) { if (view.hydrated()) { view.dehydrate(); } @@ -55,7 +56,7 @@ export class ViewFactory { } } - _createView(protoView:ProtoView): View { + _createView(protoView:pvModule.ProtoView): viewModule.View { var rootElementClone = protoView.isRootView ? protoView.element : DOM.importIntoDoc(protoView.element); var elementsWithBindingsDynamic; if (protoView.isTemplateElement) { @@ -72,7 +73,7 @@ export class ViewFactory { var viewRootNodes; if (protoView.isTemplateElement) { var childNode = DOM.firstChild(DOM.content(rootElementClone)); - viewRootNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in ProtoView + viewRootNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in pvModule.ProtoView // Note: An explicit loop is the fastest way to convert a DOM array into a JS array! while(childNode != null) { ListWrapper.push(viewRootNodes, childNode); @@ -108,7 +109,7 @@ export class ViewFactory { // viewContainers var viewContainer = null; if (isBlank(binder.componentId) && isPresent(binder.nestedProtoView)) { - viewContainer = new ViewContainer(this, element); + viewContainer = new vcModule.ViewContainer(this, element); } viewContainers[binderIdx] = viewContainer; @@ -120,7 +121,7 @@ export class ViewFactory { contentTags[binderIdx] = contentTag; } - var view = new View( + var view = new viewModule.View( protoView, viewRootNodes, boundTextNodes, boundElements, viewContainers, contentTags ); diff --git a/modules/angular2/src/services/ruler.js b/modules/angular2/src/services/ruler.js index 8d14b4f175..40136dee25 100644 --- a/modules/angular2/src/services/ruler.js +++ b/modules/angular2/src/services/ruler.js @@ -1,6 +1,6 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {DomAdapter} from 'angular2/src/dom/dom_adapter'; -import {NgElement} from 'angular2/src/core/dom/element'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; export class Rectangle { left; diff --git a/modules/angular2/src/test_lib/test_bed.js b/modules/angular2/src/test_lib/test_bed.js index 7de912ebcc..f8024a1b73 100644 --- a/modules/angular2/src/test_lib/test_bed.js +++ b/modules/angular2/src/test_lib/test_bed.js @@ -1,4 +1,4 @@ -import {Injector} from 'angular2/di'; +import {Injector, bind} from 'angular2/di'; import {Type, isPresent, BaseException} from 'angular2/src/facade/lang'; import {Promise} from 'angular2/src/facade/async'; @@ -10,12 +10,15 @@ import {Template} from 'angular2/src/core/annotations/template'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {Compiler} from 'angular2/src/core/compiler/compiler'; import {View} from 'angular2/src/core/compiler/view'; +import {ViewFactory} from 'angular2/src/core/compiler/view_factory'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; +import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector'; +import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {queryView} from './utils'; +import {queryView, viewRootNodes, el} from './utils'; import {instantiateType, getTypeOf} from './lang_utils'; + export class TestBed { _injector: Injector; @@ -85,11 +88,17 @@ export class TestBed { this.setInlineTemplate(component, html); } - return this._injector.get(Compiler).compile(component).then((pv) => { - var eventManager = this._injector.get(EventManager); - var view = pv.instantiate(null, eventManager); - view.hydrate(this._injector, null, null, context, null); - return new ViewProxy(view); + var rootEl = el('<div></div>'); + var metadataReader = this._injector.get(DirectiveMetadataReader); + var componentBinding = DirectiveBinding.createFromBinding( + bind(component).toValue(context), + metadataReader.read(component).annotation + ); + return this._injector.get(Compiler).compileRoot(rootEl, componentBinding).then((pv) => { + var viewFactory = this._injector.get(ViewFactory); + var view = viewFactory.getView(pv); + view.hydrate(this._injector, null, context, null); + return new ViewProxy(view.componentChildViews[0]); }); } } @@ -108,8 +117,8 @@ export class ViewProxy { return this._view.context; } - get nodes(): List { - return this._view.nodes; + get rootNodes(): List { + return viewRootNodes(this._view); } detectChanges(): void { diff --git a/modules/angular2/src/test_lib/test_injector.js b/modules/angular2/src/test_lib/test_injector.js index 92236aae1d..feef7d75e2 100644 --- a/modules/angular2/src/test_lib/test_injector.js +++ b/modules/angular2/src/test_lib/test_injector.js @@ -7,13 +7,15 @@ import {ExceptionHandler} from 'angular2/src/core/exception_handler'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {ShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {XHR} from 'angular2/src/services/xhr'; import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; import {UrlResolver} from 'angular2/src/services/url_resolver'; import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone'; +import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'; import {DOM} from 'angular2/src/dom/dom_adapter'; @@ -32,6 +34,13 @@ import {Injector} from 'angular2/di'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {FunctionWrapper} from 'angular2/src/facade/lang'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; + /** * Returns the root injector bindings. * @@ -67,11 +76,19 @@ function _getAppBindings() { bind(ShadowDomStrategy).toFactory( (styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head), [StyleUrlResolver, appDocumentToken]), + bind(Renderer).toClass(DirectDomRenderer), + bind(rc.Compiler).toClass(rc.DefaultCompiler), + rvf.ViewFactory, + bind(rvf.VIEW_POOL_CAPACITY).toValue(500), + ProtoViewFactory, + ViewFactory, + bind(VIEW_POOL_CAPACITY).toValue(500), Compiler, CompilerCache, bind(TemplateResolver).toClass(MockTemplateResolver), bind(ChangeDetection).toValue(dynamicChangeDetection), TemplateLoader, + PrivateComponentLoader, DirectiveMetadataReader, Parser, Lexer, diff --git a/modules/angular2/src/test_lib/utils.js b/modules/angular2/src/test_lib/utils.js index a436d03998..89e968d0e0 100644 --- a/modules/angular2/src/test_lib/utils.js +++ b/modules/angular2/src/test_lib/utils.js @@ -24,9 +24,14 @@ export class Log { } } +export function viewRootNodes(view) { + return view.render.delegate.rootNodes; +} + export function queryView(view, selector) { - for (var i = 0; i < view.nodes.length; ++i) { - var res = DOM.querySelector(view.nodes[i], selector); + var rootNodes = viewRootNodes(view); + for (var i = 0; i < rootNodes.length; ++i) { + var res = DOM.querySelector(rootNodes[i], selector); if (isPresent(res)) { return res; } diff --git a/modules/angular2/test/core/compiler/compiler_spec.js b/modules/angular2/test/core/compiler/compiler_spec.js index 94cd8e7994..e1640ee0b7 100644 --- a/modules/angular2/test/core/compiler/compiler_spec.js +++ b/modules/angular2/test/core/compiler/compiler_spec.js @@ -16,7 +16,7 @@ import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src import {Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; -import {NewCompiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; +import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {ProtoView} from 'angular2/src/core/compiler/view'; import {ElementBinder} from 'angular2/src/core/compiler/element_binder'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; @@ -45,7 +45,7 @@ export function main() { var urlResolver = new FakeUrlResolver(); renderer = new FakeRenderer(renderCompileResults); protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults) - return new NewCompiler( + return new Compiler( reader, new CompilerCache(), tplResolver, @@ -373,7 +373,7 @@ export function main() { ], [rootProtoView, mainProtoView] ); - compiler.compileRoot(null, createDirectiveBinding(reader, MainComponent)).then( (protoView) => { + compiler.compileRoot(null, MainComponent).then( (protoView) => { expect(protoView).toBe(rootProtoView); expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView); async.done(); @@ -388,7 +388,7 @@ function createDirectiveBinding(reader, type) { } function createProtoView(elementBinders = null) { - var pv = new ProtoView(null, null, null, null, null); + var pv = new ProtoView(null, null, null); if (isBlank(elementBinders)) { elementBinders = []; } diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js index 30378450e6..8c9f918733 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.js @@ -1,7 +1,6 @@ import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, SpyObject, proxy, el} from 'angular2/test_lib'; import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang'; import {ListWrapper, MapWrapper, List, StringMapWrapper} from 'angular2/src/facade/collection'; -import {DOM} from 'angular2/src/dom/dom_adapter'; import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding} from 'angular2/src/core/compiler/element_injector'; import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility'; import {EventEmitter, PropertySetter, Attribute} from 'angular2/src/core/annotations/di'; @@ -9,10 +8,11 @@ import {onDestroy} from 'angular2/src/core/annotations/annotations'; import {Optional, Injector, Inject, bind} from 'angular2/di'; import {ProtoView, View} from 'angular2/src/core/compiler/view'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; -import {NgElement} from 'angular2/src/core/dom/element'; -import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; import {Directive} from 'angular2/src/core/annotations/annotations'; -import {BindingPropagationConfig} from 'angular2/change_detection'; +import {BindingPropagationConfig, Parser, Lexer} from 'angular2/change_detection'; + +import {ViewRef, Renderer} from 'angular2/src/render/api'; class DummyDirective extends Directive { constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); } @@ -22,10 +22,6 @@ class DummyDirective extends Directive { @IMPLEMENTS(View) class DummyView extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}} -@proxy -@IMPLEMENTS(LightDom) -class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}} - class SimpleDirective { } @@ -198,10 +194,10 @@ export function main() { var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector)); proto.attributes = attributes; - var inj = proto.instantiate(null, null); + var inj = proto.instantiate(null); var preBuilt = isPresent(preBuiltObjects) ? preBuiltObjects : defaultPreBuiltObjects; - inj.instantiateDirectives(lightDomAppInjector, shadowDomAppInjector, preBuilt); + inj.instantiateDirectives(lightDomAppInjector, null, shadowDomAppInjector, preBuilt); return inj; } @@ -211,13 +207,13 @@ export function main() { var inj = new Injector([]); var protoParent = new ProtoElementInjector(null, 0, parentBindings); - var parent = protoParent.instantiate(null, null); + var parent = protoParent.instantiate(null); - parent.instantiateDirectives(inj, null, parentPreBuildObjects); + parent.instantiateDirectives(inj, null, null, parentPreBuildObjects); var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1); - var child = protoChild.instantiate(parent, null); - child.instantiateDirectives(inj, null, defaultPreBuiltObjects); + var child = protoChild.instantiate(parent); + child.instantiateDirectives(inj, null, null, defaultPreBuiltObjects); return child; } @@ -229,12 +225,12 @@ export function main() { var shadowInj = inj.createChild([]); var protoParent = new ProtoElementInjector(null, 0, hostBindings, true); - var host = protoParent.instantiate(null, null); - host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects); + var host = protoParent.instantiate(null); + host.instantiateDirectives(inj, null, shadowInj, hostPreBuildObjects); var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1); - var shadow = protoChild.instantiate(null, host); - shadow.instantiateDirectives(shadowInj, null, null); + var shadow = protoChild.instantiate(null); + shadow.instantiateDirectives(shadowInj, host, null, null); return shadow; } @@ -278,9 +274,9 @@ export function main() { var protoChild1 = new ProtoElementInjector(protoParent, 1, []); var protoChild2 = new ProtoElementInjector(protoParent, 2, []); - var p = protoParent.instantiate(null, null); - var c1 = protoChild1.instantiate(p, null); - var c2 = protoChild2.instantiate(p, null); + var p = protoParent.instantiate(null); + var c1 = protoChild1.instantiate(p); + var c2 = protoChild2.instantiate(p); expect(humanize(p, [ [p, 'parent'], @@ -295,8 +291,8 @@ export function main() { var protoParent = new ProtoElementInjector(null, 0, []); var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance); - var p = protoParent.instantiate(null, null); - var c = protoChild.instantiate(p, null); + var p = protoParent.instantiate(null); + var c = protoChild.instantiate(p); expect(c.directParent()).toEqual(p); }); @@ -306,8 +302,8 @@ export function main() { var protoParent = new ProtoElementInjector(null, 0, []); var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance); - var p = protoParent.instantiate(null, null); - var c = protoChild.instantiate(p, null); + var p = protoParent.instantiate(null); + var c = protoChild.instantiate(p); expect(c.directParent()).toEqual(null); }); @@ -496,14 +492,14 @@ export function main() { }); it("should return element", function () { - var element = new NgElement(null); + var element = new NgElement(null, null); var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null)); expect(inj.get(NgElement)).toEqual(element); }); it('should return viewContainer', function () { - var viewContainer = new ViewContainer(null, null, null, null, null); + var viewContainer = new ViewContainer(null, null, null, null); var inj = injector([], null, null, new PreBuiltObjects(null, null, viewContainer, null)); expect(inj.get(ViewContainer)).toEqual(viewContainer); @@ -536,9 +532,9 @@ export function main() { injWithPrivateComponent.createPrivateComponent(SomeOtherDirective, null); var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false); - var shadowDomInj = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent); + var shadowDomInj = shadowDomProtoInjector.instantiate(null); - expect(() => shadowDomInj.instantiateDirectives(appInjector, null, defaultPreBuiltObjects)). + expect(() => shadowDomInj.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects)). toThrowError(new RegExp("No provider for SimpleDirective")); }); @@ -547,8 +543,8 @@ export function main() { injWithPrivateComponent.createPrivateComponent(SimpleDirective, null); var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false); - var shadowDomInjector = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent); - shadowDomInjector.instantiateDirectives(appInjector, null, defaultPreBuiltObjects); + var shadowDomInjector = shadowDomProtoInjector.instantiate(null); + shadowDomInjector.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects); expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor); expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective); @@ -566,7 +562,7 @@ export function main() { expect(inj.getPrivateComponent()).toBe(null); expect(dir.onDestroyCounter).toBe(1); - inj.instantiateDirectives(null, null, null); + inj.instantiateDirectives(null, null, null, null); expect(inj.getPrivateComponent()).not.toBe(null); }); @@ -577,15 +573,18 @@ export function main() { function createpreBuildObject(eventName, eventHandler) { var handlers = StringMapWrapper.create(); StringMapWrapper.set(handlers, eventName, eventHandler); - var pv = new ProtoView(null, null, null, null); - pv.eventHandlers = [handlers]; - var view = new View(pv, null, MapWrapper.create()); + var pv = new ProtoView(null, null, null); + pv.bindElement(null, 0, null, null, null); + pv.bindEvent(eventName, new Parser(new Lexer()).parseAction('handler()', '')); + + var view = new View(pv, MapWrapper.create()); + view.context = new ContextWithHandler(eventHandler); return new PreBuiltObjects(view, null, null, null); } it('should be injectable and callable', () => { var called = false; - var preBuildObject = createpreBuildObject('click', (e, view) => { called = true;}); + var preBuildObject = createpreBuildObject('click', () => { called = true;}); var inj = injector([NeedsEventEmitter], null, null, preBuildObject); inj.get(NeedsEventEmitter).click(); expect(called).toEqual(true); @@ -593,7 +592,7 @@ export function main() { it('should be injectable and callable without specifying param type annotation', () => { var called = false; - var preBuildObject = createpreBuildObject('click', (e, view) => { called = true;}); + var preBuildObject = createpreBuildObject('click', () => { called = true;}); var inj = injector([NeedsEventEmitterNoType], null, null, preBuildObject); inj.get(NeedsEventEmitterNoType).click(); expect(called).toEqual(true); @@ -613,11 +612,17 @@ export function main() { }); describe('property setter', () => { - it('should be injectable and callable', () => { - var div = el('<div></div>'); - var ngElement = new NgElement(div); + var renderer, view; - var preBuildObject = new PreBuiltObjects(null, ngElement, null, null); + beforeEach( () => { + renderer = new FakeRenderer(); + var protoView = new ProtoView(renderer, null, null); + view = new View(protoView, MapWrapper.create()); + view.render = new ViewRef(); + }); + + it('should be injectable and callable', () => { + var preBuildObject = new PreBuiltObjects(view, null, null, null); var inj = injector([NeedsPropertySetter], null, null, preBuildObject); var component = inj.get(NeedsPropertySetter); component.setProp('foobar'); @@ -627,22 +632,21 @@ export function main() { component.setStyle('40px'); component.setStyleWithUnit(50); - expect(div.title).toEqual('foobar'); - expect(DOM.getAttribute(div, 'role')).toEqual('button'); - expect(DOM.hasClass(div, 'active')).toEqual(true); - expect(DOM.hasClass(div, 'foo-bar')).toEqual(true); - expect(DOM.getStyle(div, 'width')).toEqual('40px'); - expect(DOM.getStyle(div, 'height')).toEqual('50px'); + expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']); + expect(renderer.log[1]).toEqual([view.render, 0, 'attr.role', 'button']); + expect(renderer.log[2]).toEqual([view.render, 0, 'class.active', true]); + expect(renderer.log[3]).toEqual([view.render, 0, 'class.foo-bar', true]); + expect(renderer.log[4]).toEqual([view.render, 0, 'style.width', '40px']); + expect(renderer.log[5]).toEqual([view.render, 0, 'style.height.px', 50]); }); it('should be injectable and callable without specifying param type annotation', () => { - var div = el('<div></div>'); - var preBuildObject = new PreBuiltObjects(null, new NgElement(div), null, null); + var preBuildObject = new PreBuiltObjects(view, null, null, null); var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject); var component = inj.get(NeedsPropertySetterNoType); component.setProp('foobar'); - expect(div.title).toEqual('foobar'); + expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']); }); }); @@ -673,3 +677,22 @@ export function main() { }); } + +class ContextWithHandler { + handler; + constructor(handler) { + this.handler = handler; + } +} + +class FakeRenderer extends Renderer { + log:List; + constructor() { + super(); + this.log = []; + } + setElementProperty(viewRef, elementIndex, propertyName, value) { + ListWrapper.push(this.log, [viewRef, elementIndex, propertyName, value]); + } + +} \ No newline at end of file diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index 2d8a265369..917d78aaa1 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -5,32 +5,27 @@ import { xdescribe, describe, el, + dispatchEvent, expect, iit, inject, + beforeEachBindings, it, - xit, + xit } from 'angular2/test_lib'; +import {TestBed} from 'angular2/src/test_lib/test_bed'; + import {DOM} from 'angular2/src/dom/dom_adapter'; import {Type, isPresent, BaseException, assertionsEnabled, isJsObject} from 'angular2/src/facade/lang'; import {PromiseWrapper} from 'angular2/src/facade/async'; import {Injector, bind} from 'angular2/di'; -import {Lexer, Parser, dynamicChangeDetection, - DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig, ON_PUSH} from 'angular2/change_detection'; +import {dynamicChangeDetection, + ChangeDetection, DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig, ON_PUSH} from 'angular2/change_detection'; -import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; -import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {ShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location'; import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'; -import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; -import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; -import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; -import {UrlResolver} from 'angular2/src/services/url_resolver'; -import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations/annotations'; import {Template} from 'angular2/src/core/annotations/template'; @@ -43,161 +38,117 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container'; export function main() { describe('integration tests', function() { - var directiveMetadataReader, shadowDomStrategy, compiler, tplResolver; - - function createCompiler(tplResolver, changedDetection) { - var urlResolver = new UrlResolver(); - return new Compiler(changedDetection, - new TemplateLoader(null, null), - directiveMetadataReader, - new Parser(new Lexer()), - new CompilerCache(), - shadowDomStrategy, - tplResolver, - new ComponentUrlMapper(), - urlResolver - ); - } + var ctx; beforeEach( () => { - tplResolver = new MockTemplateResolver(); - - directiveMetadataReader = new DirectiveMetadataReader(); - - var urlResolver = new UrlResolver(); - shadowDomStrategy = new EmulatedUnscopedShadowDomStrategy(new StyleUrlResolver(urlResolver), null); - - compiler = createCompiler(tplResolver, dynamicChangeDetection); + ctx = new MyComp(); }); describe('react to record changes', function() { - var view, ctx, cd; - function createView(pv) { - ctx = new MyComp(); - view = pv.instantiate(null, null); - - view.hydrate(new Injector([ - bind(Compiler).toValue(compiler), - bind(DirectiveMetadataReader).toValue(directiveMetadataReader), - bind(ShadowDomStrategy).toValue(shadowDomStrategy), - bind(EventManager).toValue(null), - PrivateComponentLoader - ]), null, null, ctx, null); - - cd = view.changeDetector; - } - - it('should consume text node changes', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + it('should consume text node changes', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'})); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Hello World!'; - cd.detectChanges(); - expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Hello World!'); + view.detectChanges(); + expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Hello World!'); async.done(); }); })); - it('should consume element binding changes', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div [id]="ctxProp"></div>'})); + it('should consume element binding changes', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div [id]="ctxProp"></div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Hello World!'; - cd.detectChanges(); + view.detectChanges(); - expect(view.nodes[0].id).toEqual('Hello World!'); + expect(view.rootNodes[0].id).toEqual('Hello World!'); async.done(); }); })); - it('should consume binding to aria-* attributes', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div [attr.aria-label]="ctxProp"></div>'})); + it('should consume binding to aria-* attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div [attr.aria-label]="ctxProp"></div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Initial aria label'; - cd.detectChanges(); - expect(DOM.getAttribute(view.nodes[0], 'aria-label')).toEqual('Initial aria label'); + view.detectChanges(); + expect(DOM.getAttribute(view.rootNodes[0], 'aria-label')).toEqual('Initial aria label'); ctx.ctxProp = 'Changed aria label'; - cd.detectChanges(); - expect(DOM.getAttribute(view.nodes[0], 'aria-label')).toEqual('Changed aria label'); + view.detectChanges(); + expect(DOM.getAttribute(view.rootNodes[0], 'aria-label')).toEqual('Changed aria label'); async.done(); }); })); - it('should consume binding to property names where attr name and property name do not match', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div [tabindex]="ctxNumProp"></div>'})); + it('should consume binding to property names where attr name and property name do not match', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div [tabindex]="ctxNumProp"></div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - cd.detectChanges(); - expect(view.nodes[0].tabIndex).toEqual(0); + view.detectChanges(); + expect(view.rootNodes[0].tabIndex).toEqual(0); ctx.ctxNumProp = 5; - cd.detectChanges(); - expect(view.nodes[0].tabIndex).toEqual(5); + view.detectChanges(); + expect(view.rootNodes[0].tabIndex).toEqual(5); async.done(); }); })); - it('should consume binding to camel-cased properties using dash-cased syntax in templates', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<input [read-only]="ctxBoolProp">'})); + it('should consume binding to camel-cased properties using dash-cased syntax in templates', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<input [read-only]="ctxBoolProp">'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - cd.detectChanges(); - expect(view.nodes[0].readOnly).toBeFalsy(); + view.detectChanges(); + expect(view.rootNodes[0].readOnly).toBeFalsy(); ctx.ctxBoolProp = true; - cd.detectChanges(); - expect(view.nodes[0].readOnly).toBeTruthy(); + view.detectChanges(); + expect(view.rootNodes[0].readOnly).toBeTruthy(); async.done(); }); })); - it('should consume binding to inner-html', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'})); + it('should consume binding to inner-html', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Some <span>HTML</span>'; - cd.detectChanges(); - expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some <span>HTML</span>'); + view.detectChanges(); + expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Some <span>HTML</span>'); ctx.ctxProp = 'Some other <div>HTML</div>'; - cd.detectChanges(); - expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some other <div>HTML</div>'); + view.detectChanges(); + expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Some other <div>HTML</div>'); async.done(); }); })); - it('should ignore bindings to unknown properties', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({inline: '<div unknown="{{ctxProp}}"></div>'})); + it('should ignore bindings to unknown properties', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<div unknown="{{ctxProp}}"></div>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Some value'; - cd.detectChanges(); - expect(DOM.hasProperty(view.nodes[0], 'unknown')).toBeFalsy(); + view.detectChanges(); + expect(DOM.hasProperty(view.rootNodes[0], 'unknown')).toBeFalsy(); async.done(); }); })); - it('should consume directive watch expression change.', inject([AsyncTestCompleter], (async) => { + it('should consume directive watch expression change.', inject([TestBed, AsyncTestCompleter], (tb, async) => { var tpl = '<div>' + '<div my-dir [elprop]="ctxProp"></div>' + @@ -205,80 +156,81 @@ export function main() { '<div my-dir elprop="Hi {{\'there!\'}}"></div>' + '<div my-dir elprop="One more {{ctxProp}}"></div>' + '</div>' - tplResolver.setTemplate(MyComp, new Template({inline: tpl, directives: [MyDir]})); + tb.overrideTemplate(MyComp, new Template({inline: tpl, directives: [MyDir]})); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Hello World!'; - cd.detectChanges(); + view.detectChanges(); - expect(view.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!'); - expect(view.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!'); - expect(view.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!'); - expect(view.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!'); + expect(view.rawView.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!'); + expect(view.rawView.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!'); + expect(view.rawView.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!'); + expect(view.rawView.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!'); async.done(); }); })); - it("should support pipes in bindings and bind config", inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, - new Template({ - inline: '<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>', - directives: [ComponentWithPipes] - })); - - - var registry = new PipeRegistry({ - "double" : [new DoublePipeFactory()] + describe('pipes', () => { + beforeEachBindings(() => { + return [bind(ChangeDetection).toFactory( + () => new DynamicChangeDetection(new PipeRegistry({ + "double" : [new DoublePipeFactory()] + })), + [] + )]; }); - var changeDetection = new DynamicChangeDetection(registry); - var compiler = createCompiler(tplResolver, changeDetection); - compiler.compile(MyComp).then((pv) => { - createView(pv); - ctx.ctxProp = 'a'; - cd.detectChanges(); + it("should support pipes in bindings and bind config", inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, + new Template({ + inline: '<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>', + directives: [ComponentWithPipes] + })); - var comp = view.locals.get("comp"); + tb.createView(MyComp, {context: ctx}).then((view) => { - // it is doubled twice: once in the binding, second time in the bind config - expect(comp.prop).toEqual('aaaa'); - async.done(); - }); - })); + ctx.ctxProp = 'a'; + view.detectChanges(); - it('should support nested components.', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + var comp = view.rawView.locals.get("comp"); + + // it is doubled twice: once in the binding, second time in the bind config + expect(comp.prop).toEqual('aaaa'); + async.done(); + }); + })); + }); + + it('should support nested components.', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<child-cmp></child-cmp>', directives: [ChildComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - cd.detectChanges(); + view.detectChanges(); - expect(view.nodes).toHaveText('hello'); + expect(view.rootNodes).toHaveText('hello'); async.done(); }); })); // GH issue 328 - https://github.com/angular/angular/issues/328 - it('should support different directive types on a single node', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should support different directive types on a single node', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>', directives: [MyDir, ChildComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'Hello World!'; - cd.detectChanges(); + view.detectChanges(); - var elInj = view.elementInjectors[0]; + var elInj = view.rawView.elementInjectors[0]; expect(elInj.get(MyDir).dirProp).toEqual('Hello World!'); expect(elInj.get(ChildComp).dirProp).toEqual(null); @@ -286,57 +238,54 @@ export function main() { }); })); - it('should support directives where a binding attribute is not given', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should support directives where a binding attribute is not given', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ // No attribute "el-prop" specified. inline: '<p my-dir></p>', directives: [MyDir] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { async.done(); }); })); - it('should support directives where a selector matches property binding', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should support directives where a selector matches property binding', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<p [id]="ctxProp"></p>', directives: [IdComponent] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { ctx.ctxProp = 'some_id'; - cd.detectChanges(); - expect(view.nodes[0].id).toEqual('some_id'); - expect(view.nodes).toHaveText('Matched on id with some_id'); + view.detectChanges(); + expect(view.rootNodes[0].id).toEqual('some_id'); + expect(view.rootNodes).toHaveText('Matched on id with some_id'); ctx.ctxProp = 'other_id'; - cd.detectChanges(); - expect(view.nodes[0].id).toEqual('other_id'); - expect(view.nodes).toHaveText('Matched on id with other_id'); + view.detectChanges(); + expect(view.rootNodes[0].id).toEqual('other_id'); + expect(view.rootNodes).toHaveText('Matched on id with other_id'); async.done(); }); })); - it('should support template directives via `<template>` elements.', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should support template directives via `<template>` elements.', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<div><template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>', directives: [SomeViewport] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - cd.detectChanges(); + view.detectChanges(); - var childNodesOfWrapper = view.nodes[0].childNodes; + var childNodesOfWrapper = view.rootNodes[0].childNodes; // 1 template + 2 copies. expect(childNodesOfWrapper.length).toBe(3); expect(childNodesOfWrapper[1].childNodes[0].nodeValue).toEqual('hello'); @@ -345,18 +294,17 @@ export function main() { }); })); - it('should support template directives via `template` attribute.', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should support template directives via `template` attribute.', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<div><copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me></div>', directives: [SomeViewport] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - cd.detectChanges(); + view.detectChanges(); - var childNodesOfWrapper = view.nodes[0].childNodes; + var childNodesOfWrapper = view.rootNodes[0].childNodes; // 1 template + 2 copies. expect(childNodesOfWrapper.length).toBe(3); expect(childNodesOfWrapper[1].childNodes[0].nodeValue).toEqual('hello'); @@ -365,84 +313,79 @@ export function main() { }); })); - it('should assign the component instance to a var-', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should assign the component instance to a var-', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<p><child-cmp var-alice></child-cmp></p>', directives: [ChildComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - expect(view.locals).not.toBe(null); - expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.rawView.locals).not.toBe(null); + expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp); async.done(); }) })); - it('should assign two component instances each with a var-', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should assign two component instances each with a var-', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>', directives: [ChildComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - expect(view.locals).not.toBe(null); - expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); - expect(view.locals.get('bob')).toBeAnInstanceOf(ChildComp); - expect(view.locals.get('alice')).not.toBe(view.locals.get('bob')); + expect(view.rawView.locals).not.toBe(null); + expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp); + expect(view.rawView.locals.get('alice')).not.toBe(view.rawView.locals.get('bob')); async.done(); }) })); - it('should assign the component instance to a var- with shorthand syntax', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should assign the component instance to a var- with shorthand syntax', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<child-cmp #alice></child-cmp>', directives: [ChildComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - expect(view.locals).not.toBe(null); - expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.rawView.locals).not.toBe(null); + expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp); async.done(); }) })); - it('should assign the element instance to a user-defined variable', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should assign the element instance to a user-defined variable', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<p><div var-alice><i>Hello</i></div></p>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); - expect(view.locals).not.toBe(null); + tb.createView(MyComp, {context: ctx}).then((view) => { + expect(view.rawView.locals).not.toBe(null); - var value = view.locals.get('alice'); + var value = view.rawView.locals.get('alice'); expect(value).not.toBe(null); - expect(value.tagName.toLowerCase()).toEqual('div'); + expect(value.domElement.tagName.toLowerCase()).toEqual('div'); async.done(); }) })); - it('should assign the element instance to a user-defined variable with camelCase using dash-case', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, + it('should assign the element instance to a user-defined variable with camelCase using dash-case', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({inline: '<p><div var-super-alice><i>Hello</i></div></p>'})); - compiler.compile(MyComp).then((pv) => { - createView(pv); - expect(view.locals).not.toBe(null); + tb.createView(MyComp, {context: ctx}).then((view) => { + expect(view.rawView.locals).not.toBe(null); - var value = view.locals.get('superAlice'); + var value = view.rawView.locals.get('superAlice'); expect(value).not.toBe(null); - expect(value.tagName.toLowerCase()).toEqual('div'); + expect(value.domElement.tagName.toLowerCase()).toEqual('div'); async.done(); }) @@ -450,49 +393,47 @@ export function main() { describe("BindingPropagationConfig", () => { it("can be used to disable the change detection of the component's template", - inject([AsyncTestCompleter], (async) => { + inject([TestBed, AsyncTestCompleter], (tb, async) => { - tplResolver.setTemplate(MyComp, new Template({ + tb.overrideTemplate(MyComp, new Template({ inline: '<push-cmp #cmp></push-cmp>', directives: [[[PushBasedComp]]] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var cmp = view.locals.get('cmp'); + var cmp = view.rawView.locals.get('cmp'); - cd.detectChanges(); + view.detectChanges(); expect(cmp.numberOfChecks).toEqual(1); - cd.detectChanges(); + view.detectChanges(); expect(cmp.numberOfChecks).toEqual(1); cmp.propagate(); - cd.detectChanges(); + view.detectChanges(); expect(cmp.numberOfChecks).toEqual(2); async.done(); }) })); - it('should not affect updating properties on the component', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should not affect updating properties on the component', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<push-cmp [prop]="ctxProp" #cmp></push-cmp>', directives: [[[PushBasedComp]]] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var cmp = view.locals.get('cmp'); + var cmp = view.rawView.locals.get('cmp'); ctx.ctxProp = "one"; - cd.detectChanges(); + view.detectChanges(); expect(cmp.prop).toEqual("one"); ctx.ctxProp = "two"; - cd.detectChanges(); + view.detectChanges(); expect(cmp.prop).toEqual("two"); async.done(); @@ -500,24 +441,23 @@ export function main() { })); }); - it('should create a component that injects a @Parent', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should create a component that injects a @Parent', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<some-directive><cmp-with-parent #child></cmp-with-parent></some-directive>', directives: [SomeDirective, CompWithParent] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var childComponent = view.locals.get('child'); + var childComponent = view.rawView.locals.get('child'); expect(childComponent.myParent).toBeAnInstanceOf(SomeDirective); async.done(); }) })); - it('should create a component that injects an @Ancestor', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should create a component that injects an @Ancestor', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: ` <some-directive> <p> @@ -527,18 +467,17 @@ export function main() { directives: [SomeDirective, CompWithAncestor] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var childComponent = view.locals.get('child'); + var childComponent = view.rawView.locals.get('child'); expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective); async.done(); }) })); - it('should create a component that injects an @Ancestor through viewport directive', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should create a component that injects an @Ancestor through viewport directive', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: ` <some-directive> <p *if="true"> @@ -548,11 +487,10 @@ export function main() { directives: [SomeDirective, CompWithAncestor, If] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); - cd.detectChanges(); + tb.createView(MyComp, {context: ctx}).then((view) => { + view.detectChanges(); - var subview = view.viewContainers[1].get(0); + var subview = view.rawView.viewContainers[1].get(0); var childComponent = subview.locals.get('child'); expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective); @@ -560,16 +498,15 @@ export function main() { }); })); - it('should support events', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should support events via EventEmitter', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<div emitter listener></div>', directives: [DecoratorEmitingEvent, DecoratorListeningEvent] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var injector = view.elementInjectors[0]; + var injector = view.rawView.elementInjectors[0]; var emitter = injector.get(DecoratorEmitingEvent); var listener = injector.get(DecoratorListeningEvent); @@ -585,34 +522,54 @@ export function main() { }); })); - it('should support dynamic components', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + if (DOM.supportsDOMEvents()) { + it('should support render events', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ + inline: '<div listener></div>', + directives: [DecoratorListeningDomEvent] + })); + + tb.createView(MyComp, {context: ctx}).then((view) => { + + var injector = view.rawView.elementInjectors[0]; + + var listener = injector.get(DecoratorListeningDomEvent); + + dispatchEvent(view.rootNodes[0], 'domEvent'); + + expect(listener.eventType).toEqual('domEvent'); + + async.done(); + }); + })); + } + + it('should support dynamic components', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<dynamic-comp #dynamic></dynamic-comp>', directives: [DynamicComp] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var dynamicComponent = view.locals.get("dynamic"); + var dynamicComponent = view.rawView.locals.get("dynamic"); expect(dynamicComponent).toBeAnInstanceOf(DynamicComp); dynamicComponent.done.then((_) => { - cd.detectChanges(); - expect(view.nodes).toHaveText('hello'); + view.detectChanges(); + expect(view.rootNodes).toHaveText('hello'); async.done(); }); }); })); - it('should support static attributes', inject([AsyncTestCompleter], (async) => { - tplResolver.setTemplate(MyComp, new Template({ + it('should support static attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => { + tb.overrideTemplate(MyComp, new Template({ inline: '<input static type="text" title></input>', directives: [NeedsAttribute] })); - compiler.compile(MyComp).then((pv) => { - createView(pv); + tb.createView(MyComp, {context: ctx}).then((view) => { - var injector = view.elementInjectors[0]; + var injector = view.rawView.elementInjectors[0]; var needsAttribute = injector.get(NeedsAttribute); expect(needsAttribute.typeAttribute).toEqual('text'); expect(needsAttribute.titleAttribute).toEqual(''); @@ -632,9 +589,9 @@ export function main() { if (assertionsEnabled()) { - function expectCompileError(inlineTpl, errMessage, done) { - tplResolver.setTemplate(MyComp, new Template({inline: inlineTpl})); - PromiseWrapper.then(compiler.compile(MyComp), + function expectCompileError(tb, inlineTpl, errMessage, done) { + tb.overrideTemplate(MyComp, new Template({inline: inlineTpl})); + PromiseWrapper.then(tb.createView(MyComp), (value) => { throw new BaseException("Test failure: should not have come here as an exception was expected"); }, @@ -645,32 +602,36 @@ export function main() { ); } - it('should raise an error if no directive is registered for a template with template bindings', inject([AsyncTestCompleter], (async) => { + it('should raise an error if no directive is registered for a template with template bindings', inject([TestBed, AsyncTestCompleter], (tb, async) => { expectCompileError( + tb, '<div><div template="if: foo"></div></div>', 'Missing directive to handle \'if\' in <div template="if: foo">', () => async.done() ); })); - it('should raise an error for missing template directive (1)', inject([AsyncTestCompleter], (async) => { + it('should raise an error for missing template directive (1)', inject([TestBed, AsyncTestCompleter], (tb, async) => { expectCompileError( + tb, '<div><template foo></template></div>', 'Missing directive to handle: <template foo>', () => async.done() ); })); - it('should raise an error for missing template directive (2)', inject([AsyncTestCompleter], (async) => { + it('should raise an error for missing template directive (2)', inject([TestBed, AsyncTestCompleter], (tb, async) => { expectCompileError( + tb, '<div><template *if="condition"></template></div>', 'Missing directive to handle: <template *if="condition">', () => async.done() ); })); - it('should raise an error for missing template directive (3)', inject([AsyncTestCompleter], (async) => { + it('should raise an error for missing template directive (3)', inject([TestBed, AsyncTestCompleter], (tb, async) => { expectCompileError( + tb, '<div *if="condition"></div>', 'Missing directive to handle \'if\' in MyComp: <div *if="condition">', () => async.done() @@ -746,6 +707,8 @@ class PushBasedComp { } @Component() +@Template({directives: [ +]}) class MyComp { ctxProp:string; ctxNumProp; @@ -909,6 +872,22 @@ class DecoratorListeningEvent { } } +@Decorator({ + selector: '[listener]', + events: {'domEvent': 'onEvent($event.type)'} +}) +class DecoratorListeningDomEvent { + eventType: string; + + constructor() { + this.eventType = ''; + } + + onEvent(eventType: string) { + this.eventType = eventType; + } +} + @Component({ selector: '[id]', bind: {'id': 'id'} diff --git a/modules/angular2/test/core/compiler/private_component_loader_spec.js b/modules/angular2/test/core/compiler/private_component_loader_spec.js index e9da39f6da..4c4245b1cf 100644 --- a/modules/angular2/test/core/compiler/private_component_loader_spec.js +++ b/modules/angular2/test/core/compiler/private_component_loader_spec.js @@ -14,7 +14,7 @@ export function main() { var loader; beforeEach(() => { - loader = new PrivateComponentLoader(null, null, null, new DirectiveMetadataReader()); + loader = new PrivateComponentLoader(null, new DirectiveMetadataReader(), null); }); describe('Load errors', () => { diff --git a/modules/angular2/test/core/compiler/shadow_dom/content_tag_spec.js b/modules/angular2/test/core/compiler/shadow_dom/content_tag_spec.js deleted file mode 100644 index 298f05d9bb..0000000000 --- a/modules/angular2/test/core/compiler/shadow_dom/content_tag_spec.js +++ /dev/null @@ -1,47 +0,0 @@ -import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject, el, proxy} from 'angular2/test_lib'; -import {IMPLEMENTS} from 'angular2/src/facade/lang'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {LightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; - -@proxy -@IMPLEMENTS(LightDom) -class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}} - -var _scriptStart = `<script start=""></script>`; -var _scriptEnd = `<script end=""></script>`; - -export function main() { - describe('Content', function() { - var parent; - var content; - - beforeEach(() => { - parent = el(`<div>${_scriptStart}${_scriptEnd}`); - content = DOM.firstChild(parent); - }); - - it("should insert the nodes", () => { - var c = new Content(null, content, ''); - c.insert([el("<a></a>"), el("<b></b>")]) - - expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}<a></a><b></b>${_scriptEnd}`); - }); - - it("should remove the nodes from the previous insertion", () => { - var c = new Content(null, content, ''); - c.insert([el("<a></a>")]); - c.insert([el("<b></b>")]); - - expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}<b></b>${_scriptEnd}`); - }); - - it("should insert empty list", () => { - var c = new Content(null, content, ''); - c.insert([el("<a></a>")]); - c.insert([]); - - expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}${_scriptEnd}`); - }); - }); -} diff --git a/modules/angular2/test/core/compiler/shadow_dom/light_dom_spec.js b/modules/angular2/test/core/compiler/shadow_dom/light_dom_spec.js deleted file mode 100644 index a017a7d19d..0000000000 --- a/modules/angular2/test/core/compiler/shadow_dom/light_dom_spec.js +++ /dev/null @@ -1,219 +0,0 @@ -import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject, el, proxy} from 'angular2/test_lib'; -import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang'; -import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {LightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; -import {View} from 'angular2/src/core/compiler/view'; -import {ViewContainer} from 'angular2/src/core/compiler/view_container'; - -@proxy -@IMPLEMENTS(View) -class FakeView { - contentTags; - viewContainers; - - constructor(containers = null) { - this.contentTags = []; - this.viewContainers = []; - if (isPresent(containers)) { - ListWrapper.forEach(containers, (c) => { - if (c instanceof FakeContentTag) { - ListWrapper.push(this.contentTags, c); - } else { - ListWrapper.push(this.contentTags, null); - } - if (c instanceof FakeViewContainer) { - ListWrapper.push(this.viewContainers, c); - } else { - ListWrapper.push(this.viewContainers, null); - } - }); - } - } - - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -@proxy -@IMPLEMENTS(ViewContainer) -class FakeViewContainer { - templateElement; - _nodes; - _contentTagContainers; - - constructor(templateEl, nodes = null, views = null) { - this.templateElement = templateEl; - this._nodes = nodes; - this._contentTagContainers = views; - } - - nodes(){ - return this._nodes; - } - - contentTagContainers(){ - return this._contentTagContainers; - } - - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - - -@proxy -@IMPLEMENTS(Content) -class FakeContentTag { - select; - _nodes; - contentStartElement; - - constructor(contentEl, select = '', nodes = null) { - this.contentStartElement = contentEl; - this.select = select; - this._nodes = nodes; - } - - insert(nodes){ - this._nodes = ListWrapper.clone(nodes); - } - - nodes() { - return this._nodes; - } - - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - - -export function main() { - describe('LightDom', function() { - var lightDomView; - - beforeEach(() => { - lightDomView = new FakeView(); - }); - - describe("contentTags", () => { - it("should collect content tags from element injectors", () => { - var tag = new FakeContentTag(el('<script></script>')); - var shadowDomView = new FakeView([tag]); - - var lightDom = new LightDom(lightDomView, shadowDomView, - el("<div></div>")); - - expect(lightDom.contentTags()).toEqual([tag]); - }); - - it("should collect content tags from ViewContainers", () => { - var tag = new FakeContentTag(el('<script></script>')); - var vc = new FakeViewContainer(null, null, [ - new FakeView([tag]) - ]); - var shadowDomView = new FakeView([vc]); - var lightDom = new LightDom(lightDomView, shadowDomView, - el("<div></div>")); - - expect(lightDom.contentTags()).toEqual([tag]); - }); - }); - - describe("expandedDomNodes", () => { - it("should contain root nodes", () => { - var lightDomEl = el("<div><a></a></div>") - var lightDom = new LightDom(lightDomView, new FakeView(), lightDomEl); - expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]); - }); - - it("should include view container nodes", () => { - var lightDomEl = el("<div><template></template></div>"); - var lightDom = new LightDom( - new FakeView([ - new FakeViewContainer( - DOM.firstChild(lightDomEl), // template element - [el('<a></a>')] // light DOM nodes of view container - ) - ]), - null, - lightDomEl); - - expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]); - }); - - it("should include content nodes", () => { - var lightDomEl = el("<div><content></content></div>"); - var lightDom = new LightDom( - new FakeView([ - new FakeContentTag( - DOM.firstChild(lightDomEl), // content element - '', // selector - [el('<a></a>')] // light DOM nodes of content tag - ) - ]), - null, - lightDomEl); - - expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]); - }); - - it("should work when the element injector array contains nulls", () => { - var lightDomEl = el("<div><a></a></div>") - - var lightDomView = new FakeView(); - - var lightDom = new LightDom( - lightDomView, - new FakeView(), - lightDomEl); - - expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]); - }); - }); - - describe("redistribute", () => { - it("should redistribute nodes between content tags with select property set", () => { - var contentA = new FakeContentTag(null, "a"); - var contentB = new FakeContentTag(null, "b"); - - var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>") - - var lightDom = new LightDom(lightDomView, new FakeView([ - contentA, - contentB - ]), lightDomEl); - - lightDom.redistribute(); - - expect(toHtml(contentA.nodes())).toEqual(["<a>1</a>", "<a>3</a>"]); - expect(toHtml(contentB.nodes())).toEqual(["<b>2</b>"]); - }); - - it("should support wildcard content tags", () => { - var wildcard = new FakeContentTag(null, ''); - var contentB = new FakeContentTag(null, "b"); - - var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>") - - var lightDom = new LightDom(lightDomView, new FakeView([ - wildcard, - contentB - ]), lightDomEl); - - lightDom.redistribute(); - - expect(toHtml(wildcard.nodes())).toEqual(["<a>1</a>", "<b>2</b>", "<a>3</a>"]); - expect(toHtml(contentB.nodes())).toEqual([]); - }); - }); - }); -} - -function toHtml(nodes) { - if (isBlank(nodes)) return []; - return ListWrapper.map(nodes, DOM.getOuterHTML); -} diff --git a/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js b/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js deleted file mode 100644 index e28404a8b9..0000000000 --- a/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js +++ /dev/null @@ -1,384 +0,0 @@ -import { - AsyncTestCompleter, - beforeEach, - ddescribe, - describe, - el, - expect, - iit, - inject, - it, - xit, -} from 'angular2/test_lib'; - -import {StringMapWrapper, List} from 'angular2/src/facade/collection'; -import {Type} from 'angular2/src/facade/lang'; -import {DOM} from 'angular2/src/dom/dom_adapter'; - -import {Injector} from 'angular2/di'; -import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection'; -import {ExceptionHandler} from 'angular2/src/core/exception_handler'; - -import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; -import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; -import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {ShadowDomStrategy, - NativeShadowDomStrategy, - EmulatedScopedShadowDomStrategy, - EmulatedUnscopedShadowDomStrategy, -} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; -import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; -import {UrlResolver} from 'angular2/src/services/url_resolver'; -import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; -import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; - -import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; - -import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations'; -import {Template} from 'angular2/src/core/annotations/template'; - -import {ViewContainer} from 'angular2/src/core/compiler/view_container'; - -import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; - -export function main() { - BrowserDomAdapter.makeCurrent(); - describe('integration tests', function() { - var urlResolver; - var styleUrlResolver; - var styleInliner; - var strategies = { - "scoped" : () => new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, DOM.createElement('div')), - "unscoped" : () => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, DOM.createElement('div')) - } - if (DOM.supportsNativeShadowDOM()) { - StringMapWrapper.set(strategies, "native", () => new NativeShadowDomStrategy(styleUrlResolver)); - } - - StringMapWrapper.forEach(strategies, - (strategyFactory, name) => { - - describe(`${name} shadow dom strategy`, () => { - var compiler, tplResolver; - - beforeEach(() => { - urlResolver = new UrlResolver(); - styleUrlResolver = new StyleUrlResolver(urlResolver); - styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver); - tplResolver = new MockTemplateResolver(); - compiler = new Compiler(dynamicChangeDetection, - new TemplateLoader(null, null), - new DirectiveMetadataReader(), - new Parser(new Lexer()), - new CompilerCache(), - strategyFactory(), - tplResolver, - new ComponentUrlMapper(), - urlResolver - ); - }); - - function compile(template, directives: List<Type>, assertions) { - tplResolver.setTemplate(MyComp, new Template({ - inline: template, - directives: directives - })); - compiler.compile(MyComp) - .then(createView) - .then((view) => { - var lc = new LifeCycle(new ExceptionHandler(), view.changeDetector, false); - assertions(view, lc); - }); - } - - it('should support simple components', inject([AsyncTestCompleter], (async) => { - var temp = '<simple>' + - '<div>A</div>' + - '</simple>'; - - compile(temp, [Simple], (view, lc) => { - expect(view.nodes).toHaveText('SIMPLE(A)'); - - async.done(); - }); - })); - - it('should support multiple content tags', inject([AsyncTestCompleter], (async) => { - var temp = '<multiple-content-tags>' + - '<div>B</div>' + - '<div>C</div>' + - '<div class="left">A</div>' + - '</multiple-content-tags>'; - - compile(temp, [MultipleContentTagsComponent], (view, lc) => { - expect(view.nodes).toHaveText('(A, BC)'); - async.done(); - }); - })); - - it('should redistribute only direct children', inject([AsyncTestCompleter], (async) => { - var temp = '<multiple-content-tags>' + - '<div>B<div class="left">A</div></div>' + - '<div>C</div>' + - '</multiple-content-tags>'; - - compile(temp, [MultipleContentTagsComponent], (view, lc) => { - expect(view.nodes).toHaveText('(, BAC)'); - async.done(); - }); - })); - - it("should redistribute direct child viewcontainers when the light dom changes", inject([AsyncTestCompleter], (async) => { - var temp = '<multiple-content-tags>' + - '<div><div template="manual" class="left">A</div></div>' + - '<div>B</div>' + - '</multiple-content-tags>'; - - compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => { - var dir = view.elementInjectors[1].get(ManualViewportDirective); - - expect(view.nodes).toHaveText('(, B)'); - - dir.show(); - lc.tick(); - - expect(view.nodes).toHaveText('(, AB)'); - - dir.hide(); - lc.tick(); - - expect(view.nodes).toHaveText('(, B)'); - - async.done(); - }); - })); - - it("should redistribute when the light dom changes", inject([AsyncTestCompleter], (async) => { - var temp = '<multiple-content-tags>' + - '<div template="manual" class="left">A</div>' + - '<div>B</div>' + - '</multiple-content-tags>'; - - compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => { - var dir = view.elementInjectors[1].get(ManualViewportDirective); - - expect(view.nodes).toHaveText('(, B)'); - - dir.show(); - lc.tick(); - - expect(view.nodes).toHaveText('(A, B)'); - - dir.hide(); - lc.tick(); - - expect(view.nodes).toHaveText('(, B)'); - - async.done(); - }); - })); - - it("should support nested components", inject([AsyncTestCompleter], (async) => { - var temp = '<outer-with-indirect-nested>' + - '<div>A</div>' + - '<div>B</div>' + - '</outer-with-indirect-nested>'; - - compile(temp, [OuterWithIndirectNestedComponent], (view, lc) => { - expect(view.nodes).toHaveText('OUTER(SIMPLE(AB))'); - - async.done(); - }); - })); - - it("should support nesting with content being direct child of a nested component", inject([AsyncTestCompleter], (async) => { - var temp = '<outer>' + - '<div template="manual" class="left">A</div>' + - '<div>B</div>' + - '<div>C</div>' + - '</outer>'; - - compile(temp, [OuterComponent, ManualViewportDirective], (view, lc) => { - var dir = view.elementInjectors[1].get(ManualViewportDirective); - - expect(view.nodes).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); - - dir.show(); - lc.tick(); - - expect(view.nodes).toHaveText('OUTER(INNER(INNERINNER(A,BC)))'); - async.done(); - }); - })); - - it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter], (async) => { - var temp = '<conditional-content>' + - '<div class="left">A</div>' + - '<div>B</div>' + - '<div>C</div>' + - '</conditional-content>'; - - - compile(temp, [ConditionalContentComponent, AutoViewportDirective], (view, lc) => { - var cmp = view.elementInjectors[0].get(ConditionalContentComponent); - - expect(view.nodes).toHaveText('(, ABC)'); - - cmp.showLeft(); - lc.tick(); - - expect(view.nodes).toHaveText('(A, BC)'); - - cmp.hideLeft(); - lc.tick(); - - expect(view.nodes).toHaveText('(, ABC)'); - - async.done(); - }); - })); - - //Implement once NgElement support changing a class - //it("should redistribute when a class has been added or removed"); - //it('should not lose focus', () => { - // var temp = `<simple>aaa<input type="text" id="focused-input" ng-class="{'aClass' : showClass}"> bbb</simple>`; - // - // compile(temp, (view, lc) => { - // var input = view.nodes[1]; - // input.focus(); - // - // expect(document.activeElement.id).toEqual("focused-input"); - // - // // update class of input - // - // expect(document.activeElement.id).toEqual("focused-input"); - // }); - //}); - }); - }); - - }); -} - -class TestDirectiveMetadataReader extends DirectiveMetadataReader { - shadowDomStrategy; - - constructor(shadowDomStrategy) { - super(); - this.shadowDomStrategy = shadowDomStrategy; - } - - parseShadowDomStrategy(annotation:Component):ShadowDomStrategy{ - return this.shadowDomStrategy; - } -} - -@Viewport({ - selector: '[manual]' -}) -class ManualViewportDirective { - viewContainer; - constructor(viewContainer:ViewContainer) { - this.viewContainer = viewContainer; - } - - show() { this.viewContainer.create(); } - hide() { this.viewContainer.remove(0); } -} - -@Viewport({ - selector: '[auto]', - bind: { - 'auto': 'auto' - } -}) -class AutoViewportDirective { - viewContainer; - constructor(viewContainer:ViewContainer) { - this.viewContainer = viewContainer; - } - - set auto(newValue:boolean) { - if (newValue) { - this.viewContainer.create(); - } else { - this.viewContainer.remove(0); - } - } -} - -@Component({selector: 'simple'}) -@Template({inline: 'SIMPLE(<content></content>)'}) -class Simple { -} - -@Component({selector: 'multiple-content-tags'}) -@Template({ - inline: '(<content select=".left"></content>, <content></content>)' -}) -class MultipleContentTagsComponent { -} - - -@Component({selector: 'conditional-content'}) -@Template({ - inline: '<div>(<div *auto="cond"><content select=".left"></content></div>, <content></content>)</div>', - directives: [AutoViewportDirective] -}) -class ConditionalContentComponent { - cond:boolean; - - constructor() { - this.cond = false; - } - - showLeft() { this.cond = true; } - hideLeft() { this.cond = false; } -} - -@Component({selector: 'outer-with-indirect-nested'}) -@Template({ - inline: 'OUTER(<simple><div><content></content></div></simple>)', - directives: [Simple] -}) -class OuterWithIndirectNestedComponent { -} - -@Component({selector: 'outer'}) -@Template({ - inline: 'OUTER(<inner><content></content></inner>)', - directives: [InnerComponent] -}) -class OuterComponent { -} - -@Component({selector: 'inner'}) -@Template({ - inline: 'INNER(<innerinner><content></content></innerinner>)', - directives: [InnerInnerComponent] -}) -class InnerComponent { -} - -@Component({selector: 'innerinner'}) -@Template({ - inline: 'INNERINNER(<content select=".left"></content>,<content></content>)' -}) -class InnerInnerComponent { -} - - -@Component({selector: 'my-comp'}) -@Template({ - directives: [MultipleContentTagsComponent, ManualViewportDirective, - ConditionalContentComponent, OuterWithIndirectNestedComponent, OuterComponent] -}) -class MyComp { -} - -function createView(pv) { - var view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, {}, null); - return view; -} diff --git a/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js b/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js deleted file mode 100644 index 7638a58d45..0000000000 --- a/modules/angular2/test/core/compiler/shadow_dom_strategy_spec.js +++ /dev/null @@ -1,130 +0,0 @@ -import { - AsyncTestCompleter, - beforeEach, - ddescribe, - describe, - el, - expect, - iit, - inject, - it, - SpyObject, -} from 'angular2/test_lib'; - -import { - NativeShadowDomStrategy, - EmulatedScopedShadowDomStrategy, - EmulatedUnscopedShadowDomStrategy -} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {UrlResolver} from 'angular2/src/services/url_resolver'; -import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; -import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; -import {ProtoView} from 'angular2/src/core/compiler/view'; - -import {XHR} from 'angular2/src/services/xhr'; - -import {isPresent, isBlank} from 'angular2/src/facade/lang'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {Map, MapWrapper} from 'angular2/src/facade/collection'; -import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; - -import {DynamicProtoChangeDetector} from 'angular2/change_detection'; - -export function main() { - var strategy; - - describe('NativeShadowDomStratgey', () => { - beforeEach(() => { - var urlResolver = new UrlResolver(); - var styleUrlResolver = new StyleUrlResolver(urlResolver); - strategy = new NativeShadowDomStrategy(styleUrlResolver); - }); - - it('should attach the view nodes to the shadow root', () => { - var host = el('<div></div>'); - var nodes = el('<div>view</div>'); - var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); - var view = pv.instantiate(null, null); - - strategy.attachTemplate(host, view); - var shadowRoot = DOM.getShadowRoot(host); - expect(isPresent(shadowRoot)).toBeTruthy(); - expect(shadowRoot).toHaveText('view'); - }); - }); - - describe('EmulatedScopedShadowDomStratgey', () => { - var xhr, styleHost; - - beforeEach(() => { - var urlResolver = new UrlResolver(); - var styleUrlResolver = new StyleUrlResolver(urlResolver); - xhr = new FakeXHR(); - var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver); - styleHost = el('<div></div>'); - strategy = new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, styleHost); - }); - - it('should attach the view nodes as child of the host element', () => { - var host = el('<div><span>original content</span></div>'); - var nodes = el('<div>view</div>'); - var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); - var view = pv.instantiate(null, null); - - strategy.attachTemplate(host, view); - var firstChild = DOM.firstChild(host); - expect(DOM.tagName(firstChild).toLowerCase()).toEqual('div'); - expect(firstChild).toHaveText('view'); - expect(host).toHaveText('view'); - }); - }); - - describe('EmulatedUnscopedShadowDomStratgey', () => { - var styleHost; - - beforeEach(() => { - var urlResolver = new UrlResolver(); - var styleUrlResolver = new StyleUrlResolver(urlResolver); - styleHost = el('<div></div>'); - strategy = new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, styleHost); - }); - - it('should attach the view nodes as child of the host element', () => { - var host = el('<div><span>original content</span></div>'); - var nodes = el('<div>view</div>'); - var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null); - var view = pv.instantiate(null, null); - - strategy.attachTemplate(host, view); - var firstChild = DOM.firstChild(host); - expect(DOM.tagName(firstChild).toLowerCase()).toEqual('div'); - expect(firstChild).toHaveText('view'); - expect(host).toHaveText('view'); - }); - }); -} - -class FakeXHR extends XHR { - _responses: Map; - - constructor() { - super(); - this._responses = MapWrapper.create(); - } - - get(url: string): Promise<string> { - var response = MapWrapper.get(this._responses, url); - if (isBlank(response)) { - return PromiseWrapper.reject('xhr error'); - } - - return PromiseWrapper.resolve(response); - } - - reply(url: string, response: string) { - MapWrapper.set(this._responses, url, response); - } -} - -class SomeComponent {} -class SomeOtherComponent {} diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js deleted file mode 100644 index ee8b88980c..0000000000 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ /dev/null @@ -1,251 +0,0 @@ -import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib'; -import {View, ProtoView} from 'angular2/src/core/compiler/view'; -import {ViewContainer} from 'angular2/src/core/compiler/view_container'; -import {IMPLEMENTS} from 'angular2/src/facade/lang'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; -import {Injector} from 'angular2/di'; -import {ProtoElementInjector, ElementInjector} from 'angular2/src/core/compiler/element_injector'; -import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection'; - -function createView(nodes) { - var view = new View(null, nodes, MapWrapper.create()); - var cd = new DynamicProtoChangeDetector(null, null).instantiate(view, [], null, []); - view.init(cd, [], [], [], [], [], [], [], [], []); - return view; -} - -@proxy -@IMPLEMENTS(ChangeDetector) -class AttachableChangeDetector { - parent; - constructor() { - } - remove() { - this.parent = null; - } - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -@proxy -@IMPLEMENTS(View) -class HydrateAwareFakeView { - isHydrated: boolean; - nodes: List; - changeDetector: ChangeDetector; - rootElementInjectors; - constructor(isHydrated) { - this.isHydrated = isHydrated; - this.nodes = [DOM.createElement('div')]; - this.rootElementInjectors = []; - this.changeDetector = new AttachableChangeDetector(); - } - - hydrated() { - return this.isHydrated; - } - - - hydrate(_, __, ___, ____, _____) { - this.isHydrated = true; - } - - dehydrate() { - this.isHydrated = false; - } - - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -export function main() { - describe('ViewContainer', () => { - var viewContainer, parentView, protoView, dom, customViewWithOneNode, - customViewWithTwoNodes, elementInjector; - - beforeEach(() => { - dom = el(`<div><stuff></stuff><div insert-after-me></div><stuff></stuff></div>`); - var insertionElement = dom.childNodes[1]; - parentView = createView([dom.childNodes[0]]); - protoView = new ProtoView(null, el('<div>hi</div>'), new DynamicProtoChangeDetector(null, null), - new NativeShadowDomStrategy(null)); - elementInjector = new ElementInjector(null, null, null); - viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector, - null); - customViewWithOneNode = createView([el('<div>single</div>')]); - customViewWithTwoNodes = createView([el('<div>one</div>'), el('<div>two</div>')]); - }); - - describe('when dehydrated', () => { - it('should throw if create is called', () => { - expect(() => viewContainer.create()).toThrowError(); - }); - }); - - describe('when hydrated', () => { - function textInViewContainer() { - var out = ''; - // skipping starting filler, insert-me and final filler. - for (var i = 2; i < dom.childNodes.length - 1; i++) { - if (i != 2) out += ' '; - out += DOM.getInnerHTML(dom.childNodes[i]); - } - return out; - } - - beforeEach(() => { - viewContainer.hydrate(new Injector([]), null, null); - var fillerView = createView([el('<filler>filler</filler>')]); - viewContainer.insert(fillerView); - }); - - it('should create new views from protoView', () => { - viewContainer.create(); - expect(textInViewContainer()).toEqual('filler hi'); - expect(viewContainer.length).toBe(2); - }); - - it('should create new views from protoView at index', () => { - viewContainer.create(0); - expect(textInViewContainer()).toEqual('hi filler'); - expect(viewContainer.length).toBe(2); - }); - - it('should insert new views at the end by default', () => { - viewContainer.insert(customViewWithOneNode); - expect(textInViewContainer()).toEqual('filler single'); - expect(viewContainer.get(1)).toBe(customViewWithOneNode); - expect(viewContainer.length).toBe(2); - }); - - it('should insert new views at the given index', () => { - viewContainer.insert(customViewWithOneNode, 0); - expect(textInViewContainer()).toEqual('single filler'); - expect(viewContainer.get(0)).toBe(customViewWithOneNode); - expect(viewContainer.length).toBe(2); - }); - - it('should remove the last view by default', () => { - viewContainer.insert(customViewWithOneNode); - - viewContainer.remove(); - - expect(textInViewContainer()).toEqual('filler'); - expect(viewContainer.length).toBe(1); - }); - - it('should remove the view at a given index', () => { - viewContainer.insert(customViewWithOneNode); - viewContainer.insert(customViewWithTwoNodes); - - viewContainer.remove(1); - - expect(textInViewContainer()).toEqual('filler one two'); - expect(viewContainer.get(1)).toBe(customViewWithTwoNodes); - expect(viewContainer.length).toBe(2); - }); - - it('should detach the last view by default', () => { - viewContainer.insert(customViewWithOneNode); - expect(viewContainer.length).toBe(2); - - var detachedView = viewContainer.detach(); - - expect(detachedView).toBe(customViewWithOneNode); - expect(textInViewContainer()).toEqual('filler'); - expect(viewContainer.length).toBe(1); - }); - - it('should detach the view at a given index', () => { - viewContainer.insert(customViewWithOneNode); - viewContainer.insert(customViewWithTwoNodes); - expect(viewContainer.length).toBe(3); - - var detachedView = viewContainer.detach(1); - expect(detachedView).toBe(customViewWithOneNode); - expect(textInViewContainer()).toEqual('filler one two'); - expect(viewContainer.length).toBe(2); - }); - - it('should keep views hydration state during insert', () => { - var hydratedView = new HydrateAwareFakeView(true); - var dehydratedView = new HydrateAwareFakeView(false); - viewContainer.insert(hydratedView); - viewContainer.insert(dehydratedView); - - expect(hydratedView.hydrated()).toBe(true); - expect(dehydratedView.hydrated()).toBe(false); - }); - - it('should dehydrate on remove', () => { - var hydratedView = new HydrateAwareFakeView(true); - viewContainer.insert(hydratedView); - viewContainer.remove(); - - expect(hydratedView.hydrated()).toBe(false); - }); - - it('should keep views hydration state during detach', () => { - var hydratedView = new HydrateAwareFakeView(true); - var dehydratedView = new HydrateAwareFakeView(false); - viewContainer.insert(hydratedView); - viewContainer.insert(dehydratedView); - - expect(viewContainer.detach().hydrated()).toBe(false); - expect(viewContainer.detach().hydrated()).toBe(true); - }); - - it('should support adding/removing views with more than one node', () => { - viewContainer.insert(customViewWithTwoNodes); - viewContainer.insert(customViewWithOneNode); - - expect(textInViewContainer()).toEqual('filler one two single'); - - viewContainer.remove(1); - expect(textInViewContainer()).toEqual('filler single'); - }); - }); - - describe('should update injectors and parent views.', () => { - var fancyView; - beforeEach(() => { - var parser = new Parser(new Lexer()); - viewContainer.hydrate(new Injector([]), null, null); - - var pv = new ProtoView(null, el('<div class="ng-binding">{{}}</div>'), - new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); - pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); - pv.bindTextNode(0, parser.parseBinding('foo', null)); - fancyView = pv.instantiate(null, null); - }); - - it('hydrating should update rootElementInjectors and parent change detector', () => { - viewContainer.insert(fancyView); - ListWrapper.forEach(fancyView.rootElementInjectors, (inj) => - expect(inj.parent).toBe(elementInjector)); - - expect(parentView.changeDetector.lightDomChildren.length).toBe(1); - }); - - it('dehydrating should update rootElementInjectors and parent change detector', () => { - viewContainer.insert(fancyView); - viewContainer.remove(); - ListWrapper.forEach(fancyView.rootElementInjectors, (inj) => - expect(inj.parent).toBe(null)); - expect(parentView.changeDetector.lightDomChildren.length).toBe(0); - expect(viewContainer.length).toBe(0); - }); - }); - }); -} - -class SomeDirective { - prop; - constructor() { - this.prop = 'foo'; - } -} diff --git a/modules/angular2/test/core/compiler/view_pool_spec.js b/modules/angular2/test/core/compiler/view_pool_spec.js deleted file mode 100644 index 3146233244..0000000000 --- a/modules/angular2/test/core/compiler/view_pool_spec.js +++ /dev/null @@ -1,46 +0,0 @@ -import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib'; - -import {View} from 'angular2/src/core/compiler/view'; -import {ViewPool} from 'angular2/src/core/compiler/view_pool'; -import {IMPLEMENTS} from 'angular2/src/facade/lang'; - -@proxy -@IMPLEMENTS(View) -class FakeView { - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -export function main() { - describe('ViewPool', () => { - var viewPool, capacity = 3; - beforeEach(() => { - viewPool = new ViewPool(capacity); - }) - - it('should return null when there are no views', () => { - expect(viewPool.pop()).toBeNull(); - expect(viewPool.length()).toBe(0); - }) - - it('should support storing and retrieving a view', () => { - var view = new FakeView(); - viewPool.push(view); - expect(viewPool.length()).toBe(1); - - expect(viewPool.pop()).toBe(view); - expect(viewPool.length()).toBe(0); - }) - - it('should not store more views that its capacity', () => { - for (var i = 0; i < capacity * 2; i++) viewPool.push(new FakeView()); - expect(viewPool.length()).toBe(capacity); - - for (var i = 0; i < capacity; i++) { - expect(viewPool.pop()).not.toBe(null); - } - expect(viewPool.pop()).toBeNull(); - }) - }) -} diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js deleted file mode 100644 index b8c523fa66..0000000000 --- a/modules/angular2/test/core/compiler/view_spec.js +++ /dev/null @@ -1,787 +0,0 @@ -import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib'; -import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'angular2/src/core/compiler/view'; -import {ProtoElementInjector, ElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector'; -import {EmulatedScopedShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {Component, Decorator, Viewport, Directive, onChange, onAllChangesDone} from 'angular2/src/core/annotations/annotations'; -import {Lexer, Parser, DynamicProtoChangeDetector, - ChangeDetector} from 'angular2/change_detection'; -import {EventEmitter} from 'angular2/src/core/annotations/di'; -import {List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; -import {DOM} from 'angular2/src/dom/dom_adapter'; -import {int, IMPLEMENTS} from 'angular2/src/facade/lang'; -import {Injector} from 'angular2/di'; -import {View} from 'angular2/src/core/compiler/view'; -import {ViewContainer} from 'angular2/src/core/compiler/view_container'; -import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone'; -import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager'; -import {reflector} from 'angular2/src/reflection/reflection'; - -class DummyDirective extends Directive { - constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); } -} - -@proxy -@IMPLEMENTS(ViewContainer) -class FakeViewContainer { - templateElement; - - constructor(templateElement) { - this.templateElement = templateElement; - } - - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -@proxy -@IMPLEMENTS(View) -class FakeView { - noSuchMethod(i) { - super.noSuchMethod(i); - } -} - -export function main() { - describe('view', function() { - var parser, someComponentDirective, someViewportDirective; - - function createView(protoView, eventManager: EventManager = null) { - var ctx = new MyEvaluationContext(); - var view = protoView.instantiate(null, eventManager); - view.hydrate(null, null, null, ctx, null); - return view; - } - - beforeEach(() => { - parser = new Parser(new Lexer()); - someComponentDirective = readDirectiveBinding(SomeComponent); - someViewportDirective = readDirectiveBinding(SomeViewport); - }); - - describe('instantiated from protoView', () => { - var view; - beforeEach(() => { - var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null); - view = pv.instantiate(null, null); - }); - - it('should be dehydrated by default', () => { - expect(view.hydrated()).toBe(false); - }); - - it('should be able to be hydrated and dehydrated', () => { - var ctx = new Object(); - view.hydrate(null, null, null, ctx, null); - expect(view.hydrated()).toBe(true); - - view.dehydrate(); - expect(view.hydrated()).toBe(false); - }); - - it('should hydrate and dehydrate the change detector', () => { - var ctx = new Object(); - view.hydrate(null, null, null, ctx, null); - expect(view.changeDetector.hydrated()).toBe(true); - - view.dehydrate(); - expect(view.changeDetector.hydrated()).toBe(false); - }); - - it('should use the view pool to reuse views', () => { - var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null); - var fakeView = new FakeView(); - pv.returnToPool(fakeView); - - expect(pv.instantiate(null, null)).toBe(fakeView); - }); - }); - - describe('with locals', function() { - var view; - beforeEach(() => { - var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null); - pv.bindVariable('context-foo', 'template-foo'); - view = createView(pv); - }); - - it('should support setting of declared locals', () => { - view.setLocal('context-foo', 'bar'); - expect(view.locals.get('template-foo')).toBe('bar'); - }); - - it('should not throw on undeclared locals', () => { - expect(() => view.setLocal('setMePlease', 'bar')).not.toThrow(); - }); - - it('when dehydrated should set locals to null', () => { - view.setLocal('context-foo', 'bar'); - view.dehydrate(); - view.hydrate(null, null, null, new Object(), null); - expect(view.locals.get('template-foo')).toBe(null); - }); - - it('should throw when trying to set on dehydrated view', () => { - view.dehydrate(); - expect(() => view.setLocal('context-foo', 'bar')).toThrowError(); - }); - }); - - describe('instantiated and hydrated', function() { - - function createCollectDomNodesTestCases(useTemplateElement:boolean) { - - function templateAwareCreateElement(html) { - return el(useTemplateElement ? `<template>${html}</template>` : html); - } - - it('should collect the root node in the ProtoView element', () => { - var pv = new ProtoView(null, templateAwareCreateElement('<div id="1"></div>'), - new DynamicProtoChangeDetector(null, null), null); - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.nodes.length).toBe(1); - expect(DOM.getAttribute(view.nodes[0], 'id')).toEqual('1'); - }); - - 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(null, templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop')); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.bindElements.length).toEqual(1); - expect(view.bindElements[0]).toBe(view.nodes[0]); - }); - - it('should collect property bindings on child elements with ng-binding class', () => { - var pv = new ProtoView(null, templateAwareCreateElement('<div><span></span><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a')); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.bindElements.length).toEqual(1); - expect(view.bindElements[0]).toBe(view.nodes[0].childNodes[1]); - }); - - }); - - describe('collect text nodes with bindings', () => { - - it('should collect text nodes under the root element', () => { - var pv = new ProtoView(null, templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindTextNode(0, parser.parseBinding('a', null)); - pv.bindTextNode(2, parser.parseBinding('b', null)); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.textNodes.length).toEqual(2); - expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[0]); - expect(view.textNodes[1]).toBe(view.nodes[0].childNodes[2]); - }); - - it('should collect text nodes with bindings on child elements with ng-binding class', () => { - var pv = new ProtoView(null, templateAwareCreateElement('<div><span> </span><span class="ng-binding">{{}}</span></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindTextNode(0, parser.parseBinding('b', null)); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.textNodes.length).toEqual(1); - expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[1].childNodes[0]); - }); - - }); - } - - describe('inplace instantiation', () => { - it('should be supported.', () => { - var template = el('<div></div>'); - var pv = new ProtoView(null, template, new DynamicProtoChangeDetector(null, null), - new NativeShadowDomStrategy(null)); - pv.instantiateInPlace = true; - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.nodes[0]).toBe(template); - }); - - it('should be off by default.', () => { - var template = el('<div></div>') - 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); - expect(view.nodes[0]).not.toBe(template); - }); - }); - - describe('collect dom nodes with a regular element as root', () => { - createCollectDomNodesTestCases(false); - }); - - describe('collect dom nodes with a template element as root', () => { - createCollectDomNodesTestCases(true); - }); - - describe('create ElementInjectors', () => { - it('should use the directives of the ProtoElementInjector', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.elementInjectors.length).toBe(1); - expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); - }); - - it('should use the correct parent', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); - pv.bindElement(null, 0, protoParent); - pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective])); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.elementInjectors.length).toBe(2); - expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); - expect(view.elementInjectors[1].parent).toBe(view.elementInjectors[0]); - }); - - it('should not pass the host injector when a parent injector exists', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); - pv.bindElement(null, 0, protoParent); - var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]); - pv.bindElement(null, 0, testProtoElementInjector); - - var hostProtoInjector = new ProtoElementInjector(null, 0, []); - var hostInjector = hostProtoInjector.instantiate(null, null); - var view; - expect(() => view = pv.instantiate(hostInjector, null)).not.toThrow(); - expect(testProtoElementInjector.parentElementInjector).toBe(view.elementInjectors[0]); - expect(testProtoElementInjector.hostElementInjector).toBeNull(); - }); - - it('should pass the host injector when there is no parent injector', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirective])); - var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]); - pv.bindElement(null, 0, testProtoElementInjector); - - var hostProtoInjector = new ProtoElementInjector(null, 0, []); - var hostInjector = hostProtoInjector.instantiate(null, null); - expect(() => pv.instantiate(hostInjector, null)).not.toThrow(); - expect(testProtoElementInjector.parentElementInjector).toBeNull(); - expect(testProtoElementInjector.hostElementInjector).toBe(hostInjector); - }); - }); - - describe('collect root element injectors', () => { - - it('should collect a single root element injector', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); - pv.bindElement(null, 0, protoParent); - pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective])); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.rootElementInjectors.length).toBe(1); - expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); - }); - - it('should collect multiple root element injectors', () => { - var pv = new ProtoView(null, el('<div><span class="ng-binding"></span><span class="ng-binding"></span></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); - pv.bindElement(null, 0, new ProtoElementInjector(null, 2, [AnotherDirective])); - - var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null, null); - expect(view.rootElementInjectors.length).toBe(2) - expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); - expect(view.rootElementInjectors[1].get(AnotherDirective) instanceof AnotherDirective).toBe(true); - }); - - }); - - describe('with component views', () => { - var ctx; - - function createComponentWithSubPV(subProtoView) { - var pv = new ProtoView(null, el('<cmp class="ng-binding"></cmp>'), - new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); - var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeComponent], true)); - binder.componentDirective = someComponentDirective; - binder.nestedProtoView = subProtoView; - return pv; - } - - function createNestedView(protoView) { - ctx = new MyEvaluationContext(); - var view = protoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, ctx, null); - return view; - } - - it('should expose component services to the component', () => { - var subpv = new ProtoView(null, el('<span></span>'), new DynamicProtoChangeDetector(null, null), null); - var pv = createComponentWithSubPV(subpv); - - var view = createNestedView(pv); - - var comp = view.rootElementInjectors[0].get(SomeComponent); - expect(comp.service).toBeAnInstanceOf(SomeService); - }); - - it('should expose component services and component instance to directives in the shadow Dom', - () => { - var subpv = new ProtoView(null, - el('<div dec class="ng-binding">hello shadow dom</div>'), - new DynamicProtoChangeDetector(null, null), - null); - subpv.bindElement(null, 0, - new ProtoElementInjector(null, 0, [ServiceDependentDecorator])); - var pv = createComponentWithSubPV(subpv); - - var view = createNestedView(pv); - - var subView = view.componentChildViews[0]; - var subInj = subView.rootElementInjectors[0]; - var subDecorator = subInj.get(ServiceDependentDecorator); - var comp = view.rootElementInjectors[0].get(SomeComponent); - - expect(subDecorator).toBeAnInstanceOf(ServiceDependentDecorator); - expect(subDecorator.service).toBe(comp.service); - expect(subDecorator.component).toBe(comp); - }); - - function expectViewHasNoDirectiveInstances(view) { - view.elementInjectors.forEach((inj) => expect(inj.hasInstances()).toBe(false)); - } - - it('dehydration should dehydrate child component views too', () => { - var subpv = new ProtoView(null, - el('<div dec class="ng-binding">hello shadow dom</div>'), - new DynamicProtoChangeDetector(null, null), - null); - subpv.bindElement(null, 0, - new ProtoElementInjector(null, 0, [ServiceDependentDecorator])); - var pv = createComponentWithSubPV(subpv); - - var view = createNestedView(pv); - view.dehydrate(); - - expect(view.hydrated()).toBe(false); - expectViewHasNoDirectiveInstances(view); - view.componentChildViews.forEach( - (view) => expectViewHasNoDirectiveInstances(view)); - }); - - it('should create shadow dom (Native Strategy)', () => { - var subpv = new ProtoView(null, el('<span>hello shadow dom</span>'), - new DynamicProtoChangeDetector(null, null), - null); - var pv = createComponentWithSubPV(subpv); - - var view = createNestedView(pv); - - expect(view.nodes[0].shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom'); - }); - - it('should emulate shadow dom (Emulated Strategy)', () => { - var subpv = new ProtoView(null, el('<span>hello shadow dom</span>'), - new DynamicProtoChangeDetector(null, null), null); - - var pv = new ProtoView(null, el('<cmp class="ng-binding"></cmp>'), - 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); - binder.nestedProtoView = subpv; - - var view = createNestedView(pv); - expect(view.nodes[0].childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom'); - }); - - }); - - describe('with template views', () => { - function createViewWithViewport() { - var templateProtoView = new ProtoView(null, - el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null); - var pv = new ProtoView(null, el('<someTmpl class="ng-binding"></someTmpl>'), - new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null)); - var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeViewport])); - binder.viewportDirective = someViewportDirective; - binder.nestedProtoView = templateProtoView; - - return createView(pv); - } - - it('should create a ViewContainer for the Viewport directive', () => { - var view = createViewWithViewport(); - - var tmplComp = view.rootElementInjectors[0].get(SomeViewport); - expect(tmplComp.viewContainer).not.toBe(null); - }); - - it('dehydration should dehydrate viewcontainers', () => { - var view = createViewWithViewport(); - - var tmplComp = view.rootElementInjectors[0].get(SomeViewport); - expect(tmplComp.viewContainer.hydrated()).toBe(false); - }); - }); - - if (DOM.supportsDOMEvents()) { - describe('event handlers', () => { - var view, ctx, called, receivedEvent, dispatchedEvent; - - function createViewAndContext(protoView) { - view = createView(protoView, - new EventManager([new DomEventsPlugin()], new FakeVmTurnZone())); - ctx = view.context; - called = 0; - receivedEvent = null; - ctx.callMe = (event) => { - called += 1; - receivedEvent = event; - } - } - - function dispatchClick(el) { - dispatchedEvent = DOM.createMouseEvent('click'); - DOM.dispatchEvent(el, dispatchedEvent); - } - - function createProtoView() { - var pv = new ProtoView(null, el('<div class="ng-binding"><div></div></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, [])); - pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); - return pv; - } - - it('should fire on non-bubbling native events', () => { - createViewAndContext(createProtoView()); - - dispatchClick(view.nodes[0]); - - expect(called).toEqual(1); - expect(receivedEvent).toBe(dispatchedEvent); - }); - - it('should not fire on a bubbled native events', () => { - createViewAndContext(createProtoView()); - - dispatchClick(view.nodes[0].firstChild); - - // This test passes trivially on webkit browsers due to - // https://bugs.webkit.org/show_bug.cgi?id=122755 - expect(called).toEqual(0); - }); - - it('should not throw if the view is dehydrated', () => { - createViewAndContext(createProtoView()); - - view.dehydrate(); - expect(() => dispatchClick(view.nodes[0])).not.toThrow(); - expect(called).toEqual(0); - }); - - it('should support custom event emitters', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"><div></div></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, [EventEmitterDirective])); - pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); - - createViewAndContext(pv); - var dir = view.elementInjectors[0].get(EventEmitterDirective); - - var dispatchedEvent = new Object(); - - dir.click(dispatchedEvent); - expect(receivedEvent).toBe(dispatchedEvent); - expect(called).toEqual(1); - - // Should not eval the binding, because custom emitter takes over. - dispatchClick(view.nodes[0]); - - expect(called).toEqual(1); - }); - - it('should bind to directive events', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirectiveWithEventHandler])); - pv.bindEvent('click', parser.parseAction('onEvent($event)', null), 0); - view = createView(pv, new EventManager([new DomEventsPlugin()], new FakeVmTurnZone())); - - var directive = view.elementInjectors[0].get(SomeDirectiveWithEventHandler); - expect(directive.event).toEqual(null); - - dispatchClick(view.nodes[0]); - expect(directive.event).toBe(dispatchedEvent); - }); - }); - } - - describe('react to record changes', () => { - var view, cd, ctx; - - function createViewAndChangeDetector(protoView) { - view = createView(protoView); - ctx = view.context; - cd = view.changeDetector; - } - - it('should consume text node changes', () => { - var pv = new ProtoView(null, el('<div class="ng-binding">{{}}</div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindTextNode(0, parser.parseBinding('foo', null)); - createViewAndChangeDetector(pv); - - ctx.foo = 'buz'; - cd.detectChanges(); - expect(view.textNodes[0].nodeValue).toEqual('buz'); - }); - - it('should consume element binding changes', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - pv.bindElement(null, 0, null); - pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id')); - createViewAndChangeDetector(pv); - - ctx.foo = 'buz'; - cd.detectChanges(); - expect(view.bindElements[0].id).toEqual('buz'); - }); - - it('should consume directive watch expression change', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - 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')); - createViewAndChangeDetector(pv); - - ctx.foo = 'buz'; - cd.detectChanges(); - expect(view.elementInjectors[0].get(SomeDirective).prop).toEqual('buz'); - }); - - it('should notify a directive about changes after all its properties have been set', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - - pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ - DirectiveBinding.createFromType(DirectiveImplementingOnChange, - new DummyDirective({lifecycle: [onChange]})) - ])); - pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a')); - pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b')); - createViewAndChangeDetector(pv); - - ctx.a = 100; - ctx.b = 200; - cd.detectChanges(); - - var directive = view.elementInjectors[0].get(DirectiveImplementingOnChange); - expect(directive.c).toEqual(300); - }); - - it('should provide a map of updated properties using onChange callback', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - - pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ - DirectiveBinding.createFromType(DirectiveImplementingOnChange, - new DummyDirective({lifecycle: [onChange]})) - ])); - pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a')); - pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b')); - createViewAndChangeDetector(pv); - - var directive = view.elementInjectors[0].get(DirectiveImplementingOnChange); - - ctx.a = 0; - ctx.b = 0; - cd.detectChanges(); - - expect(directive.changes["a"].currentValue).toEqual(0); - expect(directive.changes["b"].currentValue).toEqual(0); - - ctx.a = 100; - cd.detectChanges(); - expect(directive.changes["a"].currentValue).toEqual(100); - expect(StringMapWrapper.contains(directive.changes, "b")).toBe(false); - }); - - it('should invoke the onAllChangesDone callback', () => { - var pv = new ProtoView(null, el('<div class="ng-binding"></div>'), - new DynamicProtoChangeDetector(null, null), null); - - pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [ - DirectiveBinding.createFromType(DirectiveImplementingOnAllChangesDone, - new DummyDirective({lifecycle: [onAllChangesDone]})) - ])); - - createViewAndChangeDetector(pv); - cd.detectChanges(); - - var directive = view.elementInjectors[0].get(DirectiveImplementingOnAllChangesDone); - expect(directive.onAllChangesDoneCalled).toBe(true); - }); - }); - }); - - }); -} - -function readDirectiveBinding(type) { - var meta = new DirectiveMetadataReader().read(type); - return DirectiveBinding.createFromType(type, meta.annotation); -} - -class SomeDirective { - prop; - constructor() { - this.prop = 'foo'; - } -} - -class DirectiveImplementingOnChange { - a; - b; - c; - changes; - - onChange(changes) { - this.c = this.a + this.b; - this.changes = changes; - } -} - -class DirectiveImplementingOnAllChangesDone { - onAllChangesDoneCalled; - - onAllChangesDone() { - this.onAllChangesDoneCalled = true; - } -} - -class SomeService {} - -@Component({services: [SomeService]}) -class SomeComponent { - service: SomeService; - constructor(service: SomeService) { - this.service = service; - } -} - -@Decorator({ - selector: '[dec]' -}) -class ServiceDependentDecorator { - component: SomeComponent; - service: SomeService; - constructor(component: SomeComponent, service: SomeService) { - this.component = component; - this.service = service; - } -} - -@Viewport({ - selector: 'someTmpl' -}) -class SomeViewport { - viewContainer: ViewContainer; - constructor(viewContainer: ViewContainer) { - this.viewContainer = viewContainer; - } -} - -class AnotherDirective { - prop:string; - constructor() { - this.prop = 'anotherFoo'; - } -} - -class EventEmitterDirective { - _clicker:Function; - constructor(@EventEmitter('click') clicker:Function) { - this._clicker = clicker; - } - click(eventData) { - this._clicker(eventData); - } -} - -class SomeDirectiveWithEventHandler { - event; - - constructor() { - this.event = null; - } - - onEvent(event) { - this.event = event; - } -} - -class MyEvaluationContext { - foo:string; - a; - b; - callMe; - constructor() { - this.foo = 'bar'; - }; -} - -class TestProtoElementInjector extends ProtoElementInjector { - parentElementInjector: ElementInjector; - hostElementInjector: ElementInjector; - - constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) { - super(parent, index, bindings, firstBindingIsComponent); - } - - instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector { - this.parentElementInjector = parent; - this.hostElementInjector = host; - return super.instantiate(parent, host); - } -} - -class FakeVmTurnZone extends VmTurnZone { - constructor() { - super({enableLongStackTrace: false}); - } - - run(fn) { - fn(); - } - - runOutsideAngular(fn) { - fn(); - } -} diff --git a/modules/angular2/test/directives/class_spec.js b/modules/angular2/test/directives/class_spec.js index f05988a525..26e1b4ccbb 100644 --- a/modules/angular2/test/directives/class_spec.js +++ b/modules/angular2/test/directives/class_spec.js @@ -31,7 +31,7 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo'); async.done(); }); @@ -43,11 +43,11 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo'); view.context.condition = false; view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding bar'); + expect(view.rootNodes[0].className).toEqual('ng-binding bar'); async.done(); }); @@ -59,19 +59,19 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo'); StringMapWrapper.set(view.context.expr, 'bar', true); view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo bar'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo bar'); StringMapWrapper.set(view.context.expr, 'baz', true); view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo bar baz'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo bar baz'); StringMapWrapper.delete(view.context.expr, 'bar'); view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo baz'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo baz'); async.done(); }); @@ -83,15 +83,15 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo'); view.context.expr = null; view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding foo'); + expect(view.rootNodes[0].className).toEqual('ng-binding foo'); view.context.expr = {'foo': false, 'bar': true}; view.detectChanges(); - expect(view.nodes[0].className).toEqual('ng-binding bar'); + expect(view.rootNodes[0].className).toEqual('ng-binding bar'); async.done(); }); @@ -104,11 +104,11 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { StringMapWrapper.set(view.context.expr, 'bar', true); view.detectChanges(); - expect(view.nodes[0].className).toEqual('init foo ng-binding bar'); + expect(view.rootNodes[0].className).toEqual('init foo ng-binding bar'); StringMapWrapper.set(view.context.expr, 'foo', false); view.detectChanges(); - expect(view.nodes[0].className).toEqual('init ng-binding bar'); + expect(view.rootNodes[0].className).toEqual('init ng-binding bar'); async.done(); }); @@ -120,19 +120,19 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(view.nodes[0].className).toEqual('init foo ng-binding baz'); + expect(view.rootNodes[0].className).toEqual('init foo ng-binding baz'); StringMapWrapper.set(view.context.expr, 'bar', true); view.detectChanges(); - expect(view.nodes[0].className).toEqual('init foo ng-binding baz bar'); + expect(view.rootNodes[0].className).toEqual('init foo ng-binding baz bar'); StringMapWrapper.set(view.context.expr, 'foo', false); view.detectChanges(); - expect(view.nodes[0].className).toEqual('init ng-binding baz bar'); + expect(view.rootNodes[0].className).toEqual('init ng-binding baz bar'); view.context.condition = false; view.detectChanges(); - expect(view.nodes[0].className).toEqual('init ng-binding bar'); + expect(view.rootNodes[0].className).toEqual('init ng-binding bar'); async.done(); }); diff --git a/modules/angular2/test/directives/for_spec.js b/modules/angular2/test/directives/for_spec.js index 143ca97d85..cfdb958cd9 100644 --- a/modules/angular2/test/directives/for_spec.js +++ b/modules/angular2/test/directives/for_spec.js @@ -30,7 +30,7 @@ export function main() { it('should reflect initial elements', inject([TestBed, AsyncTestCompleter], (tb, async) => { tb.createView(TestComponent, {html: TEMPLATE}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;'); async.done(); }); })); @@ -42,7 +42,7 @@ export function main() { ListWrapper.push(view.context.items, 3); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;3;'); async.done(); }); })); @@ -54,7 +54,7 @@ export function main() { ListWrapper.removeAt(view.context.items, 1); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;'); async.done(); }); })); @@ -67,7 +67,7 @@ export function main() { ListWrapper.push(view.context.items, 1); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('2;1;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('2;1;'); async.done(); }); })); @@ -81,7 +81,7 @@ export function main() { view.context.items = [6, 2, 7, 0, 4, 8]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('6;2;7;0;4;8;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('6;2;7;0;4;8;'); async.done(); }); })); @@ -94,20 +94,20 @@ export function main() { // INIT view.context.items = [{'name': 'misko'}, {'name':'shyam'}]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('misko;shyam;'); // GROW ListWrapper.push(view.context.items, {'name': 'adam'}); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;adam;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('misko;shyam;adam;'); // SHRINK ListWrapper.removeAt(view.context.items, 2); ListWrapper.removeAt(view.context.items, 0); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('shyam;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('shyam;'); async.done(); }); })); @@ -116,7 +116,7 @@ export function main() { var template = '<ul><li template="for #item of null">{{item}};</li></ul>'; tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); async.done(); }); })); @@ -125,15 +125,15 @@ export function main() { inject([TestBed, AsyncTestCompleter], (tb, async) => { tb.createView(TestComponent, {html: TEMPLATE}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;'); view.context.items = null; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); view.context.items = [1, 2, 3]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;3;'); async.done(); }); })); @@ -141,7 +141,7 @@ export function main() { it('should throw on ref changing to string', inject([TestBed, AsyncTestCompleter], (tb, async) => { tb.createView(TestComponent, {html: TEMPLATE}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;'); view.context.items = 'whaaa'; expect(() => view.detectChanges()).toThrowError(); @@ -154,7 +154,7 @@ export function main() { var a = new Foo(); view.context.items = [a, a]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('foo;foo;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('foo;foo;'); async.done(); }); })); @@ -174,11 +174,11 @@ export function main() { view.detectChanges(); view.detectChanges(); view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;|c-1;|'); + expect(DOM.getText(view.rootNodes[0])).toEqual('a-2;b-2;|c-1;|'); view.context.items = [['e'], ['f', 'g']]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('e-1;|f-2;g-2;|'); + expect(DOM.getText(view.rootNodes[0])).toEqual('e-1;|f-2;g-2;|'); async.done(); }); @@ -195,11 +195,11 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.context.items = [['a', 'b'], ['c']]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;c-1;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('a-2;b-2;c-1;'); view.context.items = [['e'], ['f', 'g']]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('e-1;f-2;g-2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('e-1;f-2;g-2;'); async.done(); }); })); @@ -212,11 +212,11 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.context.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('0123456789'); + expect(DOM.getText(view.rootNodes[0])).toEqual('0123456789'); view.context.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0]; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('0123456789'); + expect(DOM.getText(view.rootNodes[0])).toEqual('0123456789'); async.done(); }); })); diff --git a/modules/angular2/test/directives/if_spec.js b/modules/angular2/test/directives/if_spec.js index 60d7dbdd39..059e858db7 100644 --- a/modules/angular2/test/directives/if_spec.js +++ b/modules/angular2/test/directives/if_spec.js @@ -28,8 +28,8 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); async.done(); }); })); @@ -39,8 +39,8 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello2'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello2'); async.done(); }); })); @@ -51,18 +51,18 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.context.booleanCondition = false; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); view.context.booleanCondition = true; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); view.context.booleanCondition = false; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); async.done(); }); @@ -74,28 +74,28 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.context.booleanCondition = false; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); view.context.booleanCondition = true; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); view.context.nestedBooleanCondition = false; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); view.context.nestedBooleanCondition = true; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); view.context.booleanCondition = false; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); async.done(); }); @@ -111,19 +111,19 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(3); - expect(DOM.getText(view.nodes[0])).toEqual('helloNumberhelloStringhelloFunction'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(3); + expect(DOM.getText(view.rootNodes[0])).toEqual('helloNumberhelloStringhelloFunction'); view.context.numberCondition = 0; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('helloString'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('helloString'); view.context.numberCondition = 1; view.context.stringCondition = "bar"; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('helloNumber'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('helloNumber'); async.done(); }); })); @@ -136,13 +136,13 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); view.context.numberCondition = 2; view.detectChanges(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1); - expect(DOM.getText(view.nodes[0])).toEqual('hello'); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1); + expect(DOM.getText(view.rootNodes[0])).toEqual('hello'); async.done(); }); @@ -154,11 +154,11 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { view.detectChanges(); - DOM.addClass(view.nodes[0].childNodes[1], "foo"); + DOM.addClass(view.rootNodes[0].childNodes[1], "foo"); view.context.numberCondition = 2; view.detectChanges(); - expect(DOM.hasClass(view.nodes[0].childNodes[1], "foo")).toBe(true); + expect(DOM.hasClass(view.rootNodes[0].childNodes[1], "foo")).toBe(true); async.done(); }); @@ -172,8 +172,8 @@ export function main() { tb.createView(TestComponent, {html: html}).then((view) => { expect(() => view.detectChanges()).toThrowError(); - expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); async.done(); }); })); diff --git a/modules/angular2/test/directives/non_bindable_spec.js b/modules/angular2/test/directives/non_bindable_spec.js index 5974725deb..6842301fdc 100644 --- a/modules/angular2/test/directives/non_bindable_spec.js +++ b/modules/angular2/test/directives/non_bindable_spec.js @@ -15,7 +15,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {Decorator, Component} from 'angular2/src/core/annotations/annotations'; import {Template} from 'angular2/src/core/annotations/template'; -import {NgElement} from 'angular2/src/core/dom/element'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; import {NonBindable} from 'angular2/src/directives/non_bindable'; @@ -27,7 +27,7 @@ export function main() { var template = '<div>{{text}}<span non-bindable>{{text}}</span></div>'; tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('foo{{text}}'); + expect(DOM.getText(view.rootNodes[0])).toEqual('foo{{text}}'); async.done(); }); })); @@ -36,7 +36,7 @@ export function main() { var template = '<div non-bindable><span id=child test-dec>{{text}}</span></div>'; tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - var span = DOM.querySelector(view.nodes[0], '#child'); + var span = DOM.querySelector(view.rootNodes[0], '#child'); expect(DOM.hasClass(span, 'compiled')).toBeFalsy(); async.done(); }); @@ -46,7 +46,7 @@ export function main() { var template = '<div><span id=child non-bindable test-dec>{{text}}</span></div>'; tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - var span = DOM.querySelector(view.nodes[0], '#child'); + var span = DOM.querySelector(view.rootNodes[0], '#child'); expect(DOM.hasClass(span, 'compiled')).toBeTruthy(); async.done(); }); diff --git a/modules/angular2/test/directives/switch_spec.js b/modules/angular2/test/directives/switch_spec.js index be45000238..1bbf4630bd 100644 --- a/modules/angular2/test/directives/switch_spec.js +++ b/modules/angular2/test/directives/switch_spec.js @@ -31,15 +31,15 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual(''); + expect(DOM.getText(view.rootNodes[0])).toEqual(''); view.context.switchValue = 'a'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when a'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when a'); view.context.switchValue = 'b'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when b'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when b'); async.done(); }); @@ -55,15 +55,15 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when default'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when default'); view.context.switchValue = 'a'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when a'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when a'); view.context.switchValue = 'b'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when default'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when default'); async.done(); }); @@ -83,15 +83,15 @@ export function main() { tb.createView(TestComponent, {html: template}).then((view) => { view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when default1;when default2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when default1;when default2;'); view.context.switchValue = 'a'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when a1;when a2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when a1;when a2;'); view.context.switchValue = 'b'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when b1;when b2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when b1;when b2;'); async.done(); }); @@ -113,23 +113,23 @@ export function main() { view.context.when2 = 'b'; view.context.switchValue = 'a'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when 1;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when 1;'); view.context.switchValue = 'b'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when 2;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when 2;'); view.context.switchValue = 'c'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when default;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when default;'); view.context.when1 = 'c'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when 1;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when 1;'); view.context.when1 = 'd'; view.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('when default;'); + expect(DOM.getText(view.rootNodes[0])).toEqual('when default;'); async.done(); }); diff --git a/modules/angular2/test/forms/integration_spec.js b/modules/angular2/test/forms/integration_spec.js index 720fd4e5c0..0755498eea 100644 --- a/modules/angular2/test/forms/integration_spec.js +++ b/modules/angular2/test/forms/integration_spec.js @@ -25,8 +25,8 @@ import {ControlGroupDirective, ControlDirective, Control, ControlGroup, Optional DefaultValueAccessor, Validators} from 'angular2/forms'; export function main() { - if (DOM.supportsDOMEvents()) { - describe("integration tests", () => { + describe("integration tests", () => { + if (DOM.supportsDOMEvents()) { it("should initialize DOM elements with the given form object", inject([TestBed, AsyncTestCompleter], (tb, async) => { var ctx = new MyComp(new ControlGroup({ @@ -362,8 +362,8 @@ export function main() { }); })); }); - }); - } + } + }); } @Component({selector: "my-comp"}) diff --git a/modules/angular2/test/render/dom/integration_testbed.js b/modules/angular2/test/render/dom/integration_testbed.js index eb34d2e042..d1d5565b09 100644 --- a/modules/angular2/test/render/dom/integration_testbed.js +++ b/modules/angular2/test/render/dom/integration_testbed.js @@ -1,5 +1,5 @@ import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang'; -import {MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection'; +import {MapWrapper, ListWrapper, List, Map} from 'angular2/src/facade/collection'; import {PromiseWrapper, Promise} from 'angular2/src/facade/async'; import {DOM} from 'angular2/src/dom/dom_adapter'; @@ -177,7 +177,7 @@ export class LoggingEventDispatcher extends EventDispatcher { this.log = []; } dispatchEvent( - elementIndex:number, eventName:string, locals:List<any> + elementIndex:number, eventName:string, locals:Map<string, any> ) { ListWrapper.push(this.log, [elementIndex, eventName, locals]); } 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 fe57ce4e3a..3dea0d8870 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 @@ -279,7 +279,7 @@ export function main() { // var temp = `<simple>aaa<input type="text" id="focused-input" ng-class="{'aClass' : showClass}"> bbb</simple>`; // // compile(temp, (view, lc) => { - // var input = view.nodes[1]; + // var input = view.rootNodes[1]; // input.focus(); // // expect(document.activeElement.id).toEqual("focused-input"); diff --git a/modules/angular2/test/services/ruler_spec.js b/modules/angular2/test/services/ruler_spec.js index ffb62efb67..c4c44cede4 100644 --- a/modules/angular2/test/services/ruler_spec.js +++ b/modules/angular2/test/services/ruler_spec.js @@ -1,7 +1,7 @@ import {AsyncTestCompleter, inject, ddescribe, describe, it, iit, xit, expect, SpyObject} from 'angular2/test_lib'; import {DOM, DomAdapter} from 'angular2/src/dom/dom_adapter'; -import {NgElement} from 'angular2/src/core/dom/element'; +import {NgElement} from 'angular2/src/core/compiler/ng_element'; import {Ruler, Rectangle} from 'angular2/src/services/ruler'; import {createRectangle} from './rectangle_mock'; @@ -35,7 +35,7 @@ export function main() { inject([AsyncTestCompleter], (async) => { var ruler = new Ruler(new DomAdapterMock(createRectangle(10, 20, 200, 100))); - ruler.measure(new NgElement(null)).then((rect) => { + ruler.measure(new FakeNgElement(null)).then((rect) => { assertDimensions(rect, 10, 210, 20, 120, 200, 100); async.done(); }); @@ -46,7 +46,7 @@ export function main() { inject([AsyncTestCompleter], (async) => { var ruler = new Ruler(DOM); - ruler.measure(new NgElement(DOM.createElement('div'))).then((rect) => { + ruler.measure(new FakeNgElement(DOM.createElement('div'))).then((rect) => { //here we are using an element created in a doc fragment so all the measures will come back as 0 assertDimensions(rect, 0, 0, 0, 0, 0, 0); async.done(); @@ -55,3 +55,16 @@ export function main() { }); } + +class FakeNgElement extends NgElement { + _domElement; + + constructor(domElement) { + super(null, null); + this._domElement = domElement; + } + + get domElement() { + return this._domElement; + } +} \ No newline at end of file diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.js b/modules/benchmarks/src/change_detection/change_detection_benchmark.js index 48696d81bd..ad37f23afb 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.js +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.js @@ -185,11 +185,11 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations, objec var dispatcher = new DummyDispatcher(); var parser = new Parser(new Lexer()); - var parentProto = changeDetection.createProtoChangeDetector('parent'); + var parentProto = changeDetection.createProtoChangeDetector('parent', null); var parentCd = parentProto.instantiate(dispatcher, [], [], []); var targetObj = new Obj(); - var proto = changeDetection.createProtoChangeDetector("proto"); + var proto = changeDetection.createProtoChangeDetector("proto", null); var bindingRecords = [ new BindingRecord(parser.parseBinding('field0', null), new FakeBindingMemento(targetObj, reflector.setter("field0")), null), new BindingRecord(parser.parseBinding('field1', null), new FakeBindingMemento(targetObj, reflector.setter("field1")), null), @@ -240,7 +240,7 @@ export function main () { // -- BASELINE var baselineHead = setUpBaseline(numberOfDetectors, object); - + runBaselineReads(baselineHead, 1); //warmup bindAction( diff --git a/modules/benchmarks/src/compiler/compiler_benchmark.js b/modules/benchmarks/src/compiler/compiler_benchmark.js index c16b935e2a..2487017d8b 100644 --- a/modules/benchmarks/src/compiler/compiler_benchmark.js +++ b/modules/benchmarks/src/compiler/compiler_benchmark.js @@ -4,7 +4,7 @@ import {isBlank, Type} from 'angular2/src/facade/lang'; import {document} from 'angular2/src/facade/browser'; import {MapWrapper} from 'angular2/src/facade/collection'; import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata'; -import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; +import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; import {Parser, Lexer, ProtoRecordRange, dynamicChangeDetection} from 'angular2/change_detection'; @@ -23,6 +23,11 @@ import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mappe import {reflector} from 'angular2/src/reflection/reflection'; import {getIntParameter, bindAction} from 'angular2/src/test_lib/benchmark_util'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; + function setupReflector() { reflector.registerType(BenchmarkComponent, { "factory": () => new BenchmarkComponent(), @@ -94,16 +99,22 @@ export function main() { var templateResolver = new FakeTemplateResolver(); var urlResolver = new UrlResolver(); var styleUrlResolver = new StyleUrlResolver(urlResolver); + var shadowDomStrategy = new NativeShadowDomStrategy(styleUrlResolver); + var renderer = new DirectDomRenderer( + new rc.DefaultCompiler( + new Parser(new Lexer()), shadowDomStrategy, new TemplateLoader(null, urlResolver) + ), + null, + shadowDomStrategy + ); var compiler = new Compiler( - dynamicChangeDetection, - new TemplateLoader(null, urlResolver), reader, - new Parser(new Lexer()), cache, - new NativeShadowDomStrategy(styleUrlResolver), templateResolver, new ComponentUrlMapper(), - urlResolver + urlResolver, + renderer, + new ProtoViewFactory(dynamicChangeDetection, renderer) ); var templateNoBindings = createTemplateHtml('templateNoBindings', count); var templateWithBindings = createTemplateHtml('templateWithBindings', count); diff --git a/modules/benchmarks/src/element_injector/element_injector_benchmark.js b/modules/benchmarks/src/element_injector/element_injector_benchmark.js index fc68b1d24a..60e2e65bde 100644 --- a/modules/benchmarks/src/element_injector/element_injector_benchmark.js +++ b/modules/benchmarks/src/element_injector/element_injector_benchmark.js @@ -33,19 +33,19 @@ export function main() { var bindings = [A, B, C]; var proto = new ProtoElementInjector(null, 0, bindings); - var elementInjector = proto.instantiate(null, null); + var elementInjector = proto.instantiate(null); function instantiate () { for (var i = 0; i < iterations; ++i) { - var ei = proto.instantiate(null, null); - ei.instantiateDirectives(appInjector, null, null); + var ei = proto.instantiate(null); + ei.instantiateDirectives(appInjector, null, null, null); } } function instantiateDirectives () { for (var i = 0; i < iterations; ++i) { elementInjector.clearDirectives(); - elementInjector.instantiateDirectives(appInjector, null, null); + elementInjector.instantiateDirectives(appInjector, null, null, null); } } diff --git a/modules/benchmarks/src/largetable/largetable_benchmark.js b/modules/benchmarks/src/largetable/largetable_benchmark.js index 44383bca1d..da04ff8165 100644 --- a/modules/benchmarks/src/largetable/largetable_benchmark.js +++ b/modules/benchmarks/src/largetable/largetable_benchmark.js @@ -8,9 +8,9 @@ import {CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; -import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {UrlResolver} from 'angular2/src/services/url_resolver'; import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; @@ -36,6 +36,14 @@ import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import {ListWrapper} from 'angular2/src/facade/collection'; import {Parent} from 'angular2/src/core/annotations/visibility'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import {Inject} from 'angular2/di'; + var BASELINE_LARGETABLE_TEMPLATE; function setupReflector() { @@ -139,8 +147,8 @@ function setupReflector() { }); reflector.registerType(SwitchWhen, { - 'factory': (el, vc, ss) => new SwitchWhen(el, vc, ss), - 'parameters': [[NgElement],[ViewContainer],[Switch, new Parent()]], + 'factory': (vc, ss) => new SwitchWhen(vc, ss), + 'parameters': [[ViewContainer],[Switch, new Parent()]], 'annotations' : [new Viewport({ selector: '[switch-when]', bind: { @@ -158,14 +166,13 @@ function setupReflector() { }); reflector.registerType(Compiler, { - 'factory': (cd, templateLoader, reader, parser, compilerCache, strategy, tplResolver, - cmpUrlMapper, urlResolver) => - new Compiler(cd, templateLoader, reader, parser, compilerCache, strategy, tplResolver, - cmpUrlMapper, urlResolver), - 'parameters': [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], - [Parser], [CompilerCache], [ShadowDomStrategy], [TemplateResolver], - [ComponentUrlMapper], [UrlResolver]], - 'annotations': [] + "factory": (reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory) => + new Compiler(reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory), + "parameters": [[DirectiveMetadataReader], [CompilerCache], [TemplateResolver], [ComponentUrlMapper], + [UrlResolver], [Renderer], [ProtoViewFactory]], + "annotations": [] }); reflector.registerType(CompilerCache, { @@ -240,12 +247,6 @@ function setupReflector() { "annotations": [] }); - reflector.registerType(Content, { - "factory": (lightDom, el) => new Content(lightDom, el), - "parameters": [[DestinationLightDom], [NgElement]], - "annotations" : [new Decorator({selector: '[content]'})] - }); - reflector.registerType(UrlResolver, { "factory": () => new UrlResolver(), "parameters": [], @@ -290,12 +291,58 @@ function setupReflector() { }); reflector.registerType(PrivateComponentLoader, { - "factory": (compiler, strategy, eventMgr, reader) => - new PrivateComponentLoader(compiler, strategy, eventMgr, reader), - "parameters": [[Compiler], [ShadowDomStrategy], [EventManager], [DirectiveMetadataReader]], + "factory": (compiler, reader, viewFactory) => + new PrivateComponentLoader(compiler, reader, viewFactory), + "parameters": [[Compiler], [DirectiveMetadataReader], [ViewFactory]], "annotations": [] }); + reflector.registerType(DirectDomRenderer, { + "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => + new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), + "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rc.DefaultCompiler, { + "factory": (parser, shadowDomStrategy, templateLoader) => + new rc.DefaultCompiler(parser, shadowDomStrategy, templateLoader), + "parameters": [[Parser], [ShadowDomStrategy], [TemplateLoader]], + "annotations": [] + }); + + reflector.registerType(rvf.ViewFactory, { + "factory": (capacity, eventManager, shadowDomStrategy) => + new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy), + "parameters": [[new Inject(rvf.VIEW_POOL_CAPACITY)], [EventManager], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rvf.VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); + + reflector.registerType(ProtoViewFactory, { + "factory": (changeDetection, renderer) => + new ProtoViewFactory(changeDetection, renderer), + "parameters": [[ChangeDetection], [Renderer]], + "annotations": [] + }); + + reflector.registerType(ViewFactory, { + "factory": (capacity) => + new ViewFactory(capacity), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "annotations": [] + }); + + reflector.registerType(VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); reflector.registerGetters({ 'data': (a) => a.data, diff --git a/modules/benchmarks/src/naive_infinite_scroll/index.js b/modules/benchmarks/src/naive_infinite_scroll/index.js index a1e57ae0c3..4d12abbe16 100644 --- a/modules/benchmarks/src/naive_infinite_scroll/index.js +++ b/modules/benchmarks/src/naive_infinite_scroll/index.js @@ -11,9 +11,9 @@ import { import {reflector} from 'angular2/src/reflection/reflection'; import {CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; @@ -36,6 +36,13 @@ import {CompanyNameComponent, OpportunityNameComponent, OfferingNameComponent, from './cells'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import {Inject} from 'angular2/di'; export function main() { setupReflector(); @@ -188,13 +195,12 @@ export function setupReflectorForAngular() { }); reflector.registerType(Compiler, { - "factory": (changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy, - tplResolver, cmpUrlMapper, urlResolver) => - new Compiler(changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy, - tplResolver, cmpUrlMapper, urlResolver), - "parameters": [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], [Parser], - [CompilerCache], [ShadowDomStrategy], [TemplateResolver], [ComponentUrlMapper], - [UrlResolver]], + "factory": (reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory) => + new Compiler(reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory), + "parameters": [[DirectiveMetadataReader], [CompilerCache], [TemplateResolver], [ComponentUrlMapper], + [UrlResolver], [Renderer], [ProtoViewFactory]], "annotations": [] }); @@ -288,12 +294,6 @@ export function setupReflectorForAngular() { "annotations": [] }); - reflector.registerType(Content, { - "factory": (lightDom, el) => new Content(lightDom, el), - "parameters": [[DestinationLightDom], [NgElement]], - "annotations" : [new Decorator({selector: '[content]'})] - }); - reflector.registerType(TestabilityRegistry, { "factory": () => new TestabilityRegistry(), "parameters": [], @@ -320,9 +320,56 @@ export function setupReflectorForAngular() { }); reflector.registerType(PrivateComponentLoader, { - "factory": (compiler, strategy, eventMgr, reader) => - new PrivateComponentLoader(compiler, strategy, eventMgr, reader), - "parameters": [[Compiler], [ShadowDomStrategy], [EventManager], [DirectiveMetadataReader]], + "factory": (compiler, reader, viewFactory) => + new PrivateComponentLoader(compiler, reader, viewFactory), + "parameters": [[Compiler], [DirectiveMetadataReader], [ViewFactory]], + "annotations": [] + }); + + reflector.registerType(DirectDomRenderer, { + "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => + new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), + "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rc.DefaultCompiler, { + "factory": (parser, shadowDomStrategy, templateLoader) => + new rc.DefaultCompiler(parser, shadowDomStrategy, templateLoader), + "parameters": [[Parser], [ShadowDomStrategy], [TemplateLoader]], + "annotations": [] + }); + + reflector.registerType(rvf.ViewFactory, { + "factory": (capacity, eventManager, shadowDomStrategy) => + new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy), + "parameters": [[new Inject(rvf.VIEW_POOL_CAPACITY)], [EventManager], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rvf.VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); + + reflector.registerType(ProtoViewFactory, { + "factory": (changeDetection, renderer) => + new ProtoViewFactory(changeDetection, renderer), + "parameters": [[ChangeDetection], [Renderer]], + "annotations": [] + }); + + reflector.registerType(ViewFactory, { + "factory": (capacity) => + new ViewFactory(capacity), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "annotations": [] + }); + + reflector.registerType(VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], "annotations": [] }); } diff --git a/modules/benchmarks/src/tree/tree_benchmark.js b/modules/benchmarks/src/tree/tree_benchmark.js index 1e2546e28a..4336fd6fd4 100644 --- a/modules/benchmarks/src/tree/tree_benchmark.js +++ b/modules/benchmarks/src/tree/tree_benchmark.js @@ -8,9 +8,9 @@ import {CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; -import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {UrlResolver} from 'angular2/src/services/url_resolver'; import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; @@ -32,6 +32,13 @@ import {If} from 'angular2/directives'; import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import {Inject} from 'angular2/di'; function setupReflector() { // TODO: Put the general calls to reflector.register... in a shared file @@ -74,14 +81,13 @@ function setupReflector() { }); reflector.registerType(Compiler, { - 'factory': (cd, templateLoader, reader, parser, compilerCache, strategy, tplResolver, - cmpUrlMapper, urlResolver) => - new Compiler(cd, templateLoader, reader, parser, compilerCache, strategy, tplResolver, - cmpUrlMapper, urlResolver), - 'parameters': [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], - [Parser], [CompilerCache], [ShadowDomStrategy], [TemplateResolver], - [ComponentUrlMapper], [UrlResolver]], - 'annotations': [] + "factory": (reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory) => + new Compiler(reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory), + "parameters": [[DirectiveMetadataReader], [CompilerCache], [TemplateResolver], [ComponentUrlMapper], + [UrlResolver], [Renderer], [ProtoViewFactory]], + "annotations": [] }); reflector.registerType(CompilerCache, { @@ -156,12 +162,6 @@ function setupReflector() { "annotations": [] }); - reflector.registerType(Content, { - "factory": (lightDom, el) => new Content(lightDom, el), - "parameters": [[DestinationLightDom], [NgElement]], - "annotations" : [new Decorator({selector: '[content]'})] - }); - reflector.registerType(UrlResolver, { "factory": () => new UrlResolver(), "parameters": [], @@ -206,9 +206,56 @@ function setupReflector() { }); reflector.registerType(PrivateComponentLoader, { - "factory": (compiler, strategy, eventMgr, reader) => - new PrivateComponentLoader(compiler, strategy, eventMgr, reader), - "parameters": [[Compiler], [ShadowDomStrategy], [EventManager], [DirectiveMetadataReader]], + "factory": (compiler, reader, viewFactory) => + new PrivateComponentLoader(compiler, reader, viewFactory), + "parameters": [[Compiler], [DirectiveMetadataReader], [ViewFactory]], + "annotations": [] + }); + + reflector.registerType(DirectDomRenderer, { + "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => + new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), + "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rc.DefaultCompiler, { + "factory": (parser, shadowDomStrategy, templateLoader) => + new rc.DefaultCompiler(parser, shadowDomStrategy, templateLoader), + "parameters": [[Parser], [ShadowDomStrategy], [TemplateLoader]], + "annotations": [] + }); + + reflector.registerType(rvf.ViewFactory, { + "factory": (capacity, eventManager, shadowDomStrategy) => + new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy), + "parameters": [[new Inject(rvf.VIEW_POOL_CAPACITY)], [EventManager], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rvf.VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); + + reflector.registerType(ProtoViewFactory, { + "factory": (changeDetection, renderer) => + new ProtoViewFactory(changeDetection, renderer), + "parameters": [[ChangeDetection], [Renderer]], + "annotations": [] + }); + + reflector.registerType(ViewFactory, { + "factory": (capacity) => + new ViewFactory(capacity), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "annotations": [] + }); + + reflector.registerType(VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], "annotations": [] }); diff --git a/modules/examples/src/hello_world/index_static.js b/modules/examples/src/hello_world/index_static.js index e6d043224b..e1dcc52e74 100644 --- a/modules/examples/src/hello_world/index_static.js +++ b/modules/examples/src/hello_world/index_static.js @@ -7,9 +7,9 @@ import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; -import {ShadowDomStrategy, NativeShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; -import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag'; -import {DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; +import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy'; +import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy'; +import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy'; import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {XHR} from 'angular2/src/services/xhr'; @@ -24,6 +24,14 @@ import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/te import {reflector} from 'angular2/src/reflection/reflection'; +import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; +import {Renderer} from 'angular2/src/render/api'; +import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; +import * as rc from 'angular2/src/render/dom/compiler/compiler'; +import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import {Inject} from 'angular2/di'; + function setup() { reflector.registerType(app.HelloCmp, { "factory": (service) => new app.HelloCmp(service), @@ -53,13 +61,12 @@ function setup() { }); reflector.registerType(Compiler, { - "factory": (changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy, - tplResolver, cmpUrlMapper, urlResolver) => - new Compiler(changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy, - tplResolver, cmpUrlMapper, urlResolver), - "parameters": [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], [Parser], - [CompilerCache], [ShadowDomStrategy], [TemplateResolver], [ComponentUrlMapper], - [UrlResolver]], + "factory": (reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory) => + new Compiler(reader, compilerCache, tplResolver, cmpUrlMapper, urlResolver, renderer, + protoViewFactory), + "parameters": [[DirectiveMetadataReader], [CompilerCache], [TemplateResolver], [ComponentUrlMapper], + [UrlResolver], [Renderer], [ProtoViewFactory]], "annotations": [] }); @@ -153,12 +160,6 @@ function setup() { "annotations": [] }); - reflector.registerType(Content, { - "factory": (lightDom, el, selector) => new Content(lightDom, el, selector), - "parameters": [[DestinationLightDom], [NgElement], [String]], - "annotations" : [] - }); - reflector.registerType(StyleInliner, { "factory": (xhr, styleUrlResolver, urlResolver) => new StyleInliner(xhr, styleUrlResolver, urlResolver), @@ -173,9 +174,9 @@ function setup() { }); reflector.registerType(PrivateComponentLoader, { - "factory": (compiler, strategy, eventMgr, reader) => - new PrivateComponentLoader(compiler, strategy, eventMgr, reader), - "parameters": [[Compiler], [ShadowDomStrategy], [EventManager], [DirectiveMetadataReader]], + "factory": (compiler, reader, viewFactory) => + new PrivateComponentLoader(compiler, reader, viewFactory), + "parameters": [[Compiler], [DirectiveMetadataReader], [ViewFactory]], "annotations": [] }); @@ -191,6 +192,53 @@ function setup() { "annotations": [] }); + reflector.registerType(DirectDomRenderer, { + "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => + new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), + "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rc.DefaultCompiler, { + "factory": (parser, shadowDomStrategy, templateLoader) => + new rc.DefaultCompiler(parser, shadowDomStrategy, templateLoader), + "parameters": [[Parser], [ShadowDomStrategy], [TemplateLoader]], + "annotations": [] + }); + + reflector.registerType(rvf.ViewFactory, { + "factory": (capacity, eventManager, shadowDomStrategy) => + new rvf.ViewFactory(capacity, eventManager, shadowDomStrategy), + "parameters": [[new Inject(rvf.VIEW_POOL_CAPACITY)], [EventManager], [ShadowDomStrategy]], + "annotations": [] + }); + + reflector.registerType(rvf.VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); + + reflector.registerType(ProtoViewFactory, { + "factory": (changeDetection, renderer) => + new ProtoViewFactory(changeDetection, renderer), + "parameters": [[ChangeDetection], [Renderer]], + "annotations": [] + }); + + reflector.registerType(ViewFactory, { + "factory": (capacity) => + new ViewFactory(capacity), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "annotations": [] + }); + + reflector.registerType(VIEW_POOL_CAPACITY, { + "factory": () => 100000, + "parameters": [], + "annotations": [] + }); + reflector.registerGetters({ "greeting": (a) => a.greeting }); diff --git a/test-init.dart b/test-init.dart new file mode 100644 index 0000000000..7e1096de8f --- /dev/null +++ b/test-init.dart @@ -0,0 +1,5 @@ +import 'package:angular2/src/dom/browser_adapter.dart'; + +main() { + BrowserDomAdapter.makeCurrent(); +} diff --git a/test-main.dart b/test-main.dart index 82e616960d..0726d474bb 100644 --- a/test-main.dart +++ b/test-main.dart @@ -1,11 +1,9 @@ import 'package:guinness/guinness.dart'; import 'package:unittest/unittest.dart' as unit; -import 'package:angular2/src/dom/browser_adapter.dart'; import 'package:angular2/src/test_lib/test_lib.dart' show testSetup; main() { - BrowserDomAdapter.makeCurrent(); unit.filterStacks = true; unit.formatStacks = false; unit.unittestConfiguration.timeout = new Duration(milliseconds: 100);