feat(view): add imperative views
This commit is contained in:
13
modules/angular2/src/core/annotations/view.js
vendored
13
modules/angular2/src/core/annotations/view.js
vendored
@ -71,19 +71,28 @@ export class View {
|
||||
*/
|
||||
directives:any; //List<Type>;
|
||||
|
||||
/**
|
||||
* Specify a custom renderer for this View.
|
||||
* If this is set, neither `template`, `templateURL` nor `directives` are used.
|
||||
*/
|
||||
renderer:any; // string;
|
||||
|
||||
@CONST()
|
||||
constructor({
|
||||
templateUrl,
|
||||
template,
|
||||
directives
|
||||
directives,
|
||||
renderer
|
||||
}: {
|
||||
templateUrl: string,
|
||||
template: string,
|
||||
directives: List<Type>
|
||||
directives: List<Type>,
|
||||
renderer: string
|
||||
})
|
||||
{
|
||||
this.templateUrl = templateUrl;
|
||||
this.template = template;
|
||||
this.directives = directives;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
}
|
||||
|
7
modules/angular2/src/core/application.js
vendored
7
modules/angular2/src/core/application.js
vendored
@ -75,7 +75,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
// 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(appElement, appComponentAnnotatedType.type, injector);
|
||||
return dynamicComponentLoader.loadIntoNewLocation(appComponentAnnotatedType.type, null, appElement, injector);
|
||||
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||
Testability, TestabilityRegistry]),
|
||||
|
||||
@ -91,6 +91,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||
[StyleUrlResolver, appDocumentToken]),
|
||||
DirectDomRenderer,
|
||||
bind(Renderer).toClass(DirectDomRenderer),
|
||||
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
||||
// TODO(tbosch): We need an explicit factory here, as
|
||||
@ -105,8 +106,8 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
// TODO(tbosch): We need an explicit factory here, as
|
||||
// we are getting errors in dart2js with mirrors...
|
||||
bind(ViewFactory).toFactory(
|
||||
(capacity, renderer, appViewHydrator) => new ViewFactory(capacity, renderer, appViewHydrator),
|
||||
[VIEW_POOL_CAPACITY, Renderer, AppViewHydrator]
|
||||
(capacity, renderer) => new ViewFactory(capacity, renderer),
|
||||
[VIEW_POOL_CAPACITY, Renderer]
|
||||
),
|
||||
bind(VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewHydrator,
|
||||
|
23
modules/angular2/src/core/compiler/compiler.js
vendored
23
modules/angular2/src/core/compiler/compiler.js
vendored
@ -114,14 +114,21 @@ export class Compiler {
|
||||
}
|
||||
|
||||
var template = this._templateResolver.resolve(component);
|
||||
var directives = ListWrapper.map(
|
||||
this._flattenDirectives(template),
|
||||
(directive) => this._bindDirective(directive)
|
||||
);
|
||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||
});
|
||||
if (isPresent(template.renderer)) {
|
||||
var directives = [];
|
||||
pvPromise = this._renderer.createImperativeComponentProtoView(template.renderer).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(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._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||
});
|
||||
}
|
||||
|
||||
MapWrapper.set(this._compiling, component, pvPromise);
|
||||
return pvPromise;
|
||||
|
@ -70,15 +70,10 @@ export class DynamicComponentLoader {
|
||||
|
||||
return this._compiler.compile(type).then(componentProtoView => {
|
||||
var componentView = this._viewFactory.getView(componentProtoView);
|
||||
var hostView = location.hostView;
|
||||
this._viewHydrator.hydrateDynamicComponentView(
|
||||
hostView, location.boundElementIndex, componentView, componentBinding, injector);
|
||||
location, componentView, componentBinding, injector);
|
||||
|
||||
// TODO(vsavkin): return a component ref that dehydrates the component view and removes it
|
||||
// from the component child views
|
||||
// See ViewFactory.returnView
|
||||
// See AppViewHydrator.dehydrateDynamicComponentView
|
||||
var dispose = () => {throw "Not implemented";};
|
||||
var dispose = () => {throw new BaseException("Not implemented");};
|
||||
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView, dispose);
|
||||
});
|
||||
}
|
||||
@ -87,19 +82,22 @@ export class DynamicComponentLoader {
|
||||
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadIntoNewLocation(elementOrSelector:any, type:Type, injector:Injector = null):Promise<ComponentRef> {
|
||||
loadIntoNewLocation(type:Type, parentComponentLocation:ElementRef, elementOrSelector:any,
|
||||
injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
|
||||
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||
var hostView = this._viewFactory.getView(hostProtoView);
|
||||
this._viewHydrator.hydrateInPlaceHostView(null, elementOrSelector, hostView, injector);
|
||||
this._viewHydrator.hydrateInPlaceHostView(
|
||||
parentComponentLocation, elementOrSelector, hostView, injector
|
||||
);
|
||||
|
||||
// TODO(vsavkin): return a component ref that dehydrates the host view
|
||||
// See ViewFactory.returnView
|
||||
// See AppViewHydrator.dehydrateInPlaceHostView
|
||||
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||
var component = hostView.elementInjectors[0].getComponent();
|
||||
var dispose = () => {throw "Not implemented";};
|
||||
var dispose = () => {
|
||||
this._viewHydrator.dehydrateInPlaceHostView(parentComponentLocation, hostView);
|
||||
this._viewFactory.returnView(hostView);
|
||||
};
|
||||
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
||||
});
|
||||
}
|
||||
|
@ -26,27 +26,21 @@ var _staticKeys;
|
||||
* @exportedAs angular2/view
|
||||
*/
|
||||
export class ElementRef {
|
||||
hostView:viewModule.AppView;
|
||||
boundElementIndex:number;
|
||||
injector:Injector;
|
||||
elementInjector:ElementInjector;
|
||||
|
||||
constructor(elementInjector:ElementInjector){
|
||||
constructor(elementInjector, hostView, boundElementIndex, injector){
|
||||
this.elementInjector = elementInjector;
|
||||
}
|
||||
|
||||
get hostView() {
|
||||
return this.elementInjector._preBuiltObjects.view;
|
||||
this.hostView = hostView;
|
||||
this.boundElementIndex = boundElementIndex;
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
get viewContainer() {
|
||||
return this.hostView.getOrCreateViewContainer(this.boundElementIndex);
|
||||
}
|
||||
|
||||
get injector() {
|
||||
return this.elementInjector._lightDomAppInjector;
|
||||
}
|
||||
|
||||
get boundElementIndex() {
|
||||
return this.elementInjector._proto.index;
|
||||
}
|
||||
}
|
||||
|
||||
class StaticKeys {
|
||||
@ -673,7 +667,7 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
getElementRef() {
|
||||
return new ElementRef(this);
|
||||
return new ElementRef(this, this._preBuiltObjects.view, this._proto.index, this._lightDomAppInjector);
|
||||
}
|
||||
|
||||
getDynamicallyLoadedComponent() {
|
||||
|
10
modules/angular2/src/core/compiler/view.js
vendored
10
modules/angular2/src/core/compiler/view.js
vendored
@ -25,6 +25,9 @@ export class AppView {
|
||||
elementInjectors:List<ElementInjector>;
|
||||
changeDetector:ChangeDetector;
|
||||
componentChildViews: List<AppView>;
|
||||
/// Host views that were added by an imperative view.
|
||||
/// This is a dynamically growing / shrinking array.
|
||||
imperativeHostViews: List<AppView>;
|
||||
viewContainers: List<ViewContainer>;
|
||||
preBuiltObjects: List<PreBuiltObjects>;
|
||||
proto: AppProtoView;
|
||||
@ -46,7 +49,7 @@ export class AppView {
|
||||
*/
|
||||
locals:Locals;
|
||||
|
||||
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, viewHydrator:vhModule.AppViewHydrator, proto:AppProtoView, protoLocals:Map) {
|
||||
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, proto:AppProtoView, protoLocals:Map) {
|
||||
this.render = null;
|
||||
this.proto = proto;
|
||||
this.changeDetector = null;
|
||||
@ -59,7 +62,8 @@ export class AppView {
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||
this.renderer = renderer;
|
||||
this.viewFactory = viewFactory;
|
||||
this.viewHydrator = viewHydrator;
|
||||
this.viewHydrator = null;
|
||||
this.imperativeHostViews = [];
|
||||
}
|
||||
|
||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
||||
@ -151,7 +155,7 @@ export class AppView {
|
||||
}
|
||||
var result = expr.eval(context, new Locals(this.locals, locals));
|
||||
if (isPresent(result)) {
|
||||
allowDefaultBehavior = allowDefaultBehavior && result;
|
||||
allowDefaultBehavior = allowDefaultBehavior && result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
import * as viewModule from './view';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {AppViewHydrator} from './view_hydrator';
|
||||
|
||||
// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this!
|
||||
export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity';
|
||||
@ -15,13 +14,11 @@ export class ViewFactory {
|
||||
_poolCapacityPerProtoView:number;
|
||||
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
||||
_renderer:Renderer;
|
||||
_viewHydrator:AppViewHydrator;
|
||||
|
||||
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer, viewHydrator:AppViewHydrator) {
|
||||
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
this._pooledViewsPerProtoView = MapWrapper.create();
|
||||
this._renderer = renderer;
|
||||
this._viewHydrator = viewHydrator;
|
||||
}
|
||||
|
||||
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
||||
@ -48,7 +45,7 @@ export class ViewFactory {
|
||||
}
|
||||
|
||||
_createView(protoView:viewModule.AppProtoView): viewModule.AppView {
|
||||
var view = new viewModule.AppView(this._renderer, this, this._viewHydrator, protoView, protoView.protoLocals);
|
||||
var view = new viewModule.AppView(this._renderer, this, protoView, protoView.protoLocals);
|
||||
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
|
||||
protoView.getVariableBindings(), protoView.getdirectiveRecords());
|
||||
|
||||
|
@ -7,6 +7,7 @@ import * as viewModule from './view';
|
||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
|
||||
/**
|
||||
* A dehydrated view is a state of the view that allows it to be moved around
|
||||
@ -27,13 +28,17 @@ import * as renderApi from 'angular2/src/render/api';
|
||||
@Injectable()
|
||||
export class AppViewHydrator {
|
||||
_renderer:renderApi.Renderer;
|
||||
_viewFactory:ViewFactory;
|
||||
|
||||
constructor(renderer:renderApi.Renderer) {
|
||||
constructor(renderer:renderApi.Renderer, viewFactory:ViewFactory) {
|
||||
this._renderer = renderer;
|
||||
this._viewFactory = viewFactory;
|
||||
}
|
||||
|
||||
hydrateDynamicComponentView(hostView:viewModule.AppView, boundElementIndex:number,
|
||||
hydrateDynamicComponentView(location:eli.ElementRef,
|
||||
componentView:viewModule.AppView, componentDirective:eli.DirectiveBinding, injector:Injector) {
|
||||
var hostView = location.hostView;
|
||||
var boundElementIndex = location.boundElementIndex;
|
||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
||||
if (!binder.hasDynamicComponent()) {
|
||||
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`);
|
||||
@ -84,16 +89,23 @@ export class AppViewHydrator {
|
||||
// parentView.componentChildViews[boundElementIndex] = null;
|
||||
}
|
||||
|
||||
hydrateInPlaceHostView(parentView:viewModule.AppView, hostElementSelector, hostView:viewModule.AppView, injector:Injector) {
|
||||
hydrateInPlaceHostView(parentComponentLocation:eli.ElementRef,
|
||||
hostElementSelector, hostView:viewModule.AppView, injector:Injector) {
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentView)) {
|
||||
// Needed for user views
|
||||
throw new BaseException('Not yet supported');
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
|
||||
if (isBlank(injector)) {
|
||||
injector = parentComponentLocation.injector;
|
||||
}
|
||||
}
|
||||
|
||||
var binder = hostView.proto.elementBinders[0];
|
||||
var shadowDomAppInjector = this._createShadowDomAppInjector(binder.componentDirective, injector);
|
||||
|
||||
// render views
|
||||
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render);
|
||||
|
||||
this._viewHydrateRecurse(
|
||||
@ -101,11 +113,13 @@ export class AppViewHydrator {
|
||||
);
|
||||
}
|
||||
|
||||
dehydrateInPlaceHostView(parentView:viewModule.AppView, hostView:viewModule.AppView) {
|
||||
dehydrateInPlaceHostView(parentComponentLocation:eli.ElementRef, hostView:viewModule.AppView) {
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentView)) {
|
||||
// Needed for user views
|
||||
throw new BaseException('Not yet supported');
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||
}
|
||||
var render = hostView.render;
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
@ -137,7 +151,7 @@ export class AppViewHydrator {
|
||||
appInjector: Injector, hostElementInjector: eli.ElementInjector,
|
||||
context: Object, locals:Locals):number {
|
||||
if (view.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||
|
||||
view.viewHydrator = this;
|
||||
view.render = renderComponentViewRefs[renderComponentIndex++];
|
||||
|
||||
view.context = context;
|
||||
@ -215,12 +229,22 @@ export class AppViewHydrator {
|
||||
this._viewDehydrateRecurse(componentView);
|
||||
var binder = view.proto.elementBinders[i];
|
||||
if (binder.hasDynamicComponent()) {
|
||||
view.componentChildViews[i] = null;
|
||||
view.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
||||
view.componentChildViews[i] = null;
|
||||
this._viewFactory.returnView(componentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imperativeHostViews
|
||||
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||
var hostView = view.imperativeHostViews[i];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
view.changeDetector.removeChild(hostView.changeDetector);
|
||||
this._viewFactory.returnView(hostView);
|
||||
}
|
||||
view.imperativeHostViews = [];
|
||||
|
||||
// elementInjectors
|
||||
for (var i = 0; i < view.elementInjectors.length; i++) {
|
||||
if (isPresent(view.elementInjectors[i])) {
|
||||
|
Reference in New Issue
Block a user