refactor(render): use render layer fully
Introduces angular2/src/core/compiler/ViewFactory which extracts ProtoView.instantiate and replaces ViewPool. Note: This is a work in progress commit to unblock other commits. There will be follow ups to add unit tests, remove TODOs, …
This commit is contained in:
38
modules/angular2/src/core/application.js
vendored
38
modules/angular2/src/core/application.js
vendored
@ -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,
|
||||
|
63
modules/angular2/src/core/compiler/compiler.js
vendored
63
modules/angular2/src/core/compiler/compiler.js
vendored
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
34
modules/angular2/src/core/compiler/ng_element.js
vendored
Normal file
34
modules/angular2/src/core/compiler/ng_element.js
vendored
Normal file
@ -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));
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
391
modules/angular2/src/core/compiler/view.js
vendored
391
modules/angular2/src/core/compiler/view.js
vendored
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
119
modules/angular2/src/core/compiler/view_container.js
vendored
119
modules/angular2/src/core/compiler/view_container.js
vendored
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
109
modules/angular2/src/core/compiler/view_factory.js
vendored
Normal file
109
modules/angular2/src/core/compiler/view_factory.js
vendored
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
26
modules/angular2/src/core/compiler/view_pool.js
vendored
26
modules/angular2/src/core/compiler/view_pool.js
vendored
@ -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;
|
||||
}
|
||||
}
|
||||
|
16
modules/angular2/src/core/dom/element.js
vendored
16
modules/angular2/src/core/dom/element.js
vendored
@ -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));
|
||||
}
|
||||
}
|
2
modules/angular2/src/directives/class.js
vendored
2
modules/angular2/src/directives/class.js
vendored
@ -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]',
|
||||
|
3
modules/angular2/src/directives/switch.js
vendored
3
modules/angular2/src/directives/switch.js
vendored
@ -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;
|
||||
|
6
modules/angular2/src/render/api.js
vendored
6
modules/angular2/src/render/api.js
vendored
@ -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 {}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
21
modules/angular2/src/render/dom/view/view.js
vendored
21
modules/angular2/src/render/dom/view/view.js
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
|
2
modules/angular2/src/services/ruler.js
vendored
2
modules/angular2/src/services/ruler.js
vendored
@ -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;
|
||||
|
29
modules/angular2/src/test_lib/test_bed.js
vendored
29
modules/angular2/src/test_lib/test_bed.js
vendored
@ -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 {
|
||||
|
19
modules/angular2/src/test_lib/test_injector.js
vendored
19
modules/angular2/src/test_lib/test_injector.js
vendored
@ -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,
|
||||
|
9
modules/angular2/src/test_lib/utils.js
vendored
9
modules/angular2/src/test_lib/utils.js
vendored
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user