feat(view): allow to transplant a view into a ViewContainer at another place.

Closes #1492.
This commit is contained in:
Tobias Bosch
2015-05-08 17:58:08 -07:00
parent 2185e7cee9
commit 4f3433b5bd
9 changed files with 199 additions and 34 deletions

View File

@ -88,7 +88,7 @@ export class DynamicComponentLoader {
var binding = this._getBinding(typeOrBinding);
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
var hostViewRef = viewContainer.create(hostProtoViewRef, viewContainer.length, injector);
var hostViewRef = viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
var newLocation = new ElementRef(hostViewRef, 0);
var component = this._viewManager.getComponent(newLocation);

View File

@ -40,9 +40,9 @@ export class ViewContainerRef {
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
create(protoViewRef:ProtoViewRef = null, atIndex:number=-1, injector:Injector = null): ViewRef {
create(protoViewRef:ProtoViewRef = null, atIndex:number=-1, context:ElementRef, injector:Injector = null): ViewRef {
if (atIndex == -1) atIndex = this.length;
return this._viewManager.createViewInContainer(this._element, atIndex, protoViewRef, injector);
return this._viewManager.createViewInContainer(this._element, atIndex, protoViewRef, context, injector);
}
insert(viewRef:ViewRef, atIndex:number=-1): ViewRef {
@ -68,4 +68,4 @@ export class ViewContainerRef {
if (atIndex == -1) atIndex = this.length - 1;
return this._viewManager.detachViewInContainer(this._element, atIndex);
}
}
}

View File

@ -92,16 +92,22 @@ export class AppViewManager {
}
createViewInContainer(viewContainerLocation:ElementRef,
atIndex:number, protoViewRef:ProtoViewRef, injector:Injector = null):ViewRef {
atIndex:number, protoViewRef:ProtoViewRef, context:ElementRef = null, injector:Injector = null):ViewRef {
var protoView = internalProtoView(protoViewRef);
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
var contextView = null;
var contextBoundElementIndex = null;
if (isPresent(context)) {
contextView = internalView(context.parentView);
contextBoundElementIndex = context.boundElementIndex;
}
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._utils.attachViewInContainer(parentView, boundElementIndex, contextView, contextBoundElementIndex, atIndex, view);
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView, contextBoundElementIndex, atIndex, injector);
this._viewHydrateRecurse(view);
return new ViewRef(view);
}
@ -116,7 +122,13 @@ export class AppViewManager {
var view = internalView(viewRef);
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
this._utils.attachViewInContainer(parentView, boundElementIndex, atIndex, view);
// TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
// are used for moving elements without the same container.
// We will change this into an atomic `move` operation, which should preserve the
// previous parent injector (see https://github.com/angular/angular/issues/1377).
// Right now we are destroying any special
// context view that might have been used.
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, atIndex, view);
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
return viewRef;
}

View File

@ -114,7 +114,12 @@ export class AppViewManagerUtils {
}
attachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
contextView:viewModule.AppView, contextBoundElementIndex:number,
atIndex:number, view:viewModule.AppView) {
if (isBlank(contextView)) {
contextView = parentView;
contextBoundElementIndex = boundElementIndex;
}
parentView.changeDetector.addChild(view.changeDetector);
var viewContainer = parentView.viewContainers[boundElementIndex];
if (isBlank(viewContainer)) {
@ -128,7 +133,7 @@ export class AppViewManagerUtils {
} else {
sibling = ListWrapper.last(viewContainer.views[atIndex - 1].rootElementInjectors)
}
var elementInjector = parentView.elementInjectors[boundElementIndex];
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
view.rootElementInjectors[i].linkAfter(elementInjector, sibling);
}
@ -145,11 +150,16 @@ export class AppViewManagerUtils {
}
hydrateViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
contextView:viewModule.AppView, contextBoundElementIndex:number,
atIndex:number, injector:Injector) {
if (isBlank(contextView)) {
contextView = parentView;
contextBoundElementIndex = boundElementIndex;
}
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[atIndex];
var elementInjector = parentView.elementInjectors[boundElementIndex];
this._hydrateView(view, injector, elementInjector, parentView.context, parentView.locals);
var elementInjector = contextView.elementInjectors[contextBoundElementIndex].getHost();
this._hydrateView(view, injector, elementInjector, contextView.context, contextView.locals);
}
hydrateDynamicComponentInElementInjector(hostView:viewModule.AppView, boundElementIndex:number,

View File

@ -37,7 +37,7 @@ export class RouterOutlet {
]);
this._viewContainer.clear();
this._viewContainer.create(pv, 0, outletInjector);
this._viewContainer.create(pv, 0, null, outletInjector);
});
}