refactor(view): provide ViewContainers dynamically on any element

This commit is contained in:
Tobias Bosch
2015-04-16 15:38:28 -07:00
parent eac5c88893
commit f830cfca12
26 changed files with 467 additions and 210 deletions

View File

@ -47,15 +47,15 @@ export function main() {
}
function createEmptyView() {
var view = new AppView(null, createProtoView(), MapWrapper.create());
view.init(null, [], [], [], [], []);
var view = new AppView(null, null, null, createProtoView(), MapWrapper.create());
view.init(null, [], [], [], []);
return view;
}
function createElementRef(view, boundElementIndex) {
var peli = new ProtoElementInjector(null, boundElementIndex, []);
var eli = new ElementInjector(peli, null);
var preBuiltObjects = new PreBuiltObjects(view, null, null, null);
var preBuiltObjects = new PreBuiltObjects(view, null, null);
eli.instantiateDirectives(null, null, null, preBuiltObjects);
return new ElementRef(eli);
}

View File

@ -21,7 +21,7 @@ class DummyDirective extends Directive {
@proxy
@IMPLEMENTS(AppView)
class DummyView extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
class DummyView extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
class SimpleDirective {
@ -203,7 +203,7 @@ class TestNode extends TreeNode {
}
export function main() {
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null);
var appInjector = Injector.resolveAndCreate([]);
function humanize(tree, names:List) {
@ -476,7 +476,7 @@ export function main() {
it("should instantiate directives that depend on pre built objects", function () {
var view = new DummyView();
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null, null));
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null));
expect(inj.get(NeedsView).view).toBe(view);
});
@ -602,28 +602,32 @@ export function main() {
describe("pre built objects", function () {
it("should return view", function () {
var view = new DummyView();
var inj = injector([], null, null, new PreBuiltObjects(view, null, null, null));
var inj = injector([], null, null, new PreBuiltObjects(view, null, null));
expect(inj.get(AppView)).toEqual(view);
});
it("should return element", function () {
var element = new NgElement(null, null);
var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null));
var inj = injector([], null, null, new PreBuiltObjects(null, element, null));
expect(inj.get(NgElement)).toEqual(element);
});
it('should return viewContainer', function () {
var viewContainer = new ViewContainer(null, null, null, null, null);
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewContainer, null));
var viewContainer = new ViewContainer(null, null, null);
var view = new DummyView();
view.spy('getOrCreateViewContainer').andCallFake( (index) => {
return viewContainer;
});
var inj = injector([], null, null, new PreBuiltObjects(view, null, null));
expect(inj.get(ViewContainer)).toEqual(viewContainer);
});
it('should return changeDetectorRef', function () {
var cd = new DynamicChangeDetector(null, null, null, [], []);
var inj = injector([], null, null, new PreBuiltObjects(null, null, null, cd));
var inj = injector([], null, null, new PreBuiltObjects(null, null, cd));
expect(inj.get(ChangeDetectorRef)).toBe(cd.ref);
});
@ -710,12 +714,12 @@ export function main() {
beforeEach( () => {
renderer = new FakeRenderer();
var protoView = new AppProtoView(null, null);
view = new AppView(renderer, protoView, MapWrapper.create());
view = new AppView(renderer, null, null, protoView, MapWrapper.create());
view.render = new ViewRef();
});
it('should be injectable and callable', () => {
var preBuildObject = new PreBuiltObjects(view, null, null, null);
var preBuildObject = new PreBuiltObjects(view, null, null);
var inj = injector([NeedsPropertySetter], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetter);
component.setProp('foobar');
@ -734,7 +738,7 @@ export function main() {
});
it('should be injectable and callable without specifying param type annotation', () => {
var preBuildObject = new PreBuiltObjects(view, null, null, null);
var preBuildObject = new PreBuiltObjects(view, null, null);
var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetterNoType);
component.setProp('foobar');
@ -773,6 +777,16 @@ export function main() {
var inj = injector([NeedsElementRef]);
expect(inj.get(NeedsElementRef).elementRef).toBeAnInstanceOf(ElementRef);
});
it('should return the viewContainer from the view', () => {
var viewContainer = new ViewContainer(null, null, null);
var view = new DummyView();
view.spy('getOrCreateViewContainer').andCallFake( (index) => {
return viewContainer;
});
var inj = injector([NeedsElementRef], null, null, new PreBuiltObjects(view, null, null));
expect(inj.get(NeedsElementRef).elementRef.viewContainer).toBe(viewContainer);
});
});
describe('directive queries', () => {

View File

@ -34,6 +34,7 @@ import {ElementRef} from 'angular2/src/core/compiler/element_injector';
import {If} from 'angular2/src/directives/if';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {Compiler} from 'angular2/src/core/compiler/compiler';
export function main() {
describe('integration tests', function() {
@ -694,6 +695,27 @@ export function main() {
}));
});
describe('dynamic ViewContainers', () => {
it('should allow to create a ViewContainer at any bound location',
inject([TestBed, AsyncTestCompleter, Compiler], (tb, async, compiler) => {
tb.overrideView(MyComp, new View({
template: '<div><dynamic-vp #dynamic></dynamic-vp></div>',
directives: [DynamicViewport]
}));
tb.createView(MyComp).then((view) => {
var dynamicVp = view.rawView.elementInjectors[0].get(DynamicViewport);
dynamicVp.done.then( (_) => {
view.detectChanges();
expect(view.rootNodes).toHaveText('dynamic greet');
async.done();
});
});
}));
});
it('should support static attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideView(MyComp, new View({
template: '<input static type="text" title>',
@ -774,9 +796,22 @@ export function main() {
});
}
class DynamicallyCreatedComponentService {
@Decorator({
selector: 'dynamic-vp'
})
class DynamicViewport {
done;
constructor(vc:ViewContainer, inj:Injector, compiler:Compiler) {
var myService = new MyService();
myService.greeting = 'dynamic greet';
this.done = compiler.compileInHost(ChildCompUsingService).then( (hostPv) => {
vc.create(0, hostPv, inj.createChildFromResolved(Injector.resolve([bind(MyService).toValue(myService)])))
});
}
}
class DynamicallyCreatedComponentService {}
@DynamicComponent({
selector: 'dynamic-comp'
})
@ -909,6 +944,19 @@ class ChildComp {
}
}
@Component({
selector: 'child-cmp-svc'
})
@View({
template: '{{ctxProp}}'
})
class ChildCompUsingService {
ctxProp:string;
constructor(service: MyService) {
this.ctxProp = service.greeting;
}
}
@Decorator({
selector: 'some-directive'
})

View File

@ -35,7 +35,7 @@ export function main() {
});
function createViewFactory({capacity}):ViewFactory {
return new ViewFactory(capacity, renderer);
return new ViewFactory(capacity, renderer, null);
}
function createProtoChangeDetector() {

View File

@ -80,19 +80,19 @@ export function main() {
}
function createEmptyView() {
var view = new AppView(renderer, createProtoView(), MapWrapper.create());
var view = new AppView(renderer, null, null, createProtoView(), MapWrapper.create());
var changeDetector = new SpyChangeDetector();
view.init(changeDetector, [], [], [], [], []);
view.init(changeDetector, [], [], [], []);
return view;
}
function createHostView(pv, shadowView, componentInstance) {
var view = new AppView(renderer, pv, MapWrapper.create());
var view = new AppView(renderer, null, null, pv, MapWrapper.create());
var changeDetector = new SpyChangeDetector();
var eij = createElementInjector();
eij.spy('getComponent').andCallFake( () => componentInstance );
view.init(changeDetector, [eij], [eij],
[null], [null], [shadowView]);
[null], [shadowView]);
return view;
}

View File

@ -0,0 +1,115 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
xdescribe,
describe,
el,
dispatchEvent,
expect,
iit,
inject,
beforeEachBindings,
it,
xit,
SpyObject, proxy
} from 'angular2/test_lib';
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {Renderer} from 'angular2/src/render/api';
import {ChangeDetector} from 'angular2/change_detection';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {ElementInjector} from 'angular2/src/core/compiler/element_injector';
export function main() {
describe('AppView', () => {
var renderer;
beforeEach( () => {
renderer = new SpyRenderer();
});
function createElementInjector() {
return new SpyElementInjector();
}
function createEmptyElBinder() {
return new ElementBinder(0, null, 0, null, null, null);
}
function createEmbeddedProtoViewElBinder(nestedProtoView) {
var binder = new ElementBinder(0, null, 0, null, null, null);
binder.nestedProtoView = nestedProtoView;
return binder;
}
function createProtoView(binders = null) {
if (isBlank(binders)) {
binders = [];
}
var res = new AppProtoView(null, null);
res.elementBinders = binders;
return res;
}
function createViewWithOneBoundElement(pv) {
var view = new AppView(renderer, null, null, pv, MapWrapper.create());
var changeDetector = new SpyChangeDetector();
var eij = createElementInjector();
view.init(changeDetector, [eij], [eij],
[null], [null]);
return view;
}
describe('getOrCreateViewContainer()', () => {
it('should create a new container', () => {
var pv = createProtoView([createEmptyElBinder()]);
var view = createViewWithOneBoundElement(pv);
expect(view.getOrCreateViewContainer(0) instanceof ViewContainer).toBe(true);
});
it('should return an existing container', () => {
var pv = createProtoView([createEmptyElBinder()]);
var view = createViewWithOneBoundElement(pv);
var vc = view.getOrCreateViewContainer(0);
expect(view.getOrCreateViewContainer(0)).toBe(vc);
});
it('should store an existing nestedProtoView in the container', () => {
var defaultProtoView = createProtoView();
var pv = createProtoView([createEmbeddedProtoViewElBinder(defaultProtoView)]);
var view = createViewWithOneBoundElement(pv);
var vc = view.getOrCreateViewContainer(0);
expect(vc.defaultProtoView).toBe(defaultProtoView);
});
});
});
}
@proxy
@IMPLEMENTS(Renderer)
class SpyRenderer extends SpyObject {
constructor(){super(Renderer);}
noSuchMethod(m){return super.noSuchMethod(m)}
}
@proxy
@IMPLEMENTS(ChangeDetector)
class SpyChangeDetector extends SpyObject {
constructor(){super(ChangeDetector);}
noSuchMethod(m){return super.noSuchMethod(m)}
}
@proxy
@IMPLEMENTS(ElementInjector)
class SpyElementInjector extends SpyObject {
constructor(){super(ElementInjector);}
noSuchMethod(m){return super.noSuchMethod(m)}
}