refactor(render): remove recursion from renderer
The goal is to make implementing a renderer straight forward. BREAKING_CHANGE: - Renderer interface was redone / simplified. - `DirectDomRenderer` was replaced by `DomRenderer`. - `DirectDomRenderer.setImperativeComponentRootNodes` is replaced by the following 2 steps: 1. `ViewManager.getComponentView(elementRef) -> ViewRef` 2. `DomRenderer.setComponentViewRootNodes(viewRef, rootNodes)` - all `@View` annotations need to have a template, but the template may be empty. Previously views that had a `renderer` property did not have to have a `template`. - `dynamicComponentLoader.loadIntoNewLocation` does no more allow to pass an element, but requires a css selector. Special syntax: `:document` can be used as prefix to search globally on the document instead of in the provided parent view. Part of #1675
This commit is contained in:
54
modules/angular2/src/core/application.js
vendored
54
modules/angular2/src/core/application.js
vendored
@ -33,17 +33,14 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {Renderer, RenderCompiler} 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 * as rvh from 'angular2/src/render/dom/view/view_hydrator';
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
import {
|
||||
appComponentRefToken,
|
||||
appElementToken,
|
||||
appComponentAnnotatedTypeToken,
|
||||
appDocumentToken,
|
||||
appComponentAnnotatedTypeToken
|
||||
} from './application_tokens';
|
||||
|
||||
var _rootInjector: Injector;
|
||||
@ -56,28 +53,25 @@ var _rootBindings = [
|
||||
|
||||
function _injectorBindings(appComponentType): List<Binding> {
|
||||
return [
|
||||
bind(appDocumentToken).toValue(DOM.defaultDoc()),
|
||||
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
||||
bind(appComponentAnnotatedTypeToken).toFactory((reader) => {
|
||||
// TODO(rado): investigate whether to support bindings on root component.
|
||||
return reader.read(appComponentType);
|
||||
}, [DirectiveMetadataReader]),
|
||||
|
||||
bind(appElementToken).toFactory((appComponentAnnotatedType, appDocument) => {
|
||||
var selector = appComponentAnnotatedType.annotation.selector;
|
||||
var element = DOM.querySelector(appDocument, selector);
|
||||
if (isBlank(element)) {
|
||||
throw new BaseException(`The app selector "${selector}" did not match any elements`);
|
||||
}
|
||||
return element;
|
||||
}, [appComponentAnnotatedTypeToken, appDocumentToken]),
|
||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector, appElement,
|
||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
||||
appComponentAnnotatedType, testability, registry) => {
|
||||
|
||||
// We need to do this here to ensure that we create Testability and
|
||||
// it's ready on the window for users.
|
||||
registry.registerApplication(appElement, testability);
|
||||
return dynamicComponentLoader.loadIntoNewLocation(appComponentAnnotatedType.type, null, appElement, injector);
|
||||
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
var selector = appComponentAnnotatedType.annotation.selector;
|
||||
return dynamicComponentLoader.loadIntoNewLocation(appComponentAnnotatedType.type, null, selector, injector).then( (componentRef) => {
|
||||
var domView = resolveInternalDomView(componentRef.hostView.render);
|
||||
// We need to do this here to ensure that we create Testability and
|
||||
// it's ready on the window for users.
|
||||
registry.registerApplication(domView.boundElements[0], testability);
|
||||
|
||||
return componentRef;
|
||||
});
|
||||
}, [DynamicComponentLoader, Injector, appComponentAnnotatedTypeToken,
|
||||
Testability, TestabilityRegistry]),
|
||||
|
||||
bind(appComponentType).toFactory((ref) => ref.instance,
|
||||
@ -89,18 +83,16 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
}, [VmTurnZone]),
|
||||
bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||
[StyleUrlResolver, appDocumentToken]),
|
||||
DirectDomRenderer,
|
||||
bind(Renderer).toClass(DirectDomRenderer),
|
||||
bind(RenderCompiler).toClass(rc.DefaultDomCompiler),
|
||||
[StyleUrlResolver, DOCUMENT_TOKEN]),
|
||||
// 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(DomRenderer).toFactory(
|
||||
(eventManager, shadowDomStrategy, doc) => new DomRenderer(eventManager, shadowDomStrategy, doc),
|
||||
[EventManager, ShadowDomStrategy, DOCUMENT_TOKEN]
|
||||
),
|
||||
bind(rvf.VIEW_POOL_CAPACITY).toValue(10000),
|
||||
rvh.RenderViewHydrator,
|
||||
DefaultDomCompiler,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
ProtoViewFactory,
|
||||
// TODO(tbosch): We need an explicit factory here, as
|
||||
// we are getting errors in dart2js with mirrors...
|
||||
|
@ -1,6 +1,4 @@
|
||||
import {OpaqueToken} from 'angular2/di';
|
||||
|
||||
export var appComponentRefToken:OpaqueToken = new OpaqueToken('ComponentRef');
|
||||
export var appElementToken:OpaqueToken = new OpaqueToken('AppElement');
|
||||
export var appComponentAnnotatedTypeToken:OpaqueToken = new OpaqueToken('AppComponentAnnotatedType');
|
||||
export var appDocumentToken:OpaqueToken = new OpaqueToken('AppDocument');
|
||||
|
31
modules/angular2/src/core/compiler/compiler.js
vendored
31
modules/angular2/src/core/compiler/compiler.js
vendored
@ -133,21 +133,14 @@ export class Compiler {
|
||||
if (isBlank(template)) {
|
||||
return null;
|
||||
}
|
||||
if (isPresent(template.renderer)) {
|
||||
var directives = [];
|
||||
pvPromise = this._render.createImperativeComponentProtoView(template.renderer).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
|
||||
});
|
||||
} else {
|
||||
var directives = ListWrapper.map(
|
||||
this._flattenDirectives(template),
|
||||
(directive) => this._bindDirective(directive)
|
||||
);
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._render.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
|
||||
});
|
||||
}
|
||||
var directives = ListWrapper.map(
|
||||
this._flattenDirectives(template),
|
||||
(directive) => this._bindDirective(directive)
|
||||
);
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._render.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
|
||||
});
|
||||
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
return pvPromise;
|
||||
@ -187,14 +180,6 @@ export class Compiler {
|
||||
});
|
||||
|
||||
var protoViewDone = (_) => {
|
||||
var childComponentRenderPvRefs = [];
|
||||
ListWrapper.forEach(protoView.elementBinders, (eb) => {
|
||||
if (isPresent(eb.componentDirective)) {
|
||||
var componentPv = eb.nestedProtoView;
|
||||
ListWrapper.push(childComponentRenderPvRefs, isPresent(componentPv) ? componentPv.render : null);
|
||||
}
|
||||
});
|
||||
this._render.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs);
|
||||
return protoView;
|
||||
};
|
||||
if (nestedPVPromises.length > 0) {
|
||||
|
@ -62,14 +62,14 @@ export class DynamicComponentLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
||||
* Loads a component in the element specified by elementSelector. The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef, elementOrSelector:any,
|
||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef, elementSelector:string,
|
||||
injector:Injector = null):Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
||||
var hostViewRef = this._viewManager.createInPlaceHostView(
|
||||
parentComponentLocation, elementOrSelector, hostProtoViewRef, injector);
|
||||
parentComponentLocation, elementSelector, hostProtoViewRef, injector);
|
||||
var newLocation = new ElementRef(hostViewRef, 0);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
|
@ -894,7 +894,7 @@ export class ElementInjector extends TreeNode {
|
||||
|
||||
_getPreBuiltObjectByKeyId(keyId:int) {
|
||||
var staticKeys = StaticKeys.instance();
|
||||
if (keyId === staticKeys.viewManagerId) return this._preBuiltObjects.viewManagerId;
|
||||
if (keyId === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager;
|
||||
|
||||
//TODO add other objects as needed
|
||||
return _undefined;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {ViewRef} from './view_ref';
|
||||
import {DirectDomViewRef} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/view
|
||||
@ -23,8 +23,7 @@ export class ElementRef {
|
||||
// We need a more general way to read/write to the DOM element
|
||||
// via a proper abstraction in the render layer
|
||||
get domElement() {
|
||||
var renderViewRef:DirectDomViewRef = this.parentView.render;
|
||||
return renderViewRef.delegate.boundElements[this.boundElementIndex];
|
||||
return resolveInternalDomView(this.parentView.render).boundElements[this.boundElementIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
|
4
modules/angular2/src/core/compiler/view.js
vendored
4
modules/angular2/src/core/compiler/view.js
vendored
@ -32,7 +32,7 @@ export class AppView {
|
||||
componentChildViews: List<AppView>;
|
||||
/// Host views that were added by an imperative view.
|
||||
/// This is a dynamically growing / shrinking array.
|
||||
imperativeHostViews: List<AppView>;
|
||||
inPlaceHostViews: List<AppView>;
|
||||
viewContainers: List<AppViewContainer>;
|
||||
preBuiltObjects: List<PreBuiltObjects>;
|
||||
proto: AppProtoView;
|
||||
@ -64,7 +64,7 @@ export class AppView {
|
||||
this.context = null;
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||
this.renderer = renderer;
|
||||
this.imperativeHostViews = [];
|
||||
this.inPlaceHostViews = [];
|
||||
}
|
||||
|
||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
||||
|
157
modules/angular2/src/core/compiler/view_manager.js
vendored
157
modules/angular2/src/core/compiler/view_manager.js
vendored
@ -1,12 +1,11 @@
|
||||
import {Injector, Binding} from 'angular2/di';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import * as viewModule from './view';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ProtoViewRef, ViewRef, internalView, internalProtoView} from './view_ref';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {Renderer, RenderViewRef, RenderViewContainerRef} from 'angular2/src/render/api';
|
||||
import {Renderer, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {AppViewManagerUtils} from './view_manager_utils';
|
||||
import {AppViewPool} from './view_pool';
|
||||
|
||||
@ -27,6 +26,12 @@ export class AppViewManager {
|
||||
this._utils = utils;
|
||||
}
|
||||
|
||||
getComponentView(hostLocation:ElementRef):ViewRef {
|
||||
var hostView = internalView(hostLocation.parentView);
|
||||
var boundElementIndex = hostLocation.boundElementIndex;
|
||||
return new ViewRef(hostView.componentChildViews[boundElementIndex]);
|
||||
}
|
||||
|
||||
getViewContainer(location:ElementRef):ViewContainerRef {
|
||||
var hostView = internalView(location.parentView);
|
||||
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
|
||||
@ -47,20 +52,18 @@ export class AppViewManager {
|
||||
if (!binder.hasDynamicComponent()) {
|
||||
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`)
|
||||
}
|
||||
|
||||
var componentView = this._createViewRecurse(componentProtoView);
|
||||
var renderViewRefs = this._renderer.createDynamicComponentView(hostView.render, boundElementIndex, componentProtoView.render);
|
||||
componentView.render = renderViewRefs[0];
|
||||
var componentView = this._createPooledView(componentProtoView);
|
||||
this._renderer.attachComponentView(hostView.render, boundElementIndex, componentView.render);
|
||||
this._utils.attachComponentView(hostView, boundElementIndex, componentView);
|
||||
this._utils.hydrateDynamicComponentInElementInjector(hostView, boundElementIndex, componentBinding, injector);
|
||||
this._utils.hydrateComponentView(hostView, boundElementIndex);
|
||||
this._viewHydrateRecurse(componentView, renderViewRefs, 1);
|
||||
this._viewHydrateRecurse(componentView);
|
||||
|
||||
return new ViewRef(componentView);
|
||||
}
|
||||
|
||||
createInPlaceHostView(parentComponentLocation:ElementRef,
|
||||
hostElementSelector, hostProtoViewRef:ProtoViewRef, injector:Injector):ViewRef {
|
||||
hostElementSelector:string, hostProtoViewRef:ProtoViewRef, injector:Injector):ViewRef {
|
||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||
var parentComponentHostView = null;
|
||||
var parentComponentBoundElementIndex = null;
|
||||
@ -70,27 +73,22 @@ export class AppViewManager {
|
||||
parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
||||
parentRenderViewRef = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex].render;
|
||||
}
|
||||
var hostView = this._createViewRecurse(hostProtoView);
|
||||
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostProtoView.render);
|
||||
hostView.render = renderViewRefs[0];
|
||||
var hostRenderView = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostProtoView.render);
|
||||
var hostView = this._utils.createView(hostProtoView, hostRenderView, this, this._renderer);
|
||||
this._renderer.setEventDispatcher(hostView.render, hostView);
|
||||
this._createViewRecurse(hostView)
|
||||
this._utils.attachAndHydrateInPlaceHostView(parentComponentHostView, parentComponentBoundElementIndex, hostView, injector);
|
||||
this._viewHydrateRecurse(hostView, renderViewRefs, 1);
|
||||
this._viewHydrateRecurse(hostView);
|
||||
return new ViewRef(hostView);
|
||||
}
|
||||
|
||||
destroyInPlaceHostView(parentComponentLocation:ElementRef, hostViewRef:ViewRef) {
|
||||
var hostView = internalView(hostViewRef);
|
||||
var parentView = null;
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
parentView = internalView(parentComponentLocation.parentView).componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
}
|
||||
var hostViewRenderRef = hostView.render;
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._utils.detachInPlaceHostView(parentView, hostView);
|
||||
this._renderer.destroyInPlaceHostView(parentRenderViewRef, hostViewRenderRef);
|
||||
this._destroyView(hostView);
|
||||
this._destroyInPlaceHostView(parentView, hostView);
|
||||
}
|
||||
|
||||
createViewInContainer(viewContainerLocation:ElementRef,
|
||||
@ -99,25 +97,19 @@ export class AppViewManager {
|
||||
var parentView = internalView(viewContainerLocation.parentView);
|
||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||
|
||||
var view = this._createViewRecurse(protoView);
|
||||
var renderViewRefs = this._renderer.createViewInContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex, view.proto.render);
|
||||
view.render = renderViewRefs[0];
|
||||
var view = this._createPooledView(protoView);
|
||||
|
||||
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, atIndex, view);
|
||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, atIndex, injector);
|
||||
this._viewHydrateRecurse(view, renderViewRefs, 1);
|
||||
this._viewHydrateRecurse(view);
|
||||
return new ViewRef(view);
|
||||
}
|
||||
|
||||
destroyViewInContainer(viewContainerLocation:ElementRef, atIndex:number) {
|
||||
var parentView = internalView(viewContainerLocation.parentView);
|
||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
this._viewDehydrateRecurse(view);
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
this._renderer.destroyViewInContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex);
|
||||
this._destroyView(view);
|
||||
this._destroyViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
}
|
||||
|
||||
attachViewInContainer(viewContainerLocation:ElementRef, atIndex:number, viewRef:ViewRef):ViewRef {
|
||||
@ -125,7 +117,7 @@ export class AppViewManager {
|
||||
var parentView = internalView(viewContainerLocation.parentView);
|
||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, atIndex, view);
|
||||
this._renderer.insertViewIntoContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex, view.render);
|
||||
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
||||
return viewRef;
|
||||
}
|
||||
|
||||
@ -135,86 +127,105 @@ export class AppViewManager {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
this._renderer.detachViewFromContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex);
|
||||
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
||||
return new ViewRef(view);
|
||||
}
|
||||
|
||||
_getRenderViewContainerRef(parentView:viewModule.AppView, boundElementIndex:number) {
|
||||
return new RenderViewContainerRef(parentView.render, boundElementIndex);
|
||||
}
|
||||
|
||||
_createViewRecurse(protoView:viewModule.AppProtoView) {
|
||||
_createPooledView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
||||
var view = this._viewPool.getView(protoView);
|
||||
if (isBlank(view)) {
|
||||
view = this._utils.createView(protoView, this, this._renderer);
|
||||
var binders = protoView.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createViewRecurse(binder.nestedProtoView);
|
||||
this._utils.attachComponentView(view, binderIdx, childView);
|
||||
}
|
||||
}
|
||||
view = this._utils.createView(protoView, this._renderer.createView(protoView.render), this, this._renderer);
|
||||
this._renderer.setEventDispatcher(view.render, view);
|
||||
this._createViewRecurse(view);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
_destroyView(view:viewModule.AppView) {
|
||||
_createViewRecurse(view:viewModule.AppView) {
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createPooledView(binder.nestedProtoView);
|
||||
this._renderer.attachComponentView(view.render, binderIdx, childView.render);
|
||||
this._utils.attachComponentView(view, binderIdx, childView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_destroyPooledView(view:viewModule.AppView) {
|
||||
// TODO: if the pool is full, call renderer.destroyView as well!
|
||||
this._viewPool.returnView(view);
|
||||
}
|
||||
|
||||
_destroyViewInContainer(parentView, boundElementIndex, atIndex:number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
this._viewDehydrateRecurse(view, false);
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
||||
this._destroyPooledView(view);
|
||||
}
|
||||
|
||||
_destroyComponentView(hostView, boundElementIndex, componentView) {
|
||||
this._viewDehydrateRecurse(componentView, false);
|
||||
this._renderer.detachComponentView(hostView.render, boundElementIndex, componentView.render);
|
||||
this._utils.detachComponentView(hostView, boundElementIndex);
|
||||
this._destroyPooledView(componentView);
|
||||
}
|
||||
|
||||
_destroyInPlaceHostView(parentView, hostView) {
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentView)) {
|
||||
parentRenderViewRef = parentView.render;
|
||||
}
|
||||
this._viewDehydrateRecurse(hostView, true);
|
||||
this._utils.detachInPlaceHostView(parentView, hostView);
|
||||
this._renderer.destroyInPlaceHostView(parentRenderViewRef, hostView.render);
|
||||
// Note: Don't put the inplace host view into the view pool
|
||||
// as it is depending on the element for which it was created.
|
||||
}
|
||||
|
||||
_viewHydrateRecurse(
|
||||
view:viewModule.AppView,
|
||||
renderComponentViewRefs:List<RenderViewRef>,
|
||||
renderComponentIndex:number):number {
|
||||
this._renderer.setEventDispatcher(view.render, view);
|
||||
view:viewModule.AppView) {
|
||||
this._renderer.hydrateView(view.render);
|
||||
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
if (binders[i].hasStaticComponent()) {
|
||||
var childView = view.componentChildViews[i];
|
||||
childView.render = renderComponentViewRefs[renderComponentIndex++];
|
||||
this._utils.hydrateComponentView(view, i);
|
||||
renderComponentIndex = this._viewHydrateRecurse(
|
||||
view.componentChildViews[i],
|
||||
renderComponentViewRefs,
|
||||
renderComponentIndex
|
||||
this._viewHydrateRecurse(
|
||||
view.componentChildViews[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
return renderComponentIndex;
|
||||
}
|
||||
|
||||
_viewDehydrateRecurse(view:viewModule.AppView) {
|
||||
_viewDehydrateRecurse(view:viewModule.AppView, forceDestroyComponents) {
|
||||
this._utils.dehydrateView(view);
|
||||
this._renderer.dehydrateView(view.render);
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var componentView = view.componentChildViews[i];
|
||||
if (isPresent(componentView)) {
|
||||
this._viewDehydrateRecurse(componentView);
|
||||
if (binders[i].hasDynamicComponent()) {
|
||||
this._utils.detachComponentView(view, i);
|
||||
this._destroyView(componentView);
|
||||
if (binders[i].hasDynamicComponent() || forceDestroyComponents) {
|
||||
this._destroyComponentView(view, i, componentView);
|
||||
} else {
|
||||
this._viewDehydrateRecurse(componentView, false);
|
||||
}
|
||||
}
|
||||
var vc = view.viewContainers[i];
|
||||
if (isPresent(vc)) {
|
||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||
var childView = vc.views[j];
|
||||
this._viewDehydrateRecurse(childView);
|
||||
this._utils.detachViewInContainer(view, i, j);
|
||||
this._destroyView(childView);
|
||||
this._destroyViewInContainer(view, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imperativeHostViews
|
||||
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||
var hostView = view.imperativeHostViews[i];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._utils.detachInPlaceHostView(view, hostView);
|
||||
this._destroyView(hostView);
|
||||
// inPlaceHostViews
|
||||
for (var i = view.inPlaceHostViews.length-1; i>=0; i--) {
|
||||
var hostView = view.inPlaceHostViews[i];
|
||||
this._destroyInPlaceHostView(view, hostView);
|
||||
}
|
||||
view.render = null;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import * as avmModule from './view_manager';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {RenderViewRef} from 'angular2/src/render/api';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
@ -27,8 +28,11 @@ export class AppViewManagerUtils {
|
||||
}
|
||||
}
|
||||
|
||||
createView(protoView:viewModule.AppProtoView, viewManager:avmModule.AppViewManager, renderer:Renderer): viewModule.AppView {
|
||||
createView(protoView:viewModule.AppProtoView, renderView:RenderViewRef, viewManager:avmModule.AppViewManager, renderer:Renderer): viewModule.AppView {
|
||||
var view = new viewModule.AppView(renderer, protoView, protoView.protoLocals);
|
||||
// TODO(tbosch): pass RenderViewRef as argument to AppView!
|
||||
view.render = renderView;
|
||||
|
||||
var changeDetector = protoView.protoChangeDetector.instantiate(view);
|
||||
|
||||
var binders = protoView.elementBinders;
|
||||
@ -96,7 +100,7 @@ export class AppViewManagerUtils {
|
||||
hostElementInjector = parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
||||
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
ListWrapper.push(parentView.inPlaceHostViews, hostView);
|
||||
}
|
||||
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
||||
}
|
||||
@ -105,7 +109,7 @@ export class AppViewManagerUtils {
|
||||
hostView:viewModule.AppView) {
|
||||
if (isPresent(parentView)) {
|
||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
ListWrapper.remove(parentView.inPlaceHostViews, hostView);
|
||||
}
|
||||
}
|
||||
|
||||
|
169
modules/angular2/src/render/api.js
vendored
169
modules/angular2/src/render/api.js
vendored
@ -134,18 +134,11 @@ export class DirectiveMetadata {
|
||||
}
|
||||
|
||||
// An opaque reference to a DomProtoView
|
||||
export class RenderProtoViewRef {}
|
||||
export class RenderProtoViewRef {
|
||||
}
|
||||
|
||||
// An opaque reference to a DomView
|
||||
export class RenderViewRef {}
|
||||
|
||||
export class RenderViewContainerRef {
|
||||
view:RenderViewRef;
|
||||
elementIndex:number;
|
||||
constructor(view:RenderViewRef, elementIndex: number) {
|
||||
this.view = view;
|
||||
this.elementIndex = elementIndex;
|
||||
}
|
||||
export class RenderViewRef {
|
||||
}
|
||||
|
||||
export class ViewDefinition {
|
||||
@ -168,113 +161,101 @@ export class RenderCompiler {
|
||||
*/
|
||||
compileHost(componentId):Promise<ProtoViewDto> { return null; }
|
||||
|
||||
/**
|
||||
* Creats a ProtoViewDto for a component that will use an imperative View using the given
|
||||
* renderer.
|
||||
* Note: Rigth now, the renderer argument is ignored, but will be used in the future to define
|
||||
* a custom handler.
|
||||
*/
|
||||
createImperativeComponentProtoView(rendererId):Promise<ProtoViewDto> { return null; }
|
||||
|
||||
/**
|
||||
* Compiles a single DomProtoView. Non recursive so that
|
||||
* we don't need to serialize all possible components over the wire,
|
||||
* but only the needed ones based on previous calls.
|
||||
*/
|
||||
compile(template:ViewDefinition):Promise<ProtoViewDto> { return null; }
|
||||
|
||||
/**
|
||||
* Sets the preset nested components,
|
||||
* which will be instantiated when this protoView is instantiated.
|
||||
* Note: We can't create new ProtoViewRefs here as we need to support cycles / recursive components.
|
||||
* @param {List<RenderProtoViewRef>} protoViewRefs
|
||||
* DomProtoView for every element with a component in this protoView or in a view container's protoView
|
||||
*/
|
||||
mergeChildComponentProtoViews(protoViewRef:RenderProtoViewRef, componentProtoViewRefs:List<RenderProtoViewRef>) { return null; }
|
||||
}
|
||||
|
||||
export class Renderer {
|
||||
/**
|
||||
* Creates a view and inserts it into a ViewContainer.
|
||||
* @param {RenderViewContainerRef} viewContainerRef
|
||||
* @param {RenderProtoViewRef} protoViewRef A RenderProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE or ProtoViewDto.EMBEDDED_VIEW_TYPE
|
||||
* @param {number} atIndex
|
||||
* @return {List<RenderViewRef>} the view and all of its nested child component views
|
||||
*/
|
||||
createViewInContainer(vcRef:RenderViewContainerRef, atIndex:number, protoViewRef:RenderProtoViewRef):List<RenderViewRef> { return null; }
|
||||
|
||||
/**
|
||||
* Destroys the view in the given ViewContainer
|
||||
*/
|
||||
destroyViewInContainer(vcRef:RenderViewContainerRef, atIndex:number):void {}
|
||||
|
||||
/**
|
||||
* Inserts a detached view into a viewContainer.
|
||||
*/
|
||||
insertViewIntoContainer(vcRef:RenderViewContainerRef, atIndex:number, view:RenderViewRef):void {}
|
||||
|
||||
/**
|
||||
* Detaches a view from a container so that it can be inserted later on
|
||||
*/
|
||||
detachViewFromContainer(vcRef:RenderViewContainerRef, atIndex:number):void {}
|
||||
|
||||
/**
|
||||
* Creates a view and
|
||||
* installs it as a shadow view for an element.
|
||||
*
|
||||
* Note: only allowed if there is a dynamic component directive at this place.
|
||||
* @param {RenderViewRef} hostView
|
||||
* @param {number} elementIndex
|
||||
* @param {RenderProtoViewRef} componentProtoViewRef A RenderProtoViewRef of type ProtoViewDto.COMPONENT_VIEW_TYPE
|
||||
* @return {List<RenderViewRef>} the view and all of its nested child component views
|
||||
*/
|
||||
createDynamicComponentView(hostViewRef:RenderViewRef, elementIndex:number, componentProtoViewRef:RenderProtoViewRef):List<RenderViewRef> { return null; }
|
||||
|
||||
/**
|
||||
* Destroys the component view at the given index
|
||||
*
|
||||
* Note: only allowed if there is a dynamic component directive at this place.
|
||||
*/
|
||||
destroyDynamicComponentView(hostViewRef:RenderViewRef, elementIndex:number):void {}
|
||||
|
||||
/**
|
||||
* Creates a host view that includes the given element.
|
||||
* @param {RenderViewRef} parentViewRef (might be null)
|
||||
* @param {any} hostElementSelector element or css selector for the host element
|
||||
* @param {RenderProtoViewRef} hostProtoView a RenderProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE
|
||||
* @return {List<RenderViewRef>} the view and all of its nested child component views
|
||||
* @param {RenderViewRef} parentHostViewRef (might be null)
|
||||
* @param {any} hostElementSelector css selector for the host element
|
||||
* @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE
|
||||
* @return {RenderViewRef} the created view
|
||||
*/
|
||||
createInPlaceHostView(parentViewRef:RenderViewRef, hostElementSelector, hostProtoViewRef:RenderProtoViewRef):List<RenderViewRef> { return null; }
|
||||
createInPlaceHostView(parentHostViewRef:RenderViewRef, hostElementSelector:string, hostProtoViewRef:RenderProtoViewRef):RenderViewRef {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the given host view in the given parent view.
|
||||
*/
|
||||
destroyInPlaceHostView(parentViewRef:RenderViewRef, hostViewRef:RenderViewRef):void {}
|
||||
destroyInPlaceHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property on an element.
|
||||
* Creates a regular view out of the given ProtoView
|
||||
*/
|
||||
createView(protoViewRef:RenderProtoViewRef):RenderViewRef {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the given view after it has been dehydrated and detached
|
||||
*/
|
||||
destroyView(viewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a componentView into the given hostView at the given element
|
||||
*/
|
||||
attachComponentView(hostViewRef:RenderViewRef, elementIndex:number, componentViewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches a componentView into the given hostView at the given element
|
||||
*/
|
||||
detachComponentView(hostViewRef:RenderViewRef, boundElementIndex:number, componentViewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a view into a ViewContainer (in the given parentView at the given element) at the given index.
|
||||
*/
|
||||
attachViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, viewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches a view into a ViewContainer (in the given parentView at the given element) at the given index.
|
||||
*/
|
||||
// TODO(tbosch): this should return a promise as it can be animated!
|
||||
detachViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, viewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views inside of the view pool.
|
||||
*/
|
||||
hydrateView(hviewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views inside of the view pool.
|
||||
*/
|
||||
dehydrateView(viewRef:RenderViewRef) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a porperty on an element.
|
||||
* Note: This will fail if the property was not mentioned previously as a host property
|
||||
* in the View.
|
||||
* in the ProtoView
|
||||
*/
|
||||
setElementProperty(view:RenderViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {}
|
||||
setElementProperty(viewRef:RenderViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the value of a text node.
|
||||
*/
|
||||
setText(viewRef:RenderViewRef, textNodeIndex:number, text:string):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the value for a text node.
|
||||
* Note: This needs to be separate from setElementProperty as we don't have ElementBinders
|
||||
* for text nodes in the DomProtoView either.
|
||||
* Sets the dispatcher for all events of the given view
|
||||
*/
|
||||
setText(view:RenderViewRef, textNodeIndex:number, text:string):void {}
|
||||
|
||||
/**
|
||||
* Sets the dispatcher for all events that have been defined in the template or in directives
|
||||
* in the given view.
|
||||
*/
|
||||
setEventDispatcher(viewRef:RenderViewRef, dispatcher:any/*EventDispatcher*/):void {}
|
||||
|
||||
/**
|
||||
* To be called at the end of the VmTurn so the API can buffer calls
|
||||
*/
|
||||
flush():void {}
|
||||
setEventDispatcher(viewRef:RenderViewRef, dispatcher:any/*api.EventDispatcher*/):void {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@ import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {BaseException, isPresent} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ViewDefinition, ProtoViewDto, DirectiveMetadata, RenderCompiler, RenderProtoViewRef} from '../../api';
|
||||
@ -11,13 +10,6 @@ import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
|
||||
import {Parser} from 'angular2/change_detection';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
import {ProtoViewBuilder} from '../view/proto_view_builder';
|
||||
|
||||
import {DirectDomProtoViewRef} from '../direct_dom_renderer';
|
||||
|
||||
function _resolveProtoView(protoViewRef:DirectDomProtoViewRef) {
|
||||
return isPresent(protoViewRef) ? protoViewRef.delegate : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The compiler loads and translates the html templates of components into
|
||||
@ -53,18 +45,6 @@ export class DomCompiler extends RenderCompiler {
|
||||
return this._compileTemplate(hostViewDef, element);
|
||||
}
|
||||
|
||||
createImperativeComponentProtoView(rendererId):Promise<ProtoViewDto> {
|
||||
var protoViewBuilder = new ProtoViewBuilder(null);
|
||||
protoViewBuilder.setImperativeRendererId(rendererId);
|
||||
return PromiseWrapper.resolve(protoViewBuilder.build());
|
||||
}
|
||||
|
||||
mergeChildComponentProtoViews(protoViewRef:RenderProtoViewRef, protoViewRefs:List<RenderProtoViewRef>) {
|
||||
_resolveProtoView(protoViewRef).mergeChildComponentProtoViews(
|
||||
ListWrapper.map(protoViewRefs, _resolveProtoView)
|
||||
);
|
||||
}
|
||||
|
||||
_compileTemplate(viewDef: ViewDefinition, tplElement):Promise<ProtoViewDto> {
|
||||
var subTaskPromises = [];
|
||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
|
||||
|
@ -1,158 +0,0 @@
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as api from '../api';
|
||||
import {DomView} from './view/view';
|
||||
import {DomProtoView} from './view/proto_view';
|
||||
import {ViewFactory} from './view/view_factory';
|
||||
import {RenderViewHydrator} from './view/view_hydrator';
|
||||
import {ShadowDomStrategy} from './shadow_dom/shadow_dom_strategy';
|
||||
import {ViewContainer} from './view/view_container';
|
||||
|
||||
function _resolveViewContainer(vc:api.RenderViewContainerRef) {
|
||||
return _resolveView(vc.view).getOrCreateViewContainer(vc.elementIndex);
|
||||
}
|
||||
|
||||
function _resolveView(viewRef:DirectDomViewRef) {
|
||||
return isPresent(viewRef) ? viewRef.delegate : null;
|
||||
}
|
||||
|
||||
function _resolveProtoView(protoViewRef:DirectDomProtoViewRef) {
|
||||
return isPresent(protoViewRef) ? protoViewRef.delegate : null;
|
||||
}
|
||||
|
||||
function _wrapView(view:DomView) {
|
||||
return new DirectDomViewRef(view);
|
||||
}
|
||||
|
||||
function _collectComponentChildViewRefs(view, target = null) {
|
||||
if (isBlank(target)) {
|
||||
target = [];
|
||||
}
|
||||
ListWrapper.push(target, _wrapView(view));
|
||||
ListWrapper.forEach(view.componentChildViews, (view) => {
|
||||
if (isPresent(view)) {
|
||||
_collectComponentChildViewRefs(view, target);
|
||||
}
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public so that the compiler can use it.
|
||||
export class DirectDomProtoViewRef extends api.RenderProtoViewRef {
|
||||
delegate:DomProtoView;
|
||||
|
||||
constructor(delegate:DomProtoView) {
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectDomViewRef extends api.RenderViewRef {
|
||||
delegate:DomView;
|
||||
|
||||
constructor(delegate:DomView) {
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DirectDomRenderer extends api.Renderer {
|
||||
_viewFactory: ViewFactory;
|
||||
_viewHydrator: RenderViewHydrator;
|
||||
_shadowDomStrategy: ShadowDomStrategy;
|
||||
|
||||
constructor(
|
||||
viewFactory: ViewFactory, viewHydrator: RenderViewHydrator, shadowDomStrategy: ShadowDomStrategy) {
|
||||
super();
|
||||
this._viewFactory = viewFactory;
|
||||
this._viewHydrator = viewHydrator;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
createViewInContainer(vcRef:api.RenderViewContainerRef, atIndex:number, protoViewRef:api.RenderProtoViewRef):List<api.RenderViewRef> {
|
||||
var view = this._viewFactory.getView(_resolveProtoView(protoViewRef));
|
||||
var vc = _resolveViewContainer(vcRef);
|
||||
this._viewHydrator.hydrateViewInViewContainer(vc, view);
|
||||
vc.insert(view, atIndex);
|
||||
return _collectComponentChildViewRefs(view);
|
||||
}
|
||||
|
||||
destroyViewInContainer(vcRef:api.RenderViewContainerRef, atIndex:number):void {
|
||||
var vc = _resolveViewContainer(vcRef);
|
||||
var view = vc.detach(atIndex);
|
||||
this._viewHydrator.dehydrateViewInViewContainer(vc, view);
|
||||
this._viewFactory.returnView(view);
|
||||
}
|
||||
|
||||
insertViewIntoContainer(vcRef:api.RenderViewContainerRef, atIndex=-1, viewRef:api.RenderViewRef):void {
|
||||
_resolveViewContainer(vcRef).insert(_resolveView(viewRef), atIndex);
|
||||
}
|
||||
|
||||
detachViewFromContainer(vcRef:api.RenderViewContainerRef, atIndex:number):void {
|
||||
_resolveViewContainer(vcRef).detach(atIndex);
|
||||
}
|
||||
|
||||
createDynamicComponentView(hostViewRef:api.RenderViewRef, elementIndex:number, componentViewRef:api.RenderProtoViewRef):List<api.RenderViewRef> {
|
||||
var hostView = _resolveView(hostViewRef);
|
||||
var componentView = this._viewFactory.getView(_resolveProtoView(componentViewRef));
|
||||
this._viewHydrator.hydrateDynamicComponentView(hostView, elementIndex, componentView);
|
||||
return _collectComponentChildViewRefs(componentView);
|
||||
}
|
||||
|
||||
destroyDynamicComponentView(hostViewRef:api.RenderViewRef, elementIndex:number):void {
|
||||
throw new BaseException('Not supported yet');
|
||||
// Something along these lines:
|
||||
// var hostView = _resolveView(hostViewRef);
|
||||
// var componentView = hostView.childComponentViews[elementIndex];
|
||||
// this._viewHydrator.dehydrateDynamicComponentView(hostView, componentView);
|
||||
}
|
||||
|
||||
createInPlaceHostView(parentViewRef:api.RenderViewRef, hostElementSelector, hostProtoViewRef:api.RenderProtoViewRef):List<api.RenderViewRef> {
|
||||
var parentView = _resolveView(parentViewRef);
|
||||
var hostView = this._viewFactory.createInPlaceHostView(hostElementSelector, _resolveProtoView(hostProtoViewRef));
|
||||
this._viewHydrator.hydrateInPlaceHostView(parentView, hostView);
|
||||
return _collectComponentChildViewRefs(hostView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the given host view in the given parent view.
|
||||
*/
|
||||
destroyInPlaceHostView(parentViewRef:api.RenderViewRef, hostViewRef:api.RenderViewRef):void {
|
||||
var parentView = _resolveView(parentViewRef);
|
||||
var hostView = _resolveView(hostViewRef);
|
||||
this._viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
||||
}
|
||||
|
||||
setImperativeComponentRootNodes(parentViewRef:api.RenderViewRef, elementIndex:number, nodes:List):void {
|
||||
var parentView = _resolveView(parentViewRef);
|
||||
var hostElement = parentView.boundElements[elementIndex];
|
||||
var componentView = parentView.componentChildViews[elementIndex];
|
||||
if (isBlank(componentView)) {
|
||||
throw new BaseException(`There is no componentChildView at index ${elementIndex}`);
|
||||
}
|
||||
if (isBlank(componentView.proto.imperativeRendererId)) {
|
||||
throw new BaseException(`This component view has no imperative renderer`);
|
||||
}
|
||||
ViewContainer.removeViewNodes(componentView);
|
||||
componentView.rootNodes = nodes;
|
||||
this._shadowDomStrategy.attachTemplate(hostElement, componentView);
|
||||
}
|
||||
|
||||
setElementProperty(viewRef:api.RenderViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
||||
_resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setText(viewRef:api.RenderViewRef, textNodeIndex:number, text:string):void {
|
||||
_resolveView(viewRef).setText(textNodeIndex, text);
|
||||
}
|
||||
|
||||
setEventDispatcher(viewRef:api.RenderViewRef, dispatcher:any/*api.EventDispatcher*/):void {
|
||||
_resolveView(viewRef).setEventDispatcher(dispatcher);
|
||||
}
|
||||
}
|
338
modules/angular2/src/render/dom/dom_renderer.js
vendored
Normal file
338
modules/angular2/src/render/dom/dom_renderer.js
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
import {Inject, Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {int, isPresent, isBlank, BaseException, RegExpWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {Content} from './shadow_dom/content_tag';
|
||||
import {ShadowDomStrategy} from './shadow_dom/shadow_dom_strategy';
|
||||
import {EventManager} from './events/event_manager';
|
||||
|
||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './view/proto_view';
|
||||
import {DomView, DomViewRef, resolveInternalDomView} from './view/view';
|
||||
import {DomViewContainer} from './view/view_container';
|
||||
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from './util';
|
||||
|
||||
import {Renderer, RenderProtoViewRef, RenderViewRef} from '../api';
|
||||
|
||||
// TODO(tbosch): use an OpaqueToken here once our transpiler supports
|
||||
// const expressions!
|
||||
export const DOCUMENT_TOKEN = 'DocumentToken';
|
||||
|
||||
var _DOCUMENT_SELECTOR_REGEX = RegExpWrapper.create('\\:document(.+)');
|
||||
|
||||
@Injectable()
|
||||
export class DomRenderer extends Renderer {
|
||||
_eventManager:EventManager;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
_document;
|
||||
|
||||
constructor(eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy, @Inject(DOCUMENT_TOKEN) document) {
|
||||
super();
|
||||
this._eventManager = eventManager;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
this._document = document;
|
||||
}
|
||||
|
||||
createInPlaceHostView(parentHostViewRef:RenderViewRef, hostElementSelector:string, hostProtoViewRef:RenderProtoViewRef):RenderViewRef {
|
||||
var containerNode;
|
||||
var documentSelectorMatch = RegExpWrapper.firstMatch(_DOCUMENT_SELECTOR_REGEX, hostElementSelector);
|
||||
if (isPresent(documentSelectorMatch)) {
|
||||
containerNode = this._document;
|
||||
hostElementSelector = documentSelectorMatch[1];
|
||||
} else if (isPresent(parentHostViewRef)) {
|
||||
var parentHostView = resolveInternalDomView(parentHostViewRef);
|
||||
containerNode = parentHostView.shadowRoot;
|
||||
} else {
|
||||
containerNode = this._document;
|
||||
}
|
||||
var element = DOM.querySelector(containerNode, hostElementSelector);
|
||||
if (isBlank(element)) {
|
||||
throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`);
|
||||
}
|
||||
var hostProtoView = resolveInternalDomProtoView(hostProtoViewRef);
|
||||
return new DomViewRef(this._createView(hostProtoView, element));
|
||||
}
|
||||
|
||||
destroyInPlaceHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
||||
var hostView = resolveInternalDomView(hostViewRef);
|
||||
this._removeViewNodes(hostView);
|
||||
}
|
||||
|
||||
createView(protoViewRef:RenderProtoViewRef):RenderViewRef {
|
||||
var protoView = resolveInternalDomProtoView(protoViewRef);
|
||||
return new DomViewRef(this._createView(protoView, null));
|
||||
}
|
||||
|
||||
destroyView(view:RenderViewRef) {
|
||||
// noop for now
|
||||
}
|
||||
|
||||
attachComponentView(hostViewRef:RenderViewRef, elementIndex:number, componentViewRef:RenderViewRef) {
|
||||
var hostView = resolveInternalDomView(hostViewRef);
|
||||
var componentView = resolveInternalDomView(componentViewRef);
|
||||
var element = hostView.boundElements[elementIndex];
|
||||
var lightDom = hostView.lightDoms[elementIndex];
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.attachShadowDomView(componentView);
|
||||
}
|
||||
var shadowRoot = this._shadowDomStrategy.prepareShadowRoot(element);
|
||||
this._moveViewNodesIntoParent(shadowRoot, componentView);
|
||||
componentView.hostLightDom = lightDom;
|
||||
componentView.shadowRoot = shadowRoot;
|
||||
}
|
||||
|
||||
setComponentViewRootNodes(componentViewRef:RenderViewRef, rootNodes:List) {
|
||||
var componentView = resolveInternalDomView(componentViewRef);
|
||||
this._removeViewNodes(componentView);
|
||||
componentView.rootNodes = rootNodes;
|
||||
this._moveViewNodesIntoParent(componentView.shadowRoot, componentView);
|
||||
}
|
||||
|
||||
detachComponentView(hostViewRef:RenderViewRef, boundElementIndex:number, componentViewRef:RenderViewRef) {
|
||||
var hostView = resolveInternalDomView(hostViewRef);
|
||||
var componentView = resolveInternalDomView(componentViewRef);
|
||||
this._removeViewNodes(componentView);
|
||||
var lightDom = hostView.lightDoms[boundElementIndex];
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.detachShadowDomView();
|
||||
}
|
||||
componentView.hostLightDom = null;
|
||||
componentView.shadowRoot = null;
|
||||
}
|
||||
|
||||
attachViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, viewRef:RenderViewRef) {
|
||||
var parentView = resolveInternalDomView(parentViewRef);
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
var viewContainer = this._getOrCreateViewContainer(parentView, boundElementIndex);
|
||||
ListWrapper.insert(viewContainer.views, atIndex, view);
|
||||
view.hostLightDom = parentView.hostLightDom;
|
||||
|
||||
var directParentLightDom = parentView.getDirectParentLightDom(boundElementIndex);
|
||||
if (isBlank(directParentLightDom)) {
|
||||
var siblingToInsertAfter;
|
||||
if (atIndex == 0) {
|
||||
siblingToInsertAfter = parentView.boundElements[boundElementIndex];
|
||||
} else {
|
||||
siblingToInsertAfter = ListWrapper.last(viewContainer.views[atIndex - 1].rootNodes);
|
||||
}
|
||||
this._moveViewNodesAfterSibling(siblingToInsertAfter, view);
|
||||
} else {
|
||||
directParentLightDom.redistribute();
|
||||
}
|
||||
// new content tags might have appeared, we need to redistribute.
|
||||
if (isPresent(parentView.hostLightDom)) {
|
||||
parentView.hostLightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
detachViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, viewRef:RenderViewRef) {
|
||||
var parentView = resolveInternalDomView(parentViewRef);
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var detachedView = viewContainer.views[atIndex];
|
||||
ListWrapper.removeAt(viewContainer.views, atIndex);
|
||||
var directParentLightDom = parentView.getDirectParentLightDom(boundElementIndex);
|
||||
if (isBlank(directParentLightDom)) {
|
||||
this._removeViewNodes(detachedView);
|
||||
} else {
|
||||
directParentLightDom.redistribute();
|
||||
}
|
||||
view.hostLightDom = null;
|
||||
// content tags might have disappeared we need to do redistribution.
|
||||
if (isPresent(parentView.hostLightDom)) {
|
||||
parentView.hostLightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
hydrateView(viewRef:RenderViewRef) {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
if (view.hydrated) throw new BaseException('The view is already hydrated.');
|
||||
view.hydrated = true;
|
||||
|
||||
for (var i = 0; i < view.lightDoms.length; ++i) {
|
||||
var lightDom = view.lightDoms[i];
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
//add global events
|
||||
view.eventHandlerRemovers = ListWrapper.create();
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
if (isPresent(binder.globalEvents)) {
|
||||
for (var i = 0; i < binder.globalEvents.length; i++) {
|
||||
var globalEvent = binder.globalEvents[i];
|
||||
var remover = this._createGlobalEventListener(view, binderIdx, globalEvent.name, globalEvent.target, globalEvent.fullName);
|
||||
ListWrapper.push(view.eventHandlerRemovers, remover);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isPresent(view.hostLightDom)) {
|
||||
view.hostLightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
dehydrateView(viewRef:RenderViewRef) {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
|
||||
//remove global events
|
||||
for (var i = 0; i < view.eventHandlerRemovers.length; i++) {
|
||||
view.eventHandlerRemovers[i]();
|
||||
}
|
||||
|
||||
view.eventHandlerRemovers = null;
|
||||
view.hydrated = false;
|
||||
}
|
||||
|
||||
setElementProperty(viewRef:RenderViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
view.setElementProperty(elementIndex, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setText(viewRef:RenderViewRef, textNodeIndex:number, text:string):void {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
DOM.setText(view.boundTextNodes[textNodeIndex], text);
|
||||
}
|
||||
|
||||
setEventDispatcher(viewRef:RenderViewRef, dispatcher:any/*api.EventDispatcher*/):void {
|
||||
var view = resolveInternalDomView(viewRef);
|
||||
view.eventDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
_createView(protoView:DomProtoView, inplaceElement): DomView {
|
||||
var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
if (protoView.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 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 DomProtoView
|
||||
// Note: An explicit loop is the fastest way to convert a DOM array into a JS array!
|
||||
while(childNode != null) {
|
||||
ListWrapper.push(viewRootNodes, childNode);
|
||||
childNode = DOM.nextSibling(childNode);
|
||||
}
|
||||
} else {
|
||||
viewRootNodes = [rootElementClone];
|
||||
}
|
||||
var binders = protoView.elementBinders;
|
||||
var boundTextNodes = [];
|
||||
var boundElements = ListWrapper.createFixedSize(binders.length);
|
||||
var contentTags = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var element;
|
||||
if (binderIdx === 0 && protoView.rootBindingOffset === 1) {
|
||||
element = rootElementClone;
|
||||
} else {
|
||||
element = elementsWithBindings[binderIdx - protoView.rootBindingOffset];
|
||||
}
|
||||
boundElements[binderIdx] = element;
|
||||
|
||||
// boundTextNodes
|
||||
var childNodes = DOM.childNodes(DOM.templateAwareRoot(element));
|
||||
var textNodeIndices = binder.textNodeIndices;
|
||||
for (var i = 0; i<textNodeIndices.length; i++) {
|
||||
ListWrapper.push(boundTextNodes, childNodes[textNodeIndices[i]]);
|
||||
}
|
||||
|
||||
// contentTags
|
||||
var contentTag = null;
|
||||
if (isPresent(binder.contentTagSelector)) {
|
||||
contentTag = new Content(element, binder.contentTagSelector);
|
||||
}
|
||||
contentTags[binderIdx] = contentTag;
|
||||
}
|
||||
|
||||
var view = new DomView(
|
||||
protoView, viewRootNodes,
|
||||
boundTextNodes, boundElements, contentTags
|
||||
);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var element = boundElements[binderIdx];
|
||||
|
||||
// lightDoms
|
||||
var lightDom = null;
|
||||
if (isPresent(binder.componentId)) {
|
||||
lightDom = this._shadowDomStrategy.constructLightDom(view, boundElements[binderIdx]);
|
||||
}
|
||||
view.lightDoms[binderIdx] = lightDom;
|
||||
|
||||
// init contentTags
|
||||
var contentTag = contentTags[binderIdx];
|
||||
if (isPresent(contentTag)) {
|
||||
var destLightDom = view.getDirectParentLightDom(binderIdx);
|
||||
contentTag.init(destLightDom);
|
||||
}
|
||||
|
||||
// events
|
||||
if (isPresent(binder.eventLocals) && isPresent(binder.localEvents)) {
|
||||
for (var i = 0; i < binder.localEvents.length; i++) {
|
||||
this._createEventListener(view, element, binderIdx, binder.localEvents[i].name, binder.eventLocals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
_createEventListener(view, element, elementIndex, eventName, eventLocals) {
|
||||
this._eventManager.addEventListener(element, eventName, (event) => {
|
||||
view.dispatchEvent(elementIndex, eventName, event);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
_moveViewNodesAfterSibling(sibling, view) {
|
||||
for (var i = view.rootNodes.length - 1; i >= 0; --i) {
|
||||
DOM.insertAfter(sibling, view.rootNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_moveViewNodesIntoParent(parent, view) {
|
||||
for (var i = 0; i < view.rootNodes.length; ++i) {
|
||||
DOM.appendChild(parent, view.rootNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_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]);
|
||||
}
|
||||
}
|
||||
|
||||
_getOrCreateViewContainer(parentView:DomView, boundElementIndex) {
|
||||
var vc = parentView.viewContainers[boundElementIndex];
|
||||
if (isBlank(vc)) {
|
||||
vc = new DomViewContainer();
|
||||
parentView.viewContainers[boundElementIndex] = vc;
|
||||
}
|
||||
return vc;
|
||||
}
|
||||
|
||||
_createGlobalEventListener(view, elementIndex, eventName, eventTarget, fullName): Function {
|
||||
return this._eventManager.addGlobalEventListener(eventTarget, eventName, (event) => {
|
||||
view.dispatchEvent(elementIndex, fullName, event);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -74,16 +74,12 @@ export class Content {
|
||||
this._strategy = null;
|
||||
}
|
||||
|
||||
hydrate(destinationLightDom:ldModule.LightDom) {
|
||||
init(destinationLightDom:ldModule.LightDom) {
|
||||
this._strategy = isPresent(destinationLightDom) ?
|
||||
new IntermediateContent(destinationLightDom) :
|
||||
new RenderedContent(this.contentStartElement);
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
this._strategy = null;
|
||||
}
|
||||
|
||||
nodes():List {
|
||||
return this._strategy.nodes;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import * as viewModule from '../view/view';
|
||||
import {LightDom} from './light_dom';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
import {moveViewNodesIntoParent} from './util';
|
||||
import {insertSharedStyleText} from './util';
|
||||
|
||||
/**
|
||||
@ -33,12 +32,12 @@ export class EmulatedUnscopedShadowDomStrategy extends ShadowDomStrategy {
|
||||
return false;
|
||||
}
|
||||
|
||||
attachTemplate(el, view:viewModule.DomView) {
|
||||
moveViewNodesIntoParent(el, view);
|
||||
prepareShadowRoot(el) {
|
||||
return el;
|
||||
}
|
||||
|
||||
constructLightDom(lightDomView:viewModule.DomView, shadowDomView:viewModule.DomView, el): LightDom {
|
||||
return new LightDom(lightDomView, shadowDomView, el);
|
||||
constructLightDom(lightDomView:viewModule.DomView, el): LightDom {
|
||||
return new LightDom(lightDomView, el);
|
||||
}
|
||||
|
||||
processStyleElement(hostComponentId:string, templateUrl:string, styleEl):Promise {
|
||||
|
@ -28,12 +28,20 @@ export class LightDom {
|
||||
nodes:List;
|
||||
roots:List<_Root>;
|
||||
|
||||
constructor(lightDomView:viewModule.DomView, shadowDomView:viewModule.DomView, element) {
|
||||
constructor(lightDomView:viewModule.DomView, element) {
|
||||
this.lightDomView = lightDomView;
|
||||
this.shadowDomView = shadowDomView;
|
||||
this.nodes = DOM.childNodesAsList(element);
|
||||
|
||||
this.roots = null;
|
||||
this.shadowDomView = null;
|
||||
}
|
||||
|
||||
attachShadowDomView(shadowDomView:viewModule.DomView) {
|
||||
this.shadowDomView = shadowDomView;
|
||||
}
|
||||
|
||||
detachShadowDomView() {
|
||||
this.shadowDomView = null;
|
||||
}
|
||||
|
||||
redistribute() {
|
||||
@ -41,7 +49,11 @@ export class LightDom {
|
||||
}
|
||||
|
||||
contentTags(): List<Content> {
|
||||
return this._collectAllContentTags(this.shadowDomView, []);
|
||||
if (isPresent(this.shadowDomView)) {
|
||||
return this._collectAllContentTags(this.shadowDomView, []);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Collects the Content directives from the view and all its child views
|
||||
|
@ -2,11 +2,8 @@ import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewModule from '../view/view';
|
||||
|
||||
import {StyleUrlResolver} from './style_url_resolver';
|
||||
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||
import {moveViewNodesIntoParent} from './util';
|
||||
|
||||
/**
|
||||
* This strategies uses the native Shadow DOM support.
|
||||
@ -22,8 +19,8 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
this.styleUrlResolver = styleUrlResolver;
|
||||
}
|
||||
|
||||
attachTemplate(el, view:viewModule.DomView){
|
||||
moveViewNodesIntoParent(DOM.createShadowRoot(el), view);
|
||||
prepareShadowRoot(el) {
|
||||
return DOM.createShadowRoot(el);
|
||||
}
|
||||
|
||||
processStyleElement(hostComponentId:string, templateUrl:string, styleEl):Promise {
|
||||
|
@ -9,9 +9,14 @@ export class ShadowDomStrategy {
|
||||
return true;
|
||||
}
|
||||
|
||||
attachTemplate(el, view:viewModule.DomView) {}
|
||||
/**
|
||||
* Prepares and returns the shadow root for the given element.
|
||||
*/
|
||||
prepareShadowRoot(el):any {
|
||||
return null;
|
||||
}
|
||||
|
||||
constructLightDom(lightDomView:viewModule.DomView, shadowDomView:viewModule.DomView, el): LightDom {
|
||||
constructLightDom(lightDomView:viewModule.DomView, el): LightDom {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,6 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ShadowCss} from './shadow_css';
|
||||
|
||||
export function moveViewNodesIntoParent(parent, view) {
|
||||
for (var i = 0; i < view.rootNodes.length; ++i) {
|
||||
DOM.appendChild(parent, view.rootNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var _componentUIDs: Map<string, int> = MapWrapper.create();
|
||||
var _nextComponentUID: int = 0;
|
||||
var _sharedStyleTexts: Map<string, boolean> = MapWrapper.create();
|
||||
|
@ -1,4 +1,3 @@
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {AST} from 'angular2/change_detection';
|
||||
import {SetterFn} from 'angular2/src/reflection/types';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
@ -39,14 +38,6 @@ export class ElementBinder {
|
||||
this.distanceToParent = distanceToParent;
|
||||
this.propertySetters = propertySetters;
|
||||
}
|
||||
|
||||
hasStaticComponent() {
|
||||
return isPresent(this.componentId) && isPresent(this.nestedProtoView);
|
||||
}
|
||||
|
||||
hasDynamicComponent() {
|
||||
return isPresent(this.componentId) && isBlank(this.nestedProtoView);
|
||||
}
|
||||
}
|
||||
|
||||
export class Event {
|
||||
|
@ -1,43 +1,39 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {NG_BINDING_CLASS} from '../util';
|
||||
|
||||
import {RenderProtoViewRef} from '../../api';
|
||||
|
||||
export function resolveInternalDomProtoView(protoViewRef:RenderProtoViewRef) {
|
||||
var domProtoViewRef:DomProtoViewRef = protoViewRef;
|
||||
return domProtoViewRef._protoView;
|
||||
}
|
||||
|
||||
export class DomProtoViewRef extends RenderProtoViewRef {
|
||||
_protoView:DomProtoView;
|
||||
constructor(protoView:DomProtoView) {
|
||||
super();
|
||||
this._protoView = protoView;
|
||||
}
|
||||
}
|
||||
|
||||
export class DomProtoView {
|
||||
element;
|
||||
elementBinders:List<ElementBinder>;
|
||||
isTemplateElement:boolean;
|
||||
rootBindingOffset:int;
|
||||
imperativeRendererId:string;
|
||||
|
||||
constructor({
|
||||
elementBinders,
|
||||
element,
|
||||
imperativeRendererId
|
||||
element
|
||||
}) {
|
||||
this.element = element;
|
||||
this.elementBinders = elementBinders;
|
||||
this.imperativeRendererId = imperativeRendererId;
|
||||
if (isPresent(imperativeRendererId)) {
|
||||
this.rootBindingOffset = 0;
|
||||
this.isTemplateElement = false;
|
||||
} else {
|
||||
this.isTemplateElement = DOM.isTemplateElement(this.element);
|
||||
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
mergeChildComponentProtoViews(componentProtoViews:List<DomProtoView>) {
|
||||
var componentProtoViewIndex = 0;
|
||||
for (var i=0; i<this.elementBinders.length; i++) {
|
||||
var eb = this.elementBinders[i];
|
||||
if (isPresent(eb.componentId)) {
|
||||
eb.nestedProtoView = componentProtoViews[componentProtoViewIndex];
|
||||
componentProtoViewIndex++;
|
||||
}
|
||||
}
|
||||
this.isTemplateElement = DOM.isTemplateElement(this.element);
|
||||
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,11 @@ import {
|
||||
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
|
||||
} from 'angular2/change_detection';
|
||||
|
||||
import {DomProtoView} from './proto_view';
|
||||
import {DomProtoView, DomProtoViewRef, resolveInternalDomProtoView} from './proto_view';
|
||||
import {ElementBinder, Event} from './element_binder';
|
||||
import {setterFactory} from './property_setter_factory';
|
||||
|
||||
import * as api from '../../api';
|
||||
import * as directDomRenderer from '../direct_dom_renderer';
|
||||
|
||||
import {NG_BINDING_CLASS, EVENT_TARGET_SEPARATOR} from '../util';
|
||||
|
||||
@ -19,18 +18,11 @@ export class ProtoViewBuilder {
|
||||
rootElement;
|
||||
variableBindings: Map<string, string>;
|
||||
elements:List<ElementBinderBuilder>;
|
||||
imperativeRendererId:string;
|
||||
|
||||
constructor(rootElement) {
|
||||
this.rootElement = rootElement;
|
||||
this.elements = [];
|
||||
this.variableBindings = MapWrapper.create();
|
||||
this.imperativeRendererId = null;
|
||||
}
|
||||
|
||||
setImperativeRendererId(id:string):ProtoViewBuilder {
|
||||
this.imperativeRendererId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
bindElement(element, description = null):ElementBinderBuilder {
|
||||
@ -93,7 +85,7 @@ export class ProtoViewBuilder {
|
||||
contentTagSelector: ebb.contentTagSelector,
|
||||
parentIndex: parentIndex,
|
||||
distanceToParent: ebb.distanceToParent,
|
||||
nestedProtoView: isPresent(nestedProtoView) ? nestedProtoView.render.delegate : null,
|
||||
nestedProtoView: isPresent(nestedProtoView) ? resolveInternalDomProtoView(nestedProtoView.render) : null,
|
||||
componentId: ebb.componentId,
|
||||
eventLocals: new LiteralArray(ebb.eventBuilder.buildEventLocals()),
|
||||
localEvents: ebb.eventBuilder.buildLocalEvents(),
|
||||
@ -102,10 +94,9 @@ export class ProtoViewBuilder {
|
||||
}));
|
||||
});
|
||||
return new api.ProtoViewDto({
|
||||
render: new directDomRenderer.DirectDomProtoViewRef(new DomProtoView({
|
||||
render: new DomProtoViewRef(new DomProtoView({
|
||||
element: this.rootElement,
|
||||
elementBinders: renderElementBinders,
|
||||
imperativeRendererId: this.imperativeRendererId
|
||||
elementBinders: renderElementBinders
|
||||
})),
|
||||
elementBinders: apiElementBinders,
|
||||
variableBindings: this.variableBindings
|
||||
|
56
modules/angular2/src/render/dom/view/view.js
vendored
56
modules/angular2/src/render/dom/view/view.js
vendored
@ -2,13 +2,30 @@ 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 {ViewContainer} from './view_container';
|
||||
import {DomViewContainer} from './view_container';
|
||||
import {DomProtoView} from './proto_view';
|
||||
import {LightDom} from '../shadow_dom/light_dom';
|
||||
import {Content} from '../shadow_dom/content_tag';
|
||||
|
||||
import {RenderViewRef} from '../../api';
|
||||
|
||||
// TODO(tbosch): enable this again!
|
||||
// import {EventDispatcher} from '../../api';
|
||||
|
||||
export function resolveInternalDomView(viewRef:RenderViewRef) {
|
||||
var domViewRef:DomViewRef = viewRef;
|
||||
return domViewRef._view;
|
||||
}
|
||||
|
||||
export class DomViewRef extends RenderViewRef {
|
||||
_view:DomView;
|
||||
constructor(view:DomView) {
|
||||
super();
|
||||
this._view = view;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
|
||||
/**
|
||||
@ -22,18 +39,15 @@ export class DomView {
|
||||
rootNodes:List;
|
||||
// TODO(tbosch): move componentChildViews, viewContainers, contentTags, lightDoms into
|
||||
// a single array with records inside
|
||||
componentChildViews: List<DomView>;
|
||||
viewContainers: List<ViewContainer>;
|
||||
viewContainers: List<DomViewContainer>;
|
||||
contentTags: List<Content>;
|
||||
lightDoms: List<LightDom>;
|
||||
hostLightDom: LightDom;
|
||||
shadowRoot;
|
||||
proto: DomProtoView;
|
||||
hydrated: boolean;
|
||||
_eventDispatcher: any/*EventDispatcher*/;
|
||||
eventDispatcher: any/*EventDispatcher*/;
|
||||
eventHandlerRemovers: List<Function>;
|
||||
/// Host views that were added by an imperative view.
|
||||
/// This is a dynamically growing / shrinking array.
|
||||
imperativeHostViews: List<DomView>;
|
||||
|
||||
constructor(
|
||||
proto:DomProtoView, rootNodes:List,
|
||||
@ -45,12 +59,11 @@ export class DomView {
|
||||
this.viewContainers = ListWrapper.createFixedSize(boundElements.length);
|
||||
this.contentTags = contentTags;
|
||||
this.lightDoms = ListWrapper.createFixedSize(boundElements.length);
|
||||
ListWrapper.fill(this.lightDoms, null);
|
||||
this.componentChildViews = ListWrapper.createFixedSize(boundElements.length);
|
||||
this.hostLightDom = null;
|
||||
this.hydrated = false;
|
||||
this.eventHandlerRemovers = [];
|
||||
this.imperativeHostViews = [];
|
||||
this.eventDispatcher = null;
|
||||
this.shadowRoot = null;
|
||||
}
|
||||
|
||||
getDirectParentLightDom(boundElementIndex:number) {
|
||||
@ -62,15 +75,6 @@ export class DomView {
|
||||
return destLightDom;
|
||||
}
|
||||
|
||||
getOrCreateViewContainer(binderIndex) {
|
||||
var vc = this.viewContainers[binderIndex];
|
||||
if (isBlank(vc)) {
|
||||
vc = new ViewContainer(this, binderIndex);
|
||||
this.viewContainers[binderIndex] = vc;
|
||||
}
|
||||
return vc;
|
||||
}
|
||||
|
||||
setElementProperty(elementIndex:number, propertyName:string, value:any) {
|
||||
var setter = MapWrapper.get(this.proto.elementBinders[elementIndex].propertySetters, propertyName);
|
||||
setter(this.boundElements[elementIndex], value);
|
||||
@ -80,24 +84,16 @@ export class DomView {
|
||||
DOM.setText(this.boundTextNodes[textIndex], value);
|
||||
}
|
||||
|
||||
getViewContainer(index:number):ViewContainer {
|
||||
return this.viewContainers[index];
|
||||
}
|
||||
|
||||
setEventDispatcher(dispatcher:any/*EventDispatcher*/) {
|
||||
this._eventDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
dispatchEvent(elementIndex, eventName, event): boolean {
|
||||
var allowDefaultBehavior = true;
|
||||
if (isPresent(this._eventDispatcher)) {
|
||||
if (isPresent(this.eventDispatcher)) {
|
||||
var evalLocals = MapWrapper.create();
|
||||
MapWrapper.set(evalLocals, '$event', event);
|
||||
// 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);
|
||||
allowDefaultBehavior = this._eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals);
|
||||
// this.eventDispatcher.dispatchEvent(elementIndex, eventName, localValues);
|
||||
allowDefaultBehavior = this.eventDispatcher.dispatchEvent(elementIndex, eventName, evalLocals);
|
||||
if (!allowDefaultBehavior) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
@ -1,90 +1,15 @@
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
export class ViewContainer {
|
||||
parentView: viewModule.DomView;
|
||||
boundElementIndex: number;
|
||||
export class DomViewContainer {
|
||||
views: List<viewModule.DomView>;
|
||||
|
||||
constructor(parentView: viewModule.DomView, boundElementIndex: number) {
|
||||
this.parentView = parentView;
|
||||
this.boundElementIndex = boundElementIndex;
|
||||
constructor() {
|
||||
// The order in this list matches the DOM order.
|
||||
this.views = [];
|
||||
}
|
||||
|
||||
get(index: number): viewModule.DomView {
|
||||
return this.views[index];
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.views.length;
|
||||
}
|
||||
|
||||
_siblingToInsertAfter(index: number) {
|
||||
if (index == 0) return this.parentView.boundElements[this.boundElementIndex];
|
||||
return ListWrapper.last(this.views[index - 1].rootNodes);
|
||||
}
|
||||
|
||||
_checkHydrated() {
|
||||
if (!this.parentView.hydrated) throw new BaseException(
|
||||
'Cannot change dehydrated ViewContainer');
|
||||
}
|
||||
|
||||
_getDirectParentLightDom() {
|
||||
return this.parentView.getDirectParentLightDom(this.boundElementIndex);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._checkHydrated();
|
||||
for (var i=this.views.length-1; i>=0; i--) {
|
||||
this.detach(i);
|
||||
}
|
||||
if (isPresent(this._getDirectParentLightDom())) {
|
||||
this._getDirectParentLightDom().redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
insert(view, atIndex=-1): viewModule.DomView {
|
||||
this._checkHydrated();
|
||||
if (atIndex == -1) atIndex = this.views.length;
|
||||
ListWrapper.insert(this.views, atIndex, view);
|
||||
|
||||
if (isBlank(this._getDirectParentLightDom())) {
|
||||
ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view);
|
||||
} else {
|
||||
this._getDirectParentLightDom().redistribute();
|
||||
}
|
||||
// new content tags might have appeared, we need to redistribute.
|
||||
if (isPresent(this.parentView.hostLightDom)) {
|
||||
this.parentView.hostLightDom.redistribute();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method can be used together with insert to implement a view move, i.e.
|
||||
* moving the dom nodes while the directives in the view stay intact.
|
||||
*/
|
||||
detach(atIndex:number) {
|
||||
this._checkHydrated();
|
||||
var detachedView = this.get(atIndex);
|
||||
ListWrapper.removeAt(this.views, atIndex);
|
||||
if (isBlank(this._getDirectParentLightDom())) {
|
||||
ViewContainer.removeViewNodes(detachedView);
|
||||
} else {
|
||||
this._getDirectParentLightDom().redistribute();
|
||||
}
|
||||
// content tags might have disappeared we need to do redistribution.
|
||||
if (isPresent(this.parentView.hostLightDom)) {
|
||||
this.parentView.hostLightDom.redistribute();
|
||||
}
|
||||
return detachedView;
|
||||
}
|
||||
|
||||
contentTagContainers() {
|
||||
return this.views;
|
||||
}
|
||||
@ -97,18 +22,4 @@ export class ViewContainer {
|
||||
return r;
|
||||
}
|
||||
|
||||
static moveViewNodesAfterSibling(sibling, view) {
|
||||
for (var i = view.rootNodes.length - 1; i >= 0; --i) {
|
||||
DOM.insertAfter(sibling, view.rootNodes[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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
164
modules/angular2/src/render/dom/view/view_factory.js
vendored
164
modules/angular2/src/render/dom/view/view_factory.js
vendored
@ -1,164 +0,0 @@
|
||||
import {Inject, Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
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 * as pvModule from './proto_view';
|
||||
import * as viewModule from './view';
|
||||
import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util';
|
||||
|
||||
// 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 {
|
||||
_poolCapacityPerProtoView:number;
|
||||
_pooledViewsPerProtoView:Map<pvModule.DomProtoView, List<viewModule.DomView>>;
|
||||
_eventManager:EventManager;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
|
||||
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView,
|
||||
eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
this._pooledViewsPerProtoView = MapWrapper.create();
|
||||
this._eventManager = eventManager;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
createInPlaceHostView(hostElementSelector, hostProtoView:pvModule.DomProtoView):viewModule.DomView {
|
||||
return this._createView(hostProtoView, hostElementSelector);
|
||||
}
|
||||
|
||||
getView(protoView:pvModule.DomProtoView):viewModule.DomView {
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||
return ListWrapper.removeLast(pooledViews);
|
||||
}
|
||||
return this._createView(protoView, null);
|
||||
}
|
||||
|
||||
returnView(view:viewModule.DomView) {
|
||||
if (view.hydrated) {
|
||||
throw new BaseException('View is still hydrated');
|
||||
}
|
||||
var protoView = view.proto;
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews);
|
||||
}
|
||||
if (pooledViews.length < this._poolCapacityPerProtoView) {
|
||||
ListWrapper.push(pooledViews, view);
|
||||
}
|
||||
}
|
||||
|
||||
_createView(protoView:pvModule.DomProtoView, inplaceElement): viewModule.DomView {
|
||||
if (isPresent(protoView.imperativeRendererId)) {
|
||||
return new viewModule.DomView(
|
||||
protoView, [], [], [], []
|
||||
);
|
||||
}
|
||||
|
||||
var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
if (protoView.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 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 pvModule.DomProtoView
|
||||
// Note: An explicit loop is the fastest way to convert a DOM array into a JS array!
|
||||
while(childNode != null) {
|
||||
ListWrapper.push(viewRootNodes, childNode);
|
||||
childNode = DOM.nextSibling(childNode);
|
||||
}
|
||||
} else {
|
||||
viewRootNodes = [rootElementClone];
|
||||
}
|
||||
var binders = protoView.elementBinders;
|
||||
var boundTextNodes = [];
|
||||
var boundElements = ListWrapper.createFixedSize(binders.length);
|
||||
var contentTags = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var element;
|
||||
if (binderIdx === 0 && protoView.rootBindingOffset === 1) {
|
||||
element = rootElementClone;
|
||||
} else {
|
||||
element = elementsWithBindings[binderIdx - protoView.rootBindingOffset];
|
||||
}
|
||||
boundElements[binderIdx] = element;
|
||||
|
||||
// boundTextNodes
|
||||
var childNodes = DOM.childNodes(DOM.templateAwareRoot(element));
|
||||
var textNodeIndices = binder.textNodeIndices;
|
||||
for (var i = 0; i<textNodeIndices.length; i++) {
|
||||
ListWrapper.push(boundTextNodes, childNodes[textNodeIndices[i]]);
|
||||
}
|
||||
|
||||
// contentTags
|
||||
var contentTag = null;
|
||||
if (isPresent(binder.contentTagSelector)) {
|
||||
contentTag = new Content(element, binder.contentTagSelector);
|
||||
}
|
||||
contentTags[binderIdx] = contentTag;
|
||||
}
|
||||
|
||||
var view = new viewModule.DomView(
|
||||
protoView, viewRootNodes,
|
||||
boundTextNodes, boundElements, contentTags
|
||||
);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var element = boundElements[binderIdx];
|
||||
|
||||
// static child components
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createView(binder.nestedProtoView, null);
|
||||
ViewFactory.setComponentView(this._shadowDomStrategy, view, binderIdx, childView);
|
||||
}
|
||||
|
||||
// events
|
||||
if (isPresent(binder.eventLocals) && isPresent(binder.localEvents)) {
|
||||
for (var i = 0; i < binder.localEvents.length; i++) {
|
||||
this._createEventListener(view, element, binderIdx, binder.localEvents[i].name, binder.eventLocals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
_createEventListener(view, element, elementIndex, eventName, eventLocals) {
|
||||
this._eventManager.addEventListener(element, eventName, (event) => {
|
||||
view.dispatchEvent(elementIndex, eventName, event);
|
||||
});
|
||||
}
|
||||
|
||||
// This method is used by the ViewFactory and the ViewHydrator
|
||||
// TODO(tbosch): change shadow dom emulation so that LightDom
|
||||
// instances don't need to be recreated by instead hydrated/dehydrated
|
||||
static setComponentView(shadowDomStrategy:ShadowDomStrategy, hostView:viewModule.DomView, elementIndex:number, componentView:viewModule.DomView) {
|
||||
var element = hostView.boundElements[elementIndex];
|
||||
var lightDom = shadowDomStrategy.constructLightDom(hostView, componentView, element);
|
||||
shadowDomStrategy.attachTemplate(element, componentView);
|
||||
hostView.lightDoms[elementIndex] = lightDom;
|
||||
hostView.componentChildViews[elementIndex] = componentView;
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as ldModule from '../shadow_dom/light_dom';
|
||||
import {EventManager} from '../events/event_manager';
|
||||
import {ViewFactory} from './view_factory';
|
||||
import * as vcModule from './view_container';
|
||||
import * as viewModule from './view';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
|
||||
/**
|
||||
* A dehydrated view is a state of the view that allows it to be moved around
|
||||
* the view tree.
|
||||
*
|
||||
* A dehydrated view has the following properties:
|
||||
*
|
||||
* - all viewcontainers are empty.
|
||||
*
|
||||
* A call to hydrate/dehydrate is called whenever a view is attached/detached,
|
||||
* but it does not do the attach/detach itself.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RenderViewHydrator {
|
||||
_eventManager:EventManager;
|
||||
_viewFactory:ViewFactory;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
|
||||
constructor(eventManager:EventManager, viewFactory:ViewFactory, shadowDomStrategy:ShadowDomStrategy) {
|
||||
this._eventManager = eventManager;
|
||||
this._viewFactory = viewFactory;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
hydrateDynamicComponentView(hostView:viewModule.DomView, boundElementIndex:number, componentView:viewModule.DomView) {
|
||||
ViewFactory.setComponentView(this._shadowDomStrategy, hostView, boundElementIndex, componentView);
|
||||
var lightDom = hostView.lightDoms[boundElementIndex];
|
||||
this._viewHydrateRecurse(componentView, lightDom);
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
dehydrateDynamicComponentView(parentView:viewModule.DomView, boundElementIndex:number) {
|
||||
throw new BaseException('Not supported yet');
|
||||
// Something along these lines:
|
||||
// var componentView = parentView.componentChildViews[boundElementIndex];
|
||||
// vcModule.ViewContainer.removeViewNodes(componentView);
|
||||
// parentView.componentChildViews[boundElementIndex] = null;
|
||||
// parentView.lightDoms[boundElementIndex] = null;
|
||||
// this._viewDehydrateRecurse(componentView);
|
||||
}
|
||||
|
||||
hydrateInPlaceHostView(parentView:viewModule.DomView, hostView:viewModule.DomView) {
|
||||
if (isPresent(parentView)) {
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
this._viewHydrateRecurse(hostView, null);
|
||||
}
|
||||
|
||||
dehydrateInPlaceHostView(parentView:viewModule.DomView, hostView:viewModule.DomView) {
|
||||
if (isPresent(parentView)) {
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
vcModule.ViewContainer.removeViewNodes(hostView);
|
||||
hostView.rootNodes = [];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
}
|
||||
|
||||
hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.DomView) {
|
||||
this._viewHydrateRecurse(view, viewContainer.parentView.hostLightDom);
|
||||
}
|
||||
|
||||
dehydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.DomView) {
|
||||
this._viewDehydrateRecurse(view);
|
||||
}
|
||||
|
||||
_viewHydrateRecurse(view, hostLightDom: ldModule.LightDom) {
|
||||
if (view.hydrated) throw new BaseException('The view is already hydrated.');
|
||||
view.hydrated = true;
|
||||
view.hostLightDom = hostLightDom;
|
||||
|
||||
// content tags
|
||||
for (var i = 0; i < view.contentTags.length; i++) {
|
||||
var destLightDom = view.getDirectParentLightDom(i);
|
||||
var ct = view.contentTags[i];
|
||||
if (isPresent(ct)) {
|
||||
ct.hydrate(destLightDom);
|
||||
}
|
||||
}
|
||||
|
||||
// componentChildViews
|
||||
for (var i = 0; i < view.componentChildViews.length; i++) {
|
||||
var cv = view.componentChildViews[i];
|
||||
if (isPresent(cv)) {
|
||||
this._viewHydrateRecurse(cv, view.lightDoms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < view.lightDoms.length; ++i) {
|
||||
var lightDom = view.lightDoms[i];
|
||||
if (isPresent(lightDom)) {
|
||||
lightDom.redistribute();
|
||||
}
|
||||
}
|
||||
|
||||
//add global events
|
||||
view.eventHandlerRemovers = ListWrapper.create();
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
if (isPresent(binder.globalEvents)) {
|
||||
for (var i = 0; i < binder.globalEvents.length; i++) {
|
||||
var globalEvent = binder.globalEvents[i];
|
||||
var remover = this._createGlobalEventListener(view, binderIdx, globalEvent.name, globalEvent.target, globalEvent.fullName);
|
||||
ListWrapper.push(view.eventHandlerRemovers, remover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_createGlobalEventListener(view, elementIndex, eventName, eventTarget, fullName): Function {
|
||||
return this._eventManager.addGlobalEventListener(eventTarget, eventName, (event) => {
|
||||
view.dispatchEvent(elementIndex, fullName, event);
|
||||
});
|
||||
}
|
||||
|
||||
_viewDehydrateRecurse(view) {
|
||||
// Note: preserve the opposite order of the hydration process.
|
||||
|
||||
// componentChildViews
|
||||
for (var i = 0; i < view.componentChildViews.length; i++) {
|
||||
var cv = view.componentChildViews[i];
|
||||
if (isPresent(cv)) {
|
||||
this._viewDehydrateRecurse(cv);
|
||||
if (view.proto.elementBinders[i].hasDynamicComponent()) {
|
||||
vcModule.ViewContainer.removeViewNodes(cv);
|
||||
this._viewFactory.returnView(cv);
|
||||
view.lightDoms[i] = null;
|
||||
view.componentChildViews[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imperativeHostViews
|
||||
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||
var hostView = view.imperativeHostViews[i];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
vcModule.ViewContainer.removeViewNodes(hostView);
|
||||
hostView.rootNodes = [];
|
||||
this._viewFactory.returnView(hostView);
|
||||
}
|
||||
view.imperativeHostViews = [];
|
||||
|
||||
|
||||
// viewContainers and content tags
|
||||
if (isPresent(view.viewContainers)) {
|
||||
for (var i = 0; i < view.viewContainers.length; i++) {
|
||||
var vc = view.viewContainers[i];
|
||||
if (isPresent(vc)) {
|
||||
this._viewContainerDehydrateRecurse(vc);
|
||||
}
|
||||
var ct = view.contentTags[i];
|
||||
if (isPresent(ct)) {
|
||||
ct.dehydrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove global events
|
||||
for (var i = 0; i < view.eventHandlerRemovers.length; i++) {
|
||||
view.eventHandlerRemovers[i]();
|
||||
}
|
||||
|
||||
view.hostLightDom = null;
|
||||
view.eventHandlerRemovers = null;
|
||||
view.setEventDispatcher(null);
|
||||
view.hydrated = false;
|
||||
}
|
||||
|
||||
_viewContainerDehydrateRecurse(viewContainer) {
|
||||
for (var i=0; i<viewContainer.views.length; i++) {
|
||||
var view = viewContainer.views[i];
|
||||
this._viewDehydrateRecurse(view);
|
||||
this._viewFactory.returnView(view);
|
||||
}
|
||||
viewContainer.clear();
|
||||
}
|
||||
|
||||
}
|
10
modules/angular2/src/test_lib/test_bed.js
vendored
10
modules/angular2/src/test_lib/test_bed.js
vendored
@ -15,6 +15,9 @@ import {DynamicComponentLoader, ComponentRef} from 'angular2/src/core/compiler/d
|
||||
import {queryView, viewRootNodes, el} from './utils';
|
||||
import {instantiateType, getTypeOf} from './lang_utils';
|
||||
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/test
|
||||
*/
|
||||
@ -87,9 +90,12 @@ export class TestBed {
|
||||
this.setInlineTemplate(component, html);
|
||||
}
|
||||
|
||||
var rootEl = el('<div></div>');
|
||||
var doc = this._injector.get(DOCUMENT_TOKEN);
|
||||
var rootEl = el('<div id="root"></div>');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
|
||||
var componentBinding = bind(component).toValue(context);
|
||||
return this._injector.get(DynamicComponentLoader).loadIntoNewLocation(componentBinding, null, rootEl, this._injector).then((hostComponentRef) => {
|
||||
return this._injector.get(DynamicComponentLoader).loadIntoNewLocation(componentBinding, null, '#root', this._injector).then((hostComponentRef) => {
|
||||
return new ViewProxy(hostComponentRef);
|
||||
});
|
||||
}
|
||||
|
22
modules/angular2/src/test_lib/test_injector.js
vendored
22
modules/angular2/src/test_lib/test_injector.js
vendored
@ -20,8 +20,6 @@ import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {appDocumentToken} from 'angular2/src/core/application_tokens';
|
||||
|
||||
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||
|
||||
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
|
||||
@ -40,10 +38,8 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {RenderCompiler, 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 * as rvh from 'angular2/src/render/dom/view/view_hydrator';
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
|
||||
/**
|
||||
* Returns the root injector bindings.
|
||||
@ -76,16 +72,14 @@ function _getAppBindings() {
|
||||
}
|
||||
|
||||
return [
|
||||
bind(appDocumentToken).toValue(appDoc),
|
||||
bind(DOCUMENT_TOKEN).toValue(appDoc),
|
||||
bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||
[StyleUrlResolver, appDocumentToken]),
|
||||
bind(DirectDomRenderer).toClass(DirectDomRenderer),
|
||||
bind(Renderer).toClass(DirectDomRenderer),
|
||||
bind(RenderCompiler).toClass(rc.DefaultDomCompiler),
|
||||
rvf.ViewFactory,
|
||||
rvh.RenderViewHydrator,
|
||||
bind(rvf.VIEW_POOL_CAPACITY).toValue(500),
|
||||
[StyleUrlResolver, DOCUMENT_TOKEN]),
|
||||
DomRenderer,
|
||||
DefaultDomCompiler,
|
||||
bind(Renderer).toAlias(DomRenderer),
|
||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||
ProtoViewFactory,
|
||||
AppViewPool,
|
||||
AppViewManager,
|
||||
|
3
modules/angular2/src/test_lib/utils.js
vendored
3
modules/angular2/src/test_lib/utils.js
vendored
@ -1,6 +1,7 @@
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||
|
||||
export class Log {
|
||||
_result:List;
|
||||
@ -25,7 +26,7 @@ export class Log {
|
||||
}
|
||||
|
||||
export function viewRootNodes(view):List {
|
||||
return view.render.delegate.rootNodes;
|
||||
return resolveInternalDomView(view.render).rootNodes;
|
||||
}
|
||||
|
||||
export function queryView(view, selector:string) {
|
||||
|
Reference in New Issue
Block a user