refactor(view_manager): split `inPlace` views into root and free host views.
BREAKING CHANGE: `AppViewManager.createInPlaceHostView` is replaced by `AppViewManager.createRootHostView` (for bootstrap) and `AppViewManager.createFreeHostView` (for imperative components). The later creates new host elements that are not attached anywhere. To attach them, use `DomRenderer.getHostElement(hostviewRef)` to get the host element. Closes #1920
This commit is contained in:
parent
a38a0d6f87
commit
421d8916a6
|
@ -54,13 +54,10 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
return [
|
return [
|
||||||
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
||||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
||||||
metadataReader, testability, registry) => {
|
testability, registry) => {
|
||||||
|
|
||||||
var annotation = metadataReader.resolve(appComponentType);
|
|
||||||
|
|
||||||
var selector = annotation.selector;
|
|
||||||
// TODO(rado): investigate whether to support bindings on root component.
|
// TODO(rado): investigate whether to support bindings on root component.
|
||||||
return dynamicComponentLoader.loadIntoNewLocation(appComponentType, null, selector, injector).then( (componentRef) => {
|
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector).then( (componentRef) => {
|
||||||
var domView = resolveInternalDomView(componentRef.hostView.render);
|
var domView = resolveInternalDomView(componentRef.hostView.render);
|
||||||
// We need to do this here to ensure that we create Testability and
|
// We need to do this here to ensure that we create Testability and
|
||||||
// it's ready on the window for users.
|
// it's ready on the window for users.
|
||||||
|
@ -68,7 +65,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
|
|
||||||
return componentRef;
|
return componentRef;
|
||||||
});
|
});
|
||||||
}, [DynamicComponentLoader, Injector, DirectiveResolver,
|
}, [DynamicComponentLoader, Injector,
|
||||||
Testability, TestabilityRegistry]),
|
Testability, TestabilityRegistry]),
|
||||||
|
|
||||||
bind(appComponentType).toFactory((ref) => ref.instance,
|
bind(appComponentType).toFactory((ref) => ref.instance,
|
||||||
|
|
|
@ -62,19 +62,38 @@ export class DynamicComponentLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a component in the element specified by elementSelector. The loaded component receives
|
* Loads a root component that is placed at the first element that matches the
|
||||||
* injection normally as a hosted view.
|
* component's selector.
|
||||||
|
* The loaded component receives injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef, elementSelector:string,
|
loadAsRoot(typeOrBinding, overrideSelector = null, injector:Injector = null):Promise<ComponentRef> {
|
||||||
injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
||||||
var hostViewRef = this._viewManager.createInPlaceHostView(
|
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||||
parentComponentLocation, elementSelector, hostProtoViewRef, injector);
|
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
var dispose = () => {
|
var dispose = () => {
|
||||||
this._viewManager.destroyInPlaceHostView(parentComponentLocation, hostViewRef);
|
this._viewManager.destroyRootHostView(hostViewRef);
|
||||||
|
};
|
||||||
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a component into a free host view that is not yet attached to
|
||||||
|
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
|
||||||
|
* The loaded component receives injection normally as a hosted view.
|
||||||
|
*/
|
||||||
|
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef,
|
||||||
|
injector:Injector = null):Promise<ComponentRef> {
|
||||||
|
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
||||||
|
var hostViewRef = this._viewManager.createFreeHostView(
|
||||||
|
parentComponentLocation, hostProtoViewRef, injector);
|
||||||
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
|
var dispose = () => {
|
||||||
|
this._viewManager.destroyFreeHostView(parentComponentLocation, hostViewRef);
|
||||||
};
|
};
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class AppView {
|
||||||
componentChildViews: List<AppView>;
|
componentChildViews: List<AppView>;
|
||||||
/// Host views that were added by an imperative view.
|
/// Host views that were added by an imperative view.
|
||||||
/// This is a dynamically growing / shrinking array.
|
/// This is a dynamically growing / shrinking array.
|
||||||
inPlaceHostViews: List<AppView>;
|
freeHostViews: List<AppView>;
|
||||||
viewContainers: List<AppViewContainer>;
|
viewContainers: List<AppViewContainer>;
|
||||||
preBuiltObjects: List<PreBuiltObjects>;
|
preBuiltObjects: List<PreBuiltObjects>;
|
||||||
proto: AppProtoView;
|
proto: AppProtoView;
|
||||||
|
@ -64,7 +64,7 @@ export class AppView {
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.inPlaceHostViews = [];
|
this.freeHostViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
||||||
|
|
|
@ -62,33 +62,46 @@ export class AppViewManager {
|
||||||
return new ViewRef(componentView);
|
return new ViewRef(componentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
createInPlaceHostView(parentComponentLocation:ElementRef,
|
createRootHostView(hostProtoViewRef:ProtoViewRef, overrideSelector:string, injector:Injector):ViewRef {
|
||||||
hostElementSelector:string, hostProtoViewRef:ProtoViewRef, injector:Injector):ViewRef {
|
|
||||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||||
var parentComponentHostView = null;
|
var hostElementSelector = overrideSelector;
|
||||||
var parentComponentBoundElementIndex = null;
|
if (isBlank(hostElementSelector)) {
|
||||||
var parentRenderViewRef = null;
|
hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
|
||||||
if (isPresent(parentComponentLocation)) {
|
|
||||||
parentComponentHostView = internalView(parentComponentLocation.parentView);
|
|
||||||
parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
|
||||||
parentRenderViewRef = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex].render;
|
|
||||||
}
|
}
|
||||||
var hostRenderView = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostProtoView.render);
|
var renderView = this._renderer.createRootHostView(hostProtoView.render, hostElementSelector);
|
||||||
var hostView = this._utils.createView(hostProtoView, hostRenderView, this, this._renderer);
|
var hostView = this._utils.createView(hostProtoView, renderView, this, this._renderer);
|
||||||
this._renderer.setEventDispatcher(hostView.render, hostView);
|
this._renderer.setEventDispatcher(hostView.render, hostView);
|
||||||
this._createViewRecurse(hostView)
|
this._createViewRecurse(hostView);
|
||||||
this._utils.attachAndHydrateInPlaceHostView(parentComponentHostView, parentComponentBoundElementIndex, hostView, injector);
|
|
||||||
|
this._utils.hydrateRootHostView(hostView, injector);
|
||||||
this._viewHydrateRecurse(hostView);
|
this._viewHydrateRecurse(hostView);
|
||||||
return new ViewRef(hostView);
|
return new ViewRef(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyInPlaceHostView(parentComponentLocation:ElementRef, hostViewRef:ViewRef) {
|
destroyRootHostView(hostViewRef:ViewRef) {
|
||||||
|
// Note: Don't detach the hostView as we want to leave the
|
||||||
|
// root element in place. Also don't put the hostView into the view pool
|
||||||
|
// as it is depending on the element for which it was created.
|
||||||
var hostView = internalView(hostViewRef);
|
var hostView = internalView(hostViewRef);
|
||||||
var parentView = null;
|
// We do want to destroy the component view though.
|
||||||
if (isPresent(parentComponentLocation)) {
|
this._viewDehydrateRecurse(hostView, true);
|
||||||
parentView = internalView(parentComponentLocation.parentView).componentChildViews[parentComponentLocation.boundElementIndex];
|
this._renderer.destroyView(hostView.render);
|
||||||
}
|
}
|
||||||
this._destroyInPlaceHostView(parentView, hostView);
|
|
||||||
|
createFreeHostView(parentComponentLocation:ElementRef, hostProtoViewRef:ProtoViewRef, injector:Injector):ViewRef {
|
||||||
|
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||||
|
var hostView = this._createPooledView(hostProtoView);
|
||||||
|
var parentComponentHostView = internalView(parentComponentLocation.parentView);
|
||||||
|
var parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
||||||
|
this._utils.attachAndHydrateFreeHostView(parentComponentHostView, parentComponentBoundElementIndex, hostView, injector);
|
||||||
|
this._viewHydrateRecurse(hostView);
|
||||||
|
return new ViewRef(hostView);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyFreeHostView(parentComponentLocation:ElementRef, hostViewRef:ViewRef) {
|
||||||
|
var hostView = internalView(hostViewRef);
|
||||||
|
var parentView = internalView(parentComponentLocation.parentView).componentChildViews[parentComponentLocation.boundElementIndex];
|
||||||
|
this._destroyFreeHostView(parentView, hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
createViewInContainer(viewContainerLocation:ElementRef,
|
createViewInContainer(viewContainerLocation:ElementRef,
|
||||||
|
@ -186,16 +199,11 @@ export class AppViewManager {
|
||||||
this._destroyPooledView(componentView);
|
this._destroyPooledView(componentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyInPlaceHostView(parentView, hostView) {
|
_destroyFreeHostView(parentView, hostView) {
|
||||||
var parentRenderViewRef = null;
|
|
||||||
if (isPresent(parentView)) {
|
|
||||||
parentRenderViewRef = parentView.render;
|
|
||||||
}
|
|
||||||
this._viewDehydrateRecurse(hostView, true);
|
this._viewDehydrateRecurse(hostView, true);
|
||||||
this._utils.detachInPlaceHostView(parentView, hostView);
|
this._renderer.detachFreeHostView(parentView.render, hostView.render);
|
||||||
this._renderer.destroyInPlaceHostView(parentRenderViewRef, hostView.render);
|
this._utils.detachFreeHostView(parentView, hostView);
|
||||||
// Note: Don't put the inplace host view into the view pool
|
this._destroyPooledView(hostView);
|
||||||
// as it is depending on the element for which it was created.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewHydrateRecurse(
|
_viewHydrateRecurse(
|
||||||
|
@ -234,10 +242,10 @@ export class AppViewManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// inPlaceHostViews
|
// freeHostViews
|
||||||
for (var i = view.inPlaceHostViews.length-1; i>=0; i--) {
|
for (var i = view.freeHostViews.length-1; i>=0; i--) {
|
||||||
var hostView = view.inPlaceHostViews[i];
|
var hostView = view.freeHostViews[i];
|
||||||
this._destroyInPlaceHostView(view, hostView);
|
this._destroyFreeHostView(view, hostView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,24 +93,23 @@ export class AppViewManagerUtils {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachAndHydrateInPlaceHostView(parentComponentHostView:viewModule.AppView, parentComponentBoundElementIndex:number,
|
hydrateRootHostView(hostView:viewModule.AppView, injector:Injector = null) {
|
||||||
|
this._hydrateView(hostView, injector, null, new Object(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
attachAndHydrateFreeHostView(parentComponentHostView:viewModule.AppView, parentComponentBoundElementIndex:number,
|
||||||
hostView:viewModule.AppView, injector:Injector = null) {
|
hostView:viewModule.AppView, injector:Injector = null) {
|
||||||
var hostElementInjector = null;
|
var hostElementInjector = parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
||||||
if (isPresent(parentComponentHostView)) {
|
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
||||||
hostElementInjector = parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||||
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
ListWrapper.push(parentView.freeHostViews, hostView);
|
||||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
|
||||||
ListWrapper.push(parentView.inPlaceHostViews, hostView);
|
|
||||||
}
|
|
||||||
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
detachInPlaceHostView(parentView:viewModule.AppView,
|
detachFreeHostView(parentView:viewModule.AppView,
|
||||||
hostView:viewModule.AppView) {
|
hostView:viewModule.AppView) {
|
||||||
if (isPresent(parentView)) {
|
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
ListWrapper.remove(parentView.freeHostViews, hostView);
|
||||||
ListWrapper.remove(parentView.inPlaceHostViews, hostView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
|
attachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
|
||||||
|
|
|
@ -187,20 +187,19 @@ export class RenderCompiler {
|
||||||
|
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
/**
|
/**
|
||||||
* Creates a host view that includes the given element.
|
* Creates a root host view that includes the given element.
|
||||||
* @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
|
* @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE
|
||||||
|
* @param {any} hostElementSelector css selector for the host element (will be queried against the main document)
|
||||||
* @return {RenderViewRef} the created view
|
* @return {RenderViewRef} the created view
|
||||||
*/
|
*/
|
||||||
createInPlaceHostView(parentHostViewRef:RenderViewRef, hostElementSelector:string, hostProtoViewRef:RenderProtoViewRef):RenderViewRef {
|
createRootHostView(hostProtoViewRef:RenderProtoViewRef, hostElementSelector:string):RenderViewRef {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys the given host view in the given parent view.
|
* Detaches a free host view's element from the DOM.
|
||||||
*/
|
*/
|
||||||
destroyInPlaceHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
detachFreeHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,8 +19,6 @@ import {Renderer, RenderProtoViewRef, RenderViewRef} from '../api';
|
||||||
// const expressions!
|
// const expressions!
|
||||||
export const DOCUMENT_TOKEN = 'DocumentToken';
|
export const DOCUMENT_TOKEN = 'DocumentToken';
|
||||||
|
|
||||||
var _DOCUMENT_SELECTOR_REGEX = RegExpWrapper.create('\\:document(.+)');
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DomRenderer extends Renderer {
|
export class DomRenderer extends Renderer {
|
||||||
_eventManager:EventManager;
|
_eventManager:EventManager;
|
||||||
|
@ -34,27 +32,16 @@ export class DomRenderer extends Renderer {
|
||||||
this._document = document;
|
this._document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
createInPlaceHostView(parentHostViewRef:RenderViewRef, hostElementSelector:string, hostProtoViewRef:RenderProtoViewRef):RenderViewRef {
|
createRootHostView(hostProtoViewRef:RenderProtoViewRef, hostElementSelector:string):RenderViewRef {
|
||||||
var containerNode;
|
var hostProtoView = resolveInternalDomProtoView(hostProtoViewRef);
|
||||||
var documentSelectorMatch = RegExpWrapper.firstMatch(_DOCUMENT_SELECTOR_REGEX, hostElementSelector);
|
var element = DOM.querySelector(this._document, 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)) {
|
if (isBlank(element)) {
|
||||||
throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`);
|
throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`);
|
||||||
}
|
}
|
||||||
var hostProtoView = resolveInternalDomProtoView(hostProtoViewRef);
|
|
||||||
return new DomViewRef(this._createView(hostProtoView, element));
|
return new DomViewRef(this._createView(hostProtoView, element));
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyInPlaceHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
detachFreeHostView(parentHostViewRef:RenderViewRef, hostViewRef:RenderViewRef) {
|
||||||
var hostView = resolveInternalDomView(hostViewRef);
|
var hostView = resolveInternalDomView(hostViewRef);
|
||||||
this._removeViewNodes(hostView);
|
this._removeViewNodes(hostView);
|
||||||
}
|
}
|
||||||
|
@ -89,6 +76,11 @@ export class DomRenderer extends Renderer {
|
||||||
this._moveViewNodesIntoParent(componentView.shadowRoot, componentView);
|
this._moveViewNodesIntoParent(componentView.shadowRoot, componentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHostElement(hostViewRef:RenderViewRef) {
|
||||||
|
var hostView = resolveInternalDomView(hostViewRef);
|
||||||
|
return hostView.boundElements[0];
|
||||||
|
}
|
||||||
|
|
||||||
detachComponentView(hostViewRef:RenderViewRef, boundElementIndex:number, componentViewRef:RenderViewRef) {
|
detachComponentView(hostViewRef:RenderViewRef, boundElementIndex:number, componentViewRef:RenderViewRef) {
|
||||||
var hostView = resolveInternalDomView(hostViewRef);
|
var hostView = resolveInternalDomView(hostViewRef);
|
||||||
var componentView = resolveInternalDomView(componentViewRef);
|
var componentView = resolveInternalDomView(componentViewRef);
|
||||||
|
|
|
@ -94,7 +94,7 @@ export class TestBed {
|
||||||
DOM.appendChild(doc.body, rootEl);
|
DOM.appendChild(doc.body, rootEl);
|
||||||
|
|
||||||
var componentBinding = bind(component).toValue(context);
|
var componentBinding = bind(component).toValue(context);
|
||||||
return this._injector.get(DynamicComponentLoader).loadIntoNewLocation(componentBinding, null, '#root', this._injector).then((hostComponentRef) => {
|
return this._injector.get(DynamicComponentLoader).loadAsRoot(componentBinding,'#root', this._injector).then((hostComponentRef) => {
|
||||||
return new ViewProxy(hostComponentRef);
|
return new ViewProxy(hostComponentRef);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,18 @@ import {
|
||||||
inject,
|
inject,
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit
|
xit,
|
||||||
|
viewRootNodes
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
||||||
|
import {Injector} from 'angular2/di';
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
import {NgIf} from 'angular2/src/directives/ng_if';
|
import {NgIf} from 'angular2/src/directives/ng_if';
|
||||||
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
|
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
|
||||||
|
@ -193,6 +194,37 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('loadAsRoot', () => {
|
||||||
|
|
||||||
|
it('should allow to create, update and destroy components',
|
||||||
|
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector], (tb, async, dcl, doc, injector) => {
|
||||||
|
var rootEl = el('<child-cmp></child-cmp>');
|
||||||
|
DOM.appendChild(doc.body, rootEl);
|
||||||
|
dcl.loadAsRoot(ChildComp, null, injector).then( (componentRef) => {
|
||||||
|
var view = new ViewProxy(componentRef);
|
||||||
|
expect(rootEl.parentNode).toBe(doc.body);
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('hello');
|
||||||
|
|
||||||
|
componentRef.instance.ctxProp = 'new';
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('new');
|
||||||
|
|
||||||
|
componentRef.dispose();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('');
|
||||||
|
expect(rootEl.parentNode).toBe(doc.body);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +232,6 @@ export function main() {
|
||||||
selector: 'imp-ng-cmp'
|
selector: 'imp-ng-cmp'
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
renderer: 'imp-ng-cmp-renderer',
|
|
||||||
template: ''
|
template: ''
|
||||||
})
|
})
|
||||||
class ImperativeViewComponentUsingNgComponent {
|
class ImperativeViewComponentUsingNgComponent {
|
||||||
|
@ -210,7 +241,11 @@ class ImperativeViewComponentUsingNgComponent {
|
||||||
var div = el('<div id="impHost"></div>');
|
var div = el('<div id="impHost"></div>');
|
||||||
var shadowViewRef = viewManager.getComponentView(self);
|
var shadowViewRef = viewManager.getComponentView(self);
|
||||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
||||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, '#impHost', null);
|
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, null).then( (componentRef) => {
|
||||||
|
var element = renderer.getHostElement(componentRef.hostView.render);
|
||||||
|
DOM.appendChild(div, element);
|
||||||
|
return componentRef;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||||
});
|
});
|
||||||
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, _c) => {
|
renderer.spy('createRootHostView').andCallFake( (_b, _c) => {
|
||||||
var rv = new RenderViewRef();
|
var rv = new RenderViewRef();
|
||||||
ListWrapper.push(createdRenderViews, rv);
|
ListWrapper.push(createdRenderViews, rv);
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -294,7 +294,7 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createInPlaceHostView', () => {
|
describe('createFreeHostView', () => {
|
||||||
|
|
||||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||||
// this is using the same mechanism as the other methods...
|
// this is using the same mechanism as the other methods...
|
||||||
|
@ -314,27 +314,26 @@ export function main() {
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
expect(
|
expect(
|
||||||
internalView(manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null))
|
internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null))
|
||||||
).toBe(createdViews[0]);
|
).toBe(createdViews[0]);
|
||||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should attachAndHydrate the view', () => {
|
it('should attachAndHydrate the view', () => {
|
||||||
var injector = new Injector([], null, false);
|
var injector = new Injector([], null, false);
|
||||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), injector);
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), injector);
|
||||||
expect(utils.spy('attachAndHydrateInPlaceHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
expect(utils.spy('attachAndHydrateFreeHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create and set the render view', () => {
|
it('should create and set the render view', () => {
|
||||||
var elementOrSelector = 'someSelector';
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)
|
||||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), elementOrSelector, wrapPv(hostProtoView), null)
|
expect(renderer.spy('createView')).toHaveBeenCalledWith(hostProtoView.render);
|
||||||
expect(renderer.spy('createInPlaceHostView')).toHaveBeenCalledWith(parentView.render, elementOrSelector, hostProtoView.render);
|
|
||||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null);
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null);
|
||||||
var cmpView = createdViews[0];
|
var cmpView = createdViews[0];
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||||
});
|
});
|
||||||
|
@ -343,7 +342,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('destroyInPlaceHostView', () => {
|
describe('destroyFreeHostView', () => {
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
|
@ -355,29 +354,29 @@ export function main() {
|
||||||
hostProtoView = createProtoView(
|
hostProtoView = createProtoView(
|
||||||
[createComponentElBinder(null)]
|
[createComponentElBinder(null)]
|
||||||
);
|
);
|
||||||
hostView = internalView(manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), null));
|
hostView = internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null));
|
||||||
hostRenderViewRef = hostView.render;
|
hostRenderViewRef = hostView.render;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detach', () => {
|
it('should detach', () => {
|
||||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||||
expect(utils.spy('detachInPlaceHostView')).toHaveBeenCalledWith(parentView, hostView);
|
expect(utils.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dehydrate', () => {
|
it('should dehydrate', () => {
|
||||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should destroy and clear the render view', () => {
|
it('should detach the render view', () => {
|
||||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||||
expect(renderer.spy('destroyInPlaceHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
expect(renderer.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not return the view to the pool', () => {
|
it('should return the view to the pool', () => {
|
||||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -387,6 +386,78 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('createRootHostView', () => {
|
||||||
|
|
||||||
|
var hostProtoView;
|
||||||
|
beforeEach( () => {
|
||||||
|
hostProtoView = createProtoView(
|
||||||
|
[createComponentElBinder(null)]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the view', () => {
|
||||||
|
expect(
|
||||||
|
internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null))
|
||||||
|
).toBe(createdViews[0]);
|
||||||
|
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hydrate the view', () => {
|
||||||
|
var injector = new Injector([], null, false);
|
||||||
|
manager.createRootHostView(wrapPv(hostProtoView), null, injector);
|
||||||
|
expect(utils.spy('hydrateRootHostView')).toHaveBeenCalledWith(createdViews[0], injector);
|
||||||
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create and set the render view using the component selector', () => {
|
||||||
|
manager.createRootHostView(wrapPv(hostProtoView), null, null)
|
||||||
|
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, 'someComponent');
|
||||||
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to override the selector', () => {
|
||||||
|
var selector = 'someOtherSelector';
|
||||||
|
manager.createRootHostView(wrapPv(hostProtoView), selector, null)
|
||||||
|
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, selector);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the event dispatcher', () => {
|
||||||
|
manager.createRootHostView(wrapPv(hostProtoView), null, null);
|
||||||
|
var cmpView = createdViews[0];
|
||||||
|
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('destroyRootHostView', () => {
|
||||||
|
var hostProtoView, hostView, hostRenderViewRef;
|
||||||
|
beforeEach( () => {
|
||||||
|
hostProtoView = createProtoView(
|
||||||
|
[createComponentElBinder(null)]
|
||||||
|
);
|
||||||
|
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||||
|
hostRenderViewRef = hostView.render;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dehydrate', () => {
|
||||||
|
manager.destroyRootHostView(wrapView(hostView));
|
||||||
|
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||||
|
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy the render view', () => {
|
||||||
|
manager.destroyRootHostView(wrapView(hostView));
|
||||||
|
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return the view to the pool', () => {
|
||||||
|
manager.destroyRootHostView(wrapView(hostView));
|
||||||
|
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('createViewInContainer', () => {
|
describe('createViewInContainer', () => {
|
||||||
|
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
|
@ -483,19 +554,19 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dehydrate', () => {
|
it('should dehydrate', () => {
|
||||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
manager.destroyRootHostView(wrapView(parentView));
|
||||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detach', () => {
|
it('should detach', () => {
|
||||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
manager.destroyRootHostView(wrapView(parentView));
|
||||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the view to the pool', () => {
|
it('should return the view to the pool', () => {
|
||||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
manager.destroyRootHostView(wrapView(parentView));
|
||||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils
|
||||||
export function main() {
|
export function main() {
|
||||||
// TODO(tbosch): add more tests here!
|
// TODO(tbosch): add more tests here!
|
||||||
|
|
||||||
|
|
||||||
describe('AppViewManagerUtils', () => {
|
describe('AppViewManagerUtils', () => {
|
||||||
|
|
||||||
var directiveResolver;
|
var directiveResolver;
|
||||||
|
@ -170,7 +169,7 @@ export function main() {
|
||||||
var shadowView = createView();
|
var shadowView = createView();
|
||||||
utils.attachComponentView(hostView, 0, shadowView);
|
utils.attachComponentView(hostView, 0, shadowView);
|
||||||
|
|
||||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
utils.hydrateRootHostView(hostView, createInjector());
|
||||||
|
|
||||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||||
|
@ -200,7 +199,7 @@ export function main() {
|
||||||
var shadowView = createView();
|
var shadowView = createView();
|
||||||
utils.attachComponentView(hostView, 0, shadowView);
|
utils.attachComponentView(hostView, 0, shadowView);
|
||||||
|
|
||||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
utils.hydrateRootHostView(hostView, createInjector());
|
||||||
|
|
||||||
expect(spyActionAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
expect(spyActionAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||||
expect(spyActionAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
expect(spyActionAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||||
|
@ -268,6 +267,27 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hydrateRootHostView', () => {
|
||||||
|
var hostView;
|
||||||
|
|
||||||
|
function createViews() {
|
||||||
|
var hostPv = createProtoView([
|
||||||
|
createComponentElBinder()
|
||||||
|
]);
|
||||||
|
hostView = createView(hostPv);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should instantiate the elementInjectors with the given injector and an empty host element injector", () => {
|
||||||
|
var injector = createInjector();
|
||||||
|
createViews();
|
||||||
|
|
||||||
|
utils.hydrateRootHostView(hostView, injector);
|
||||||
|
expect(hostView.rootElementInjectors[0].spy('instantiateDirectives'))
|
||||||
|
.toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {DomTestbed} from './dom_testbed';
|
import {DomTestbed, TestView} from './dom_testbed';
|
||||||
|
|
||||||
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
|
||||||
|
@ -27,15 +27,29 @@ export function main() {
|
||||||
DomTestbed
|
DomTestbed
|
||||||
]);
|
]);
|
||||||
|
|
||||||
it('should create and destroy host views while using the given elements in place',
|
it('should create and destroy root host views while using the given elements in place',
|
||||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
tb.compileAll([someComponent]).then( (protoViewDtos) => {
|
tb.compiler.compileHost(someComponent).then( (hostProtoViewDto) => {
|
||||||
var view = tb.createRootView(protoViewDtos[0]);
|
var view = new TestView(tb.renderer.createRootHostView(hostProtoViewDto.render, '#root'));
|
||||||
expect(tb.rootEl.parentNode).toBeTruthy();
|
|
||||||
expect(view.rawView.rootNodes[0]).toEqual(tb.rootEl);
|
expect(view.rawView.rootNodes[0]).toEqual(tb.rootEl);
|
||||||
|
|
||||||
tb.renderer.destroyInPlaceHostView(null, view.viewRef);
|
tb.renderer.destroyView(view.viewRef);
|
||||||
expect(tb.rootEl.parentNode).toBeFalsy();
|
// destroying a root view should not disconnect it!
|
||||||
|
expect(tb.rootEl.parentNode).toBeTruthy();
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create and destroy free host views',
|
||||||
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
|
tb.compiler.compileHost(someComponent).then( (hostProtoViewDto) => {
|
||||||
|
var view = new TestView(tb.renderer.createView(hostProtoViewDto.render));
|
||||||
|
var hostElement = tb.renderer.getHostElement(view.viewRef);
|
||||||
|
DOM.appendChild(tb.rootEl, hostElement);
|
||||||
|
|
||||||
|
tb.renderer.detachFreeHostView(null, view.viewRef);
|
||||||
|
expect(DOM.parentElement(hostElement)).toBeFalsy();
|
||||||
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,7 +77,7 @@ export class DomTestbed {
|
||||||
}
|
}
|
||||||
|
|
||||||
createRootView(rootProtoView:ProtoViewDto):TestView {
|
createRootView(rootProtoView:ProtoViewDto):TestView {
|
||||||
var viewRef = this.renderer.createInPlaceHostView(null, '#root', rootProtoView.render);
|
var viewRef = this.renderer.createRootHostView(rootProtoView.render, '#root');
|
||||||
this.renderer.hydrateView(viewRef);
|
this.renderer.hydrateView(viewRef);
|
||||||
return this._createTestView(viewRef);
|
return this._createTestView(viewRef);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {DynamicComponentLoader, ElementRef, ComponentRef, onDestroy} from 'angular2/angular2';
|
import {DynamicComponentLoader, ElementRef, ComponentRef, onDestroy, DomRenderer} from 'angular2/angular2';
|
||||||
import {bind, Injector} from 'angular2/di';
|
import {bind, Injector} from 'angular2/di';
|
||||||
import {ObservableWrapper, Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {ObservableWrapper, Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {isPresent, Type} from 'angular2/src/facade/lang';
|
import {isPresent, Type} from 'angular2/src/facade/lang';
|
||||||
|
@ -12,7 +12,6 @@ import {Component, Directive} from 'angular2/src/core/annotations_impl/annotatio
|
||||||
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
|
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||||
|
|
||||||
|
|
||||||
// TODO(jelbourn): Opener of dialog can control where it is rendered.
|
// TODO(jelbourn): Opener of dialog can control where it is rendered.
|
||||||
// TODO(jelbourn): body scrolling is disabled while dialog is open.
|
// TODO(jelbourn): body scrolling is disabled while dialog is open.
|
||||||
// TODO(jelbourn): Don't manually construct and configure a DOM element. See #1402
|
// TODO(jelbourn): Don't manually construct and configure a DOM element. See #1402
|
||||||
|
@ -29,9 +28,11 @@ var _nextDialogId = 0;
|
||||||
*/
|
*/
|
||||||
export class MdDialog {
|
export class MdDialog {
|
||||||
componentLoader: DynamicComponentLoader;
|
componentLoader: DynamicComponentLoader;
|
||||||
|
domRenderer: DomRenderer;
|
||||||
|
|
||||||
constructor(loader: DynamicComponentLoader) {
|
constructor(loader: DynamicComponentLoader, domRenderer: DomRenderer) {
|
||||||
this.componentLoader = loader;
|
this.componentLoader = loader;
|
||||||
|
this.domRenderer = domRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,25 +48,6 @@ export class MdDialog {
|
||||||
options: MdDialogConfig = null): Promise<MdDialogRef> {
|
options: MdDialogConfig = null): Promise<MdDialogRef> {
|
||||||
var config = isPresent(options) ? options : new MdDialogConfig();
|
var config = isPresent(options) ? options : new MdDialogConfig();
|
||||||
|
|
||||||
// TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element
|
|
||||||
// directly on the document body (also needed for web workers stuff).
|
|
||||||
// Create a DOM node to serve as a physical host element for the dialog.
|
|
||||||
var dialogElement = this._createHostElement();
|
|
||||||
DOM.appendChild(DOM.query('body'), dialogElement);
|
|
||||||
|
|
||||||
// TODO(jelbourn): Use hostProperties binding to set these once #1539 is fixed.
|
|
||||||
// Configure properties on the host element.
|
|
||||||
DOM.addClass(dialogElement, 'md-dialog');
|
|
||||||
DOM.setAttribute(dialogElement, 'tabindex', '0');
|
|
||||||
|
|
||||||
// TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once ready.
|
|
||||||
if (isPresent(config.width)) {
|
|
||||||
DOM.setStyle(dialogElement, 'width', config.width);
|
|
||||||
}
|
|
||||||
if (isPresent(config.height)) {
|
|
||||||
DOM.setStyle(dialogElement, 'height', config.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the dialogRef here so that it can be injected into the content component.
|
// Create the dialogRef here so that it can be injected into the content component.
|
||||||
var dialogRef = new MdDialogRef();
|
var dialogRef = new MdDialogRef();
|
||||||
|
|
||||||
|
@ -76,7 +58,27 @@ export class MdDialog {
|
||||||
|
|
||||||
// First, load the MdDialogContainer, into which the given component will be loaded.
|
// First, load the MdDialogContainer, into which the given component will be loaded.
|
||||||
return this.componentLoader.loadIntoNewLocation(
|
return this.componentLoader.loadIntoNewLocation(
|
||||||
MdDialogContainer, elementRef, `:document#${dialogElement.id}`).then(containerRef => {
|
MdDialogContainer, elementRef).then(containerRef => {
|
||||||
|
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807)
|
||||||
|
// TODO(jelbourn): Don't use direct DOM access. Need abstraction to create an element
|
||||||
|
// directly on the document body (also needed for web workers stuff).
|
||||||
|
// Create a DOM node to serve as a physical host element for the dialog.
|
||||||
|
var dialogElement = this.domRenderer.getHostElement(containerRef.hostView.render);
|
||||||
|
DOM.appendChild(DOM.query('body'), dialogElement);
|
||||||
|
|
||||||
|
// TODO(jelbourn): Use hostProperties binding to set these once #1539 is fixed.
|
||||||
|
// Configure properties on the host element.
|
||||||
|
DOM.addClass(dialogElement, 'md-dialog');
|
||||||
|
DOM.setAttribute(dialogElement, 'tabindex', '0');
|
||||||
|
|
||||||
|
// TODO(jelbourn): Do this with hostProperties (or another rendering abstraction) once ready.
|
||||||
|
if (isPresent(config.width)) {
|
||||||
|
DOM.setStyle(dialogElement, 'width', config.width);
|
||||||
|
}
|
||||||
|
if (isPresent(config.height)) {
|
||||||
|
DOM.setStyle(dialogElement, 'height', config.height);
|
||||||
|
}
|
||||||
|
|
||||||
dialogRef.containerRef = containerRef;
|
dialogRef.containerRef = containerRef;
|
||||||
|
|
||||||
// Now load the given component into the MdDialogContainer.
|
// Now load the given component into the MdDialogContainer.
|
||||||
|
@ -102,18 +104,14 @@ export class MdDialog {
|
||||||
|
|
||||||
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */
|
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */
|
||||||
_openBackdrop(elementRef:ElementRef, injector: Injector): Promise<ComponentRef> {
|
_openBackdrop(elementRef:ElementRef, injector: Injector): Promise<ComponentRef> {
|
||||||
var backdropElement = this._createHostElement();
|
|
||||||
DOM.addClass(backdropElement, 'md-backdrop');
|
|
||||||
DOM.appendChild(DOM.query('body'), backdropElement);
|
|
||||||
|
|
||||||
return this.componentLoader.loadIntoNewLocation(
|
return this.componentLoader.loadIntoNewLocation(
|
||||||
MdBackdrop, elementRef, `:document#${backdropElement.id}`, injector);
|
MdBackdrop, elementRef, injector).then( (componentRef) => {
|
||||||
}
|
// TODO(tbosch): clean this up when we have custom renderers (https://github.com/angular/angular/issues/1807)
|
||||||
|
var backdropElement = this.domRenderer.getHostElement(componentRef.hostView.render);
|
||||||
_createHostElement() {
|
DOM.addClass(backdropElement, 'md-backdrop');
|
||||||
var hostElement = DOM.createElement('div');
|
DOM.appendChild(DOM.query('body'), backdropElement);
|
||||||
hostElement.id = `mdDialog${_nextDialogId++}`;
|
return componentRef;
|
||||||
return hostElement;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(message: string, okMessage: string): Promise {
|
alert(message: string, okMessage: string): Promise {
|
||||||
|
|
Loading…
Reference in New Issue