Major changes: - `compiler.compileRoot(el, type)` -> `compiler.compileInHost(type) + viewHydrator.hydrateHostViewInPlace(el, view)` - move all `hydrate`/`dehydrate` methods out of `View` and `ViewContainer` into a standalone class `view_hydrator` as private methods and provide new public methods dedicated to the individual use cases. Note: This PR does not change the current functionality, only moves it into different places. See design discussion in #1351, in preparation for imperative views.
193 lines
6.4 KiB
JavaScript
193 lines
6.4 KiB
JavaScript
import {Injectable} from 'angular2/di';
|
|
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
|
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
|
|
|
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';
|
|
|
|
/**
|
|
* 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;
|
|
|
|
constructor(eventManager:EventManager, viewFactory:ViewFactory) {
|
|
this._eventManager = eventManager;
|
|
this._viewFactory = viewFactory;
|
|
}
|
|
|
|
hydrateDynamicComponentView(hostView:viewModule.RenderView, boundElementIndex:number, componentView:viewModule.RenderView) {
|
|
this._viewFactory.setComponentView(hostView, boundElementIndex, componentView);
|
|
var lightDom = hostView.lightDoms[boundElementIndex];
|
|
this._viewHydrateRecurse(componentView, lightDom);
|
|
if (isPresent(lightDom)) {
|
|
lightDom.redistribute();
|
|
}
|
|
}
|
|
|
|
dehydrateDynamicComponentView(parentView:viewModule.RenderView, 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.RenderView, hostView:viewModule.RenderView) {
|
|
if (isPresent(parentView)) {
|
|
throw new BaseException('Not supported yet');
|
|
}
|
|
this._viewHydrateRecurse(hostView, null);
|
|
}
|
|
|
|
dehydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
|
if (isPresent(parentView)) {
|
|
throw new BaseException('Not supported yet');
|
|
}
|
|
this._viewDehydrateRecurse(hostView);
|
|
}
|
|
|
|
hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.RenderView) {
|
|
this._viewHydrateRecurse(view, viewContainer.hostLightDom);
|
|
}
|
|
|
|
dehydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.RenderView) {
|
|
this._viewDehydrateRecurse(view);
|
|
}
|
|
|
|
_getViewDestLightDom(view, binderIndex) {
|
|
var binder = view.proto.elementBinders[binderIndex];
|
|
var destLightDom = null;
|
|
if (binder.parentIndex !== -1 && binder.distanceToParent === 1) {
|
|
destLightDom = view.lightDoms[binder.parentIndex];
|
|
}
|
|
return destLightDom;
|
|
}
|
|
|
|
_viewHydrateRecurse(view, hostLightDom: ldModule.LightDom) {
|
|
if (view.hydrated) throw new BaseException('The view is already hydrated.');
|
|
view.hydrated = true;
|
|
|
|
// viewContainers and content tags
|
|
for (var i = 0; i < view.viewContainers.length; i++) {
|
|
var vc = view.viewContainers[i];
|
|
var destLightDom = this._getViewDestLightDom(view, i);
|
|
if (isPresent(vc)) {
|
|
this._viewContainerHydrateRecurse(vc, destLightDom, hostLightDom);
|
|
}
|
|
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);
|
|
view.lightDoms[i] = null;
|
|
view.componentChildViews[i] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.eventHandlerRemovers = null;
|
|
view.setEventDispatcher(null);
|
|
view.hydrated = false;
|
|
}
|
|
|
|
_viewContainerHydrateRecurse(viewContainer, destLightDom: ldModule.LightDom, hostLightDom: ldModule.LightDom) {
|
|
viewContainer.hydrated = true;
|
|
viewContainer.hostLightDom = hostLightDom;
|
|
viewContainer.lightDom = destLightDom;
|
|
}
|
|
|
|
_viewContainerDehydrateRecurse(viewContainer) {
|
|
for (var i=0; i<viewContainer.views.length; i++) {
|
|
this._viewDehydrateRecurse(viewContainer.views[i]);
|
|
}
|
|
viewContainer.clear();
|
|
|
|
viewContainer.hostLightDom = null;
|
|
viewContainer.lightDom = null;
|
|
viewContainer.hydrated = false;
|
|
}
|
|
|
|
}
|