feat(view): add imperative views
This commit is contained in:
8
modules/angular2/src/render/api.js
vendored
8
modules/angular2/src/render/api.js
vendored
@ -165,6 +165,14 @@ export class Renderer {
|
||||
*/
|
||||
createHostProtoView(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 RenderProtoView. Non recursive so that
|
||||
* we don't need to serialize all possible components over the wire,
|
||||
|
@ -12,6 +12,7 @@ import {Compiler} from './compiler/compiler';
|
||||
import {ShadowDomStrategy} from './shadow_dom/shadow_dom_strategy';
|
||||
import {ProtoViewBuilder} from './view/proto_view_builder';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ViewContainer} from './view/view_container';
|
||||
|
||||
function _resolveViewContainer(vc:api.ViewContainerRef) {
|
||||
return _resolveView(vc.view).getOrCreateViewContainer(vc.elementIndex);
|
||||
@ -90,6 +91,12 @@ export class DirectDomRenderer extends api.Renderer {
|
||||
return PromiseWrapper.resolve(hostProtoViewBuilder.build());
|
||||
}
|
||||
|
||||
createImperativeComponentProtoView(rendererId):Promise<api.ProtoViewDto> {
|
||||
var protoViewBuilder = new ProtoViewBuilder(null);
|
||||
protoViewBuilder.setImperativeRendererId(rendererId);
|
||||
return PromiseWrapper.resolve(protoViewBuilder.build());
|
||||
}
|
||||
|
||||
compile(template:api.ViewDefinition):Promise<api.ProtoViewDto> {
|
||||
// Note: compiler already uses a DirectDomProtoViewRef, so we don't
|
||||
// need to do anything here
|
||||
@ -156,6 +163,21 @@ export class DirectDomRenderer extends api.Renderer {
|
||||
this._viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
||||
}
|
||||
|
||||
setImperativeComponentRootNodes(parentViewRef:api.ViewRef, 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.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
||||
_resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue);
|
||||
}
|
||||
|
@ -11,15 +11,23 @@ export class RenderProtoView {
|
||||
elementBinders:List<ElementBinder>;
|
||||
isTemplateElement:boolean;
|
||||
rootBindingOffset:int;
|
||||
imperativeRendererId:string;
|
||||
|
||||
constructor({
|
||||
elementBinders,
|
||||
element
|
||||
element,
|
||||
imperativeRendererId
|
||||
}) {
|
||||
this.element = element;
|
||||
this.elementBinders = elementBinders;
|
||||
this.isTemplateElement = DOM.isTemplateElement(this.element);
|
||||
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
|
||||
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<RenderProtoView>) {
|
||||
|
@ -20,12 +20,18 @@ export class ProtoViewBuilder {
|
||||
rootElement;
|
||||
variableBindings: Map<string, string>;
|
||||
elements:List<ElementBinderBuilder>;
|
||||
isRootView:boolean;
|
||||
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 {
|
||||
@ -90,7 +96,8 @@ export class ProtoViewBuilder {
|
||||
return new api.ProtoViewDto({
|
||||
render: new directDomRenderer.DirectDomProtoViewRef(new RenderProtoView({
|
||||
element: this.rootElement,
|
||||
elementBinders: renderElementBinders
|
||||
elementBinders: renderElementBinders,
|
||||
imperativeRendererId: this.imperativeRendererId
|
||||
})),
|
||||
elementBinders: apiElementBinders,
|
||||
variableBindings: this.variableBindings
|
||||
|
6
modules/angular2/src/render/dom/view/view.js
vendored
6
modules/angular2/src/render/dom/view/view.js
vendored
@ -31,6 +31,9 @@ export class RenderView {
|
||||
hydrated: boolean;
|
||||
_eventDispatcher: any/*EventDispatcher*/;
|
||||
eventHandlerRemovers: List<Function>;
|
||||
/// Host views that were added by an imperative view.
|
||||
/// This is a dynamically growing / shrinking array.
|
||||
imperativeHostViews: List<RenderView>;
|
||||
|
||||
constructor(
|
||||
proto:RenderProtoView, rootNodes:List,
|
||||
@ -46,7 +49,8 @@ export class RenderView {
|
||||
this.componentChildViews = ListWrapper.createFixedSize(boundElements.length);
|
||||
this.hostLightDom = null;
|
||||
this.hydrated = false;
|
||||
this.eventHandlerRemovers = null;
|
||||
this.eventHandlerRemovers = [];
|
||||
this.imperativeHostViews = [];
|
||||
}
|
||||
|
||||
getDirectParentLightDom(boundElementIndex:number) {
|
||||
|
@ -58,6 +58,12 @@ export class ViewFactory {
|
||||
}
|
||||
|
||||
_createView(protoView:pvModule.RenderProtoView, inplaceElement): viewModule.RenderView {
|
||||
if (isPresent(protoView.imperativeRendererId)) {
|
||||
return new viewModule.RenderView(
|
||||
protoView, [], [], [], []
|
||||
);
|
||||
}
|
||||
|
||||
var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element);
|
||||
var elementsWithBindingsDynamic;
|
||||
if (protoView.isTemplateElement) {
|
||||
@ -125,7 +131,7 @@ export class ViewFactory {
|
||||
// static child components
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createView(binder.nestedProtoView, null);
|
||||
this.setComponentView(view, binderIdx, childView);
|
||||
ViewFactory.setComponentView(this._shadowDomStrategy, view, binderIdx, childView);
|
||||
}
|
||||
|
||||
// events
|
||||
@ -148,10 +154,10 @@ export class ViewFactory {
|
||||
// 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
|
||||
setComponentView(hostView:viewModule.RenderView, elementIndex:number, componentView:viewModule.RenderView) {
|
||||
static setComponentView(shadowDomStrategy:ShadowDomStrategy, hostView:viewModule.RenderView, elementIndex:number, componentView:viewModule.RenderView) {
|
||||
var element = hostView.boundElements[elementIndex];
|
||||
var lightDom = this._shadowDomStrategy.constructLightDom(hostView, componentView, element);
|
||||
this._shadowDomStrategy.attachTemplate(element, componentView);
|
||||
var lightDom = shadowDomStrategy.constructLightDom(hostView, componentView, element);
|
||||
shadowDomStrategy.attachTemplate(element, componentView);
|
||||
hostView.lightDoms[elementIndex] = lightDom;
|
||||
hostView.componentChildViews[elementIndex] = componentView;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ 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
|
||||
@ -23,14 +24,16 @@ import * as viewModule from './view';
|
||||
export class RenderViewHydrator {
|
||||
_eventManager:EventManager;
|
||||
_viewFactory:ViewFactory;
|
||||
_shadowDomStrategy:ShadowDomStrategy;
|
||||
|
||||
constructor(eventManager:EventManager, viewFactory:ViewFactory) {
|
||||
constructor(eventManager:EventManager, viewFactory:ViewFactory, shadowDomStrategy:ShadowDomStrategy) {
|
||||
this._eventManager = eventManager;
|
||||
this._viewFactory = viewFactory;
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
hydrateDynamicComponentView(hostView:viewModule.RenderView, boundElementIndex:number, componentView:viewModule.RenderView) {
|
||||
this._viewFactory.setComponentView(hostView, boundElementIndex, componentView);
|
||||
ViewFactory.setComponentView(this._shadowDomStrategy, hostView, boundElementIndex, componentView);
|
||||
var lightDom = hostView.lightDoms[boundElementIndex];
|
||||
this._viewHydrateRecurse(componentView, lightDom);
|
||||
if (isPresent(lightDom)) {
|
||||
@ -50,15 +53,17 @@ export class RenderViewHydrator {
|
||||
|
||||
hydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
||||
if (isPresent(parentView)) {
|
||||
throw new BaseException('Not supported yet');
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
this._viewHydrateRecurse(hostView, null);
|
||||
}
|
||||
|
||||
dehydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
||||
if (isPresent(parentView)) {
|
||||
throw new BaseException('Not supported yet');
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
vcModule.ViewContainer.removeViewNodes(hostView);
|
||||
hostView.rootNodes = [];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
}
|
||||
|
||||
@ -130,12 +135,24 @@ export class RenderViewHydrator {
|
||||
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++) {
|
||||
|
Reference in New Issue
Block a user