refactor(compiler): use the new compiler everywhere

Closes #3605

BREAKING CHANGE:
- we don't mark an element as bound any more if it only contains text bindings
  E.g. <div>{{hello}}</div>
  This changes the indices when using `DebugElement.componentViewChildren` / `DebugElement.children`.
- `@Directive.compileChildren` was removed,
  `ng-non-bindable` is now builtin and not a directive any more
- angular no more adds the `ng-binding` class to elements with bindings
- directives are now ordered as they are listed in the View.directives regarding change detection.
  Previously they had an undefined order.
- the `Renderer` interface has new methods `createProtoView` and `registerComponentTemplate`. See `DomRenderer` for default implementations.
- reprojection with `ng-content` is now all or nothing per `ng-content` element
- angular2 transformer can't be used in tests that modify directive metadata.
  Use `angular2/src/transform/inliner_for_test` transformer instead.
This commit is contained in:
Tobias Bosch
2015-10-01 10:07:49 -07:00
parent 30ca0434a2
commit 76247b7097
124 changed files with 2013 additions and 3451 deletions

View File

@ -1,752 +1,63 @@
import {
AsyncTestCompleter,
beforeEach,
xdescribe,
ddescribe,
describe,
el,
expect,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
it
beforeEachBindings
} from 'angular2/test_lib';
import {SpyRenderCompiler, SpyDirectiveResolver} from '../spies';
import {ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {Type, isBlank, stringify, isPresent, isArray} from 'angular2/src/core/facade/lang';
import {PromiseCompleter, PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {AppProtoView} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
import {Attribute, ViewMetadata, Component, Directive, Pipe} from 'angular2/src/core/metadata';
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {Component, View, bind} from 'angular2/core';
import {SpyProtoViewFactory} from '../spies';
import {
ComponentUrlMapper,
RuntimeComponentUrlMapper
} from 'angular2/src/core/compiler/component_url_mapper';
CompiledHostTemplate,
CompiledTemplate,
BeginComponentCmd
} from 'angular2/src/core/compiler/template_commands';
import {Compiler} from 'angular2/src/core/compiler/compiler';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {
ProtoViewDto,
ViewType,
RenderProtoViewRef,
ViewDefinition,
RenderProtoViewMergeMapping,
RenderDirectiveMetadata,
DirectiveBinder,
RenderElementBinder
} from 'angular2/src/core/render/api';
// TODO(tbosch): Spys don't support named modules...
import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding';
import {reflector, ReflectionInfo} from 'angular2/src/core/reflection/reflection';
import {AppProtoView} from 'angular2/src/core/compiler/view';
export function main() {
describe('compiler', function() {
var directiveResolver, pipeResolver, tplResolver, renderCompiler, protoViewFactory,
cmpUrlMapper, rootProtoView;
var renderCompileRequests: any[];
describe('Compiler', () => {
var compiler: Compiler;
var protoViewFactorySpy;
var someProtoView;
var cht: CompiledHostTemplate;
function createCompiler(renderCompileResults: Array<ProtoViewDto | Promise<ProtoViewDto>>,
protoViewFactoryResults: AppProtoView[]) {
var urlResolver = new UrlResolver();
renderCompileRequests = [];
renderCompileResults = ListWrapper.clone(renderCompileResults);
renderCompiler.spy('compile').andCallFake((view) => {
renderCompileRequests.push(view);
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
});
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
return new Compiler(directiveResolver, pipeResolver, [SomeDefaultPipe], new CompilerCache(),
tplResolver, cmpUrlMapper, urlResolver, renderCompiler, protoViewFactory,
new AppRootUrl("http://www.app.com"));
}
beforeEach(() => {
directiveResolver = new DirectiveResolver();
pipeResolver = new PipeResolver();
tplResolver = new FakeViewResolver();
cmpUrlMapper = new RuntimeComponentUrlMapper();
renderCompiler = new SpyRenderCompiler();
renderCompiler.spy('compileHost')
.andCallFake((componentId) => {
return PromiseWrapper.resolve(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.HOST));
});
renderCompiler.spy('mergeProtoViewsRecursively')
.andCallFake((protoViewRefs: Array<RenderProtoViewRef | any[]>) => {
return PromiseWrapper.resolve(new RenderProtoViewMergeMapping(
new MergedRenderProtoViewRef(protoViewRefs), 1, [], 0, [], [], [null]));
});
// TODO spy on .compile and return RenderProtoViewRef, same for compileHost
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
beforeEachBindings(() => {
protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
return [bind(ProtoViewFactory).toValue(protoViewFactorySpy), Compiler];
});
describe('serialize template', () => {
beforeEach(inject([Compiler], (_compiler) => {
compiler = _compiler;
cht = new CompiledHostTemplate(() => new CompiledTemplate(23, null));
reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
}));
function captureTemplate(template: ViewMetadata): Promise<ViewDefinition> {
tplResolver.setView(MainComponent, template);
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
return compiler.compileInHost(MainComponent)
.then((_) => {
expect(renderCompileRequests.length).toBe(1);
return renderCompileRequests[0];
});
}
function captureDirective(directive): Promise<RenderDirectiveMetadata> {
return captureTemplate(new ViewMetadata({template: '<div></div>', directives: [directive]}))
.then((renderTpl) => {
expect(renderTpl.directives.length).toBe(1);
return renderTpl.directives[0];
});
}
it('should fill the componentId', inject([AsyncTestCompleter], (async) => {
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.componentId).toEqual(stringify(MainComponent));
async.done();
});
}));
it('should fill inline template', inject([AsyncTestCompleter], (async) => {
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.template).toEqual('<div></div>');
async.done();
});
}));
it('should fill templateAbsUrl given inline templates',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toEqual('http://www.app.com/cmp/main.js');
async.done();
});
}));
it('should not fill templateAbsUrl given no inline template or template url',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: null}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should not fill templateAbsUrl given template url with empty string',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: ''}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should not fill templateAbsUrl given template url with blank string',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: ' '}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should fill templateAbsUrl given url template', inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({templateUrl: 'tpl/main.html'}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toEqual('http://www.app.com/cmp/tpl/main.html');
async.done();
});
}));
it('should fill styleAbsUrls given styleUrls', inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({styleUrls: ['css/1.css', 'css/2.css']}))
.then((renderTpl) => {
expect(renderTpl.styleAbsUrls)
.toEqual(
['http://www.app.com/cmp/css/1.css', 'http://www.app.com/cmp/css/2.css']);
async.done();
});
}));
it('should fill directive.id', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.id).toEqual(stringify(MainComponent));
async.done();
});
}));
it('should fill directive.selector', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.selector).toEqual('main-comp');
async.done();
});
}));
it('should fill directive.type for components', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
async.done();
});
}));
it('should fill directive.type for dynamic components',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDynamicComponentDirective)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
async.done();
});
}));
it('should fill directive.type for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDirective)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.DIRECTIVE_TYPE);
async.done();
});
}));
it('should set directive.compileChildren to false for other directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(true);
async.done();
});
}));
it('should set directive.compileChildren to true for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDirective)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(true);
async.done();
});
}));
it('should set directive.compileChildren to false for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(IgnoreChildrenDirective)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(false);
async.done();
});
}));
it('should set directive.hostListeners', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithEvents)
.then((renderDir) => {
expect(renderDir.hostListeners)
.toEqual(MapWrapper.createFromStringMap({'someEvent': 'someAction'}));
async.done();
});
}));
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithProperties)
.then((renderDir) => {
expect(renderDir.hostProperties)
.toEqual(MapWrapper.createFromStringMap({'someProp': 'someExp'}));
async.done();
});
}));
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithBind)
.then((renderDir) => {
expect(renderDir.inputs).toEqual(['a: b']);
async.done();
});
}));
it('should read @Attribute', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithAttributes)
.then((renderDir) => {
expect(renderDir.readAttributes).toEqual(['someAttr']);
async.done();
});
}));
});
describe('call ProtoViewFactory', () => {
it('should pass the ProtoViewDto', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var renderProtoView = createRenderProtoView();
var expectedProtoView = createProtoView();
var compiler = createCompiler([renderProtoView], [rootProtoView, expectedProtoView]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[1]).toBe(renderProtoView);
async.done();
});
}));
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[0].key.token).toBe(MainComponent);
async.done();
});
}));
it('should pass the directive bindings', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(
MainComponent,
new ViewMetadata({template: '<div></div>', directives: [SomeDirective]}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
var binding = request[2][0];
expect(binding.key.token).toBe(SomeDirective);
async.done();
});
}));
it('should pass the pipe bindings', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent,
new ViewMetadata({template: '<div></div>', pipes: [SomePipe]}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[3][0].key.token).toBe(SomeDefaultPipe);
expect(request[3][1].key.token).toBe(SomePipe);
async.done();
});
}));
it('should use the protoView of the ProtoViewFactory',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
async.done();
});
}));
});
it('should load nested components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var mainProtoView =
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
var nestedProtoView = createProtoView();
var renderPvDtos = [
createRenderProtoView([createRenderComponentElementBinder(0)]),
createRenderProtoView()
];
var compiler =
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual(
[rootProtoView.render, [mainProtoView.render, [nestedProtoView.render]]]);
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
it('should read the template from an annotation', inject([AsyncTestCompleter], (async) => {
compiler.compileInHost(SomeComponent)
.then((_) => {
expect(protoViewFactorySpy.spy('createHost')).toHaveBeenCalledWith(cht);
async.done();
});
}));
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, NestedComponent)], ViewType.EMBEDDED);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var nestedProtoView = createProtoView();
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler =
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
expect(originalRenderProtoViewRefs(viewportProtoView))
.toEqual([viewportProtoView.render, [nestedProtoView.render]]);
async.done();
});
}));
it('should cache compiled host components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var mainPv = createProtoView();
var compiler = createCompiler([createRenderProtoView([])], [rootProtoView, mainPv]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainPv);
return compiler.compileInHost(MainComponent);
})
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainPv);
async.done();
});
}));
it('should not bind directives for cached components', inject([AsyncTestCompleter], (async) => {
// set up the cache with the test proto view
var mainPv: AppProtoView = createProtoView();
var cache: CompilerCache = new CompilerCache();
cache.setHost(MainComponent, mainPv);
// create the spy resolver
var reader: any = new SpyDirectiveResolver();
// create the compiler
var compiler = new Compiler(reader, pipeResolver, [], cache, tplResolver, cmpUrlMapper,
new UrlResolver(), renderCompiler, protoViewFactory,
new AppRootUrl("http://www.app.com"));
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
// the test should have failed if the resolver was called, so we're good
async.done();
});
}));
it('should cache compiled nested components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(MainComponent2, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var rootProtoView2 = createRootProtoView(directiveResolver, MainComponent2);
var mainPv =
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
var nestedPv = createProtoView([]);
var compiler = createCompiler(
[createRenderProtoView(), createRenderProtoView(), createRenderProtoView()],
[rootProtoView, mainPv, nestedPv, rootProtoView2, mainPv]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)
.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(nestedPv);
return compiler.compileInHost(MainComponent2);
})
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)
.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(nestedPv);
async.done();
});
}));
it('should re-use components being compiled', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var renderProtoViewCompleter: PromiseCompleter<ProtoViewDto> = PromiseWrapper.completer();
var expectedProtoView = createProtoView();
var compiler = createCompiler([renderProtoViewCompleter.promise],
[rootProtoView, rootProtoView, expectedProtoView]);
var result = PromiseWrapper.all([
compiler.compileInHost(MainComponent),
compiler.compileInHost(MainComponent),
renderProtoViewCompleter.promise
]);
renderProtoViewCompleter.resolve(createRenderProtoView());
result.then((protoViewRefs) => {
expect(internalProtoView(protoViewRefs[0]).elementBinders[0].nestedProtoView)
.toBe(expectedProtoView);
expect(internalProtoView(protoViewRefs[1]).elementBinders[0].nestedProtoView)
.toBe(expectedProtoView);
async.done();
});
}));
it('should throw on unconditional recursive components',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var mainProtoView =
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
var compiler =
createCompiler([createRenderProtoView([createRenderComponentElementBinder(0)])],
[rootProtoView, mainProtoView]);
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
expect(() => { throw e; })
.toThrowError(`Unconditional component cycle in ${stringify(MainComponent)}`);
async.done();
return null;
});
}));
it('should allow recursive components that are connected via an embedded ProtoView',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, MainComponent)], ViewType.EMBEDDED);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(mainProtoView.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(mainProtoView);
// In case of a cycle, don't merge the embedded proto views into the component!
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
expect(originalRenderProtoViewRefs(viewportProtoView))
.toEqual([viewportProtoView.render, [mainProtoView.render, null]]);
async.done();
});
}));
it('should throw on recursive components that are connected via an embedded ProtoView with <ng-content>',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView =
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)],
ViewType.EMBEDDED, true);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
expect(() => { throw e; })
.toThrowError(
`<ng-content> is used within the recursive path of ${stringify(MainComponent)}`);
async.done();
return null;
});
}));
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var rootProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, MainComponent)], ViewType.HOST);
var mainProtoView = createProtoView();
var compiler = createCompiler([createRenderProtoView()], [rootProtoView, mainProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
async.done();
});
}));
it('should throw for non component types', () => {
var compiler = createCompiler([], []);
expect(() => compiler.compileInHost(SomeDirective))
.toThrowError(
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
it('should clear the cache', () => {
compiler.clearCache();
expect(protoViewFactorySpy.spy('clearCache')).toHaveBeenCalled();
});
});
}
function createDirectiveBinding(directiveResolver, type): DirectiveBinding {
var annotation = directiveResolver.resolve(type);
return DirectiveBinding.createFromType(type, annotation);
}
function createProtoView(elementBinders = null, type: ViewType = null,
isEmbeddedFragment: boolean = false): AppProtoView {
if (isBlank(type)) {
type = ViewType.COMPONENT;
}
var pv = new AppProtoView(type, isEmbeddedFragment, new RenderProtoViewRef(), null, null,
new Map<string, number>(), null, null);
if (isBlank(elementBinders)) {
elementBinders = [];
}
pv.elementBinders = elementBinders;
return pv;
}
function createComponentElementBinder(directiveResolver, type): ElementBinder {
var binding = createDirectiveBinding(directiveResolver, type);
return new ElementBinder(0, null, 0, null, binding);
}
function createViewportElementBinder(nestedProtoView): ElementBinder {
var elBinder = new ElementBinder(0, null, 0, null, null);
elBinder.nestedProtoView = nestedProtoView;
return elBinder;
}
function createRenderProtoView(elementBinders = null, type: ViewType = null): ProtoViewDto {
if (isBlank(type)) {
type = ViewType.COMPONENT;
}
if (isBlank(elementBinders)) {
elementBinders = [];
}
return new ProtoViewDto(
{elementBinders: elementBinders, type: type, render: new RenderProtoViewRef()});
}
function createRenderComponentElementBinder(directiveIndex): RenderElementBinder {
return new RenderElementBinder(
{directives: [new DirectiveBinder({directiveIndex: directiveIndex})]});
}
function createRenderViewportElementBinder(nestedProtoView): RenderElementBinder {
return new RenderElementBinder({nestedProtoView: nestedProtoView});
}
function createRootProtoView(directiveResolver, type): AppProtoView {
return createProtoView([createComponentElementBinder(directiveResolver, type)], ViewType.HOST);
}
@Component({selector: 'main-comp'})
class MainComponent {
}
@Component({selector: 'main-comp2'})
class MainComponent2 {
}
@Component({selector: 'nested'})
class NestedComponent {
}
class RecursiveComponent {}
@Component({selector: 'some-dynamic'})
class SomeDynamicComponentDirective {
}
@Directive({selector: 'some'})
class SomeDirective {
}
@Directive({compileChildren: false})
class IgnoreChildrenDirective {
}
@Directive({host: {'(someEvent)': 'someAction'}})
class DirectiveWithEvents {
}
@Directive({host: {'[someProp]': 'someExp'}})
class DirectiveWithProperties {
}
@Directive({inputs: ['a: b']})
class DirectiveWithBind {
}
@Pipe({name: 'some-default-pipe'})
class SomeDefaultPipe {
}
@Pipe({name: 'some-pipe'})
class SomePipe {
}
@Directive({selector: 'directive-with-accts'})
class DirectiveWithAttributes {
constructor(@Attribute('someAttr') someAttr: String) {}
}
class FakeViewResolver extends ViewResolver {
_cmpViews = new Map<Type, ViewMetadata>();
constructor() { super(); }
resolve(component: Type): ViewMetadata {
// returns null for dynamic components
return this._cmpViews.has(component) ? this._cmpViews.get(component) : null;
}
setView(component: Type, view: ViewMetadata): void { this._cmpViews.set(component, view); }
}
class FakeProtoViewFactory extends ProtoViewFactory {
requests: any[][];
constructor(public results: AppProtoView[]) {
super(null);
this.requests = [];
}
createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: ProtoViewDto,
directives: DirectiveBinding[], pipes: PipeBinding[]): AppProtoView[] {
this.requests.push([componentBinding, renderProtoView, directives, pipes]);
return collectEmbeddedPvs(ListWrapper.removeAt(this.results, 0));
}
}
class MergedRenderProtoViewRef extends RenderProtoViewRef {
constructor(public originals: RenderProtoViewRef[]) { super(); }
}
function originalRenderProtoViewRefs(appProtoView: AppProtoView) {
return (<MergedRenderProtoViewRef>appProtoView.mergeMapping.renderProtoViewRef).originals;
}
function collectEmbeddedPvs(pv: AppProtoView, target: AppProtoView[] = null): AppProtoView[] {
if (isBlank(target)) {
target = [];
}
target.push(pv);
pv.elementBinders.forEach(elementBinder => {
if (elementBinder.hasEmbeddedProtoView()) {
collectEmbeddedPvs(elementBinder.nestedProtoView, target);
}
});
return target;
}
class SomeComponent {}

View File

@ -111,7 +111,6 @@ export function main() {
rootTC.detectChanges();
expect(rootTC.debugElement.nativeElement).toHaveText('Hello World!');
async.done();
});
}));
@ -431,7 +430,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>',
'<template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template>',
directives: [SomeViewport]
}))
@ -440,11 +439,11 @@ export function main() {
rootTC.detectChanges();
var childNodesOfWrapper = rootTC.debugElement.componentViewChildren;
var childNodesOfWrapper = DOM.childNodes(rootTC.debugElement.nativeElement);
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].nativeElement).toHaveText('hello');
expect(childNodesOfWrapper[2].nativeElement).toHaveText('again');
expect(childNodesOfWrapper[1]).toHaveText('hello');
expect(childNodesOfWrapper[2]).toHaveText('again');
async.done();
});
}));
@ -454,7 +453,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me></div>',
'<copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me>',
directives: [SomeViewport]
}))
@ -462,11 +461,11 @@ export function main() {
.then((rootTC) => {
rootTC.detectChanges();
var childNodesOfWrapper = rootTC.debugElement.componentViewChildren;
var childNodesOfWrapper = DOM.childNodes(rootTC.debugElement.nativeElement);
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].nativeElement).toHaveText('hello');
expect(childNodesOfWrapper[2].nativeElement).toHaveText('again');
expect(childNodesOfWrapper[1]).toHaveText('hello');
expect(childNodesOfWrapper[2]).toHaveText('again');
async.done();
});
}));
@ -629,7 +628,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><div *ng-for="var i of [1]"><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</div></div>',
'<template ng-for [ng-for-of]="[1]" var-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>',
directives: [ChildCompNoTemplate, NgFor]
}))
@ -637,8 +636,8 @@ export function main() {
.then((rootTC) => {
rootTC.detectChanges();
// Get the element at index 1, since index 0 is the <template>.
expect(rootTC.debugElement.componentViewChildren[1].nativeElement)
// Get the element at index 2, since index 0 is the <template>.
expect(DOM.childNodes(rootTC.debugElement.nativeElement)[2])
.toHaveText("1-hello");
async.done();
@ -1217,8 +1216,9 @@ export function main() {
describe("error handling", () => {
it('should report a meaningful error when a directive is missing annotation',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb = tcb.overrideView(MyComp,
new ViewMetadata({directives: [SomeDirectiveMissingAnnotation]}));
tcb = tcb.overrideView(
MyComp,
new ViewMetadata({template: '', directives: [SomeDirectiveMissingAnnotation]}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1229,19 +1229,20 @@ export function main() {
}));
it('should report a meaningful error when a component is missing view annotation',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
PromiseWrapper.catchError(tcb.createAsync(ComponentWithoutView), (e) => {
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
try {
tcb.createAsync(ComponentWithoutView);
} catch (e) {
expect(e.message).toEqual(
`No View annotation found on component ${stringify(ComponentWithoutView)}`);
async.done();
return null;
});
}
}));
it('should report a meaningful error when a directive is null',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]]}));
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]], template: ''}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1353,7 +1354,8 @@ export function main() {
var undefinedValue;
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [undefinedValue]}));
tcb = tcb.overrideView(MyComp,
new ViewMetadata({directives: [undefinedValue], template: ''}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1457,7 +1459,7 @@ export function main() {
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
`Can't bind to 'unknown' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
`Template parse errors:\nCan't bind to 'unknown' since it isn't a known native property in MyComp > div:nth-child(0)[unknown={{ctxProp}}]`);
async.done();
return null;
});
@ -1516,9 +1518,8 @@ export function main() {
describe('logging property updates', () => {
beforeEachBindings(() => [
bind(ChangeDetection)
.toValue(
new DynamicChangeDetection(new ChangeDetectorGenConfig(true, true, true, false)))
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, true, false))
]);
it('should reflect property values as attributes',

View File

@ -522,7 +522,8 @@ class OuterWithIndirectNestedComponent {
@Component({selector: 'outer'})
@View({
template: 'OUTER(<inner><ng-content></ng-content></inner>)',
template:
'OUTER(<inner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></inner>)',
directives: [forwardRef(() => InnerComponent)]
})
class OuterComponent {
@ -530,7 +531,8 @@ class OuterComponent {
@Component({selector: 'inner'})
@View({
template: 'INNER(<innerinner><ng-content></ng-content></innerinner>)',
template:
'INNER(<innerinner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></innerinner>)',
directives: [forwardRef(() => InnerInnerComponent)]
})
class InnerComponent {

View File

@ -13,7 +13,6 @@ import {
import {SpyChangeDetection} from '../spies';
import {isBlank, stringify} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {
ChangeDetection,
@ -24,10 +23,7 @@ import {
} from 'angular2/src/core/change_detection/change_detection';
import {
BindingRecordsCreator,
ProtoViewFactory,
getChangeDetectorDefinitions,
createDirectiveVariableBindings,
createVariableLocations
getChangeDetectorDefinitions
} from 'angular2/src/core/compiler/proto_view_factory';
import {Component, Directive} from 'angular2/src/core/metadata';
import {Key, Binding} from 'angular2/core';
@ -43,18 +39,14 @@ import {
} from 'angular2/src/core/render/api';
export function main() {
// TODO(tbosch): add missing tests
describe('ProtoViewFactory', () => {
var changeDetection;
var protoViewFactory: ProtoViewFactory;
var directiveResolver;
beforeEach(() => {
directiveResolver = new DirectiveResolver();
changeDetection = new SpyChangeDetection();
changeDetection.prop("generateDetectors", true);
protoViewFactory = new ProtoViewFactory(changeDetection);
});
function bindDirective(type) {
@ -73,107 +65,6 @@ export function main() {
});
describe('createAppProtoViews', () => {
it('should create an AppProtoView for the root render proto view', () => {
var varBindings = new Map();
varBindings.set('a', 'b');
var renderPv = createRenderProtoView([], null, varBindings);
var appPvs =
protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, [], []);
expect(appPvs[0].variableBindings.get('a')).toEqual('b');
expect(appPvs.length).toBe(1);
});
});
describe("createDirectiveVariableBindings", () => {
it("should calculate directive variable bindings", () => {
var dvbs = createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"exportName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'otherName'})})
]);
expect(dvbs).toEqual(MapWrapper.createFromStringMap<number>({"templateName": 0}));
});
it("should set exportAs to $implicit for component with exportAs = null", () => {
var dvbs = createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"$implicit": "templateName"})
}),
[
directiveBinding({
metadata: RenderDirectiveMetadata.create(
{exportAs: null, type: RenderDirectiveMetadata.COMPONENT_TYPE})
})
]);
expect(dvbs).toEqual(MapWrapper.createFromStringMap<number>({"templateName": 0}));
});
it("should throw we no directive exported with this name", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"someInvalidName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).toThrowError(new RegExp("Cannot find directive with exportAs = 'someInvalidName'"));
});
it("should throw when binding to a name exported by two directives", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"exportName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).toThrowError(new RegExp("More than one directive have exportAs = 'exportName'"));
});
it("should not throw when not binding to a name exported by two directives", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({variableBindings: new Map<string, string>()}), [
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).not.toThrow();
});
});
describe('createVariableLocations', () => {
it('should merge the names in the template for all ElementBinders', () => {
expect(createVariableLocations([
new RenderElementBinder(
{variableBindings: MapWrapper.createFromStringMap<string>({"x": "a"})}),
new RenderElementBinder(
{variableBindings: MapWrapper.createFromStringMap<string>({"y": "b"})})
])).toEqual(MapWrapper.createFromStringMap<number>({'a': 0, 'b': 1}));
});
});
describe('BindingRecordsCreator', () => {
var creator: BindingRecordsCreator;
@ -247,15 +138,6 @@ function createRenderProtoView(elementBinders = null, type: ViewType = null,
});
}
function createRenderComponentElementBinder(directiveIndex) {
return new RenderElementBinder(
{directives: [new DirectiveBinder({directiveIndex: directiveIndex})]});
}
function createRenderViewportElementBinder(nestedProtoView) {
return new RenderElementBinder({nestedProtoView: nestedProtoView});
}
@Component({selector: 'main-comp'})
class MainComponent {
}

View File

@ -34,7 +34,7 @@ export function main() {
viewManager = new SpyAppViewManager();
view = new SpyView();
view.prop("viewContainers", [null]);
location = new ElementRef(new ViewRef(view), 0, 0, null);
location = new ElementRef(new ViewRef(view), 0, null);
});
describe('length', () => {

View File

@ -13,15 +13,10 @@ import {
it,
xit
} from 'angular2/test_lib';
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener} from '../spies';
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener, SpyProtoViewFactory} from '../spies';
import {Injector, bind} from 'angular2/core';
import {
AppProtoView,
AppView,
AppViewContainer,
AppProtoViewMergeMapping
} from 'angular2/src/core/compiler/view';
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {TemplateRef} from 'angular2/src/core/compiler/template_ref';
@ -54,6 +49,7 @@ export function main() {
var utils: AppViewManagerUtils;
var viewListener;
var viewPool;
var linker;
var manager: AppViewManager;
var createdRenderViews: RenderViewWithFragments[];
@ -78,7 +74,8 @@ export function main() {
utils = new AppViewManagerUtils();
viewListener = new SpyAppViewListener();
viewPool = new SpyAppViewPool();
manager = new AppViewManager(viewPool, viewListener, utils, renderer);
linker = new SpyProtoViewFactory();
manager = new AppViewManager(viewPool, viewListener, utils, renderer, linker);
createdRenderViews = [];
renderer.spy('createRootHostView')
@ -110,6 +107,11 @@ export function main() {
beforeEach(
() => { hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]); });
it('should initialize the ProtoView', () => {
manager.createRootHostView(wrapPv(hostProtoView), null, null);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(hostProtoView);
});
it('should create the view', () => {
var rootView =
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
@ -129,8 +131,8 @@ export function main() {
var rootView =
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
expect(renderer.spy('createRootHostView'))
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
hostProtoView.mergeMapping.renderFragmentCount, 'someComponent');
.toHaveBeenCalledWith(hostProtoView.render,
hostProtoView.mergeInfo.embeddedViewCount + 1, 'someComponent');
expect(rootView.render).toBe(createdRenderViews[0].viewRef);
expect(rootView.renderFragment).toBe(createdRenderViews[0].fragmentRefs[0]);
});
@ -139,8 +141,8 @@ export function main() {
var selector = 'someOtherSelector';
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), selector, null));
expect(renderer.spy('createRootHostView'))
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
hostProtoView.mergeMapping.renderFragmentCount, selector);
.toHaveBeenCalledWith(hostProtoView.render,
hostProtoView.mergeInfo.embeddedViewCount + 1, selector);
});
it('should set the event dispatcher', () => {
@ -182,7 +184,7 @@ export function main() {
});
describe('createViewInContainer', () => {
describe('createEmbeddedViewInContainer', () => {
describe('basic functionality', () => {
var hostView: AppView;
@ -200,6 +202,11 @@ export function main() {
resetSpies();
});
it('should initialize the ProtoView', () => {
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(childProtoView);
});
describe('create the first view', () => {
it('should create an AppViewContainer if not yet existing', () => {
@ -255,8 +262,8 @@ export function main() {
expect(childView).not.toBe(firstChildView);
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(childView);
expect(renderer.spy('createView'))
.toHaveBeenCalledWith(childProtoView.mergeMapping.renderProtoViewRef,
childProtoView.mergeMapping.renderFragmentCount);
.toHaveBeenCalledWith(childProtoView.render,
childProtoView.mergeInfo.embeddedViewCount + 1);
expect(childView.render).toBe(createdRenderViews[1].viewRef);
expect(childView.renderFragment).toBe(createdRenderViews[1].fragmentRefs[0]);
});
@ -306,6 +313,12 @@ export function main() {
describe('create a host view', () => {
it('should initialize the ProtoView', () => {
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(newHostPv);
});
it('should always create a new view and not use the embedded view', () => {
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
var newHostView = internalView(
@ -314,8 +327,7 @@ export function main() {
expect(newHostView).not.toBe(hostView.views[2]);
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(newHostView);
expect(renderer.spy('createView'))
.toHaveBeenCalledWith(newHostPv.mergeMapping.renderProtoViewRef,
newHostPv.mergeMapping.renderFragmentCount);
.toHaveBeenCalledWith(newHostPv.render, newHostPv.mergeInfo.embeddedViewCount + 1);
});
});

View File

@ -18,7 +18,6 @@ import {
import {
SpyChangeDetector,
SpyProtoChangeDetector,
SpyProtoElementInjector,
SpyElementInjector,
SpyPreBuiltObjects
@ -26,9 +25,8 @@ import {
import {Injector, bind} from 'angular2/core';
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {AppProtoView, AppView, AppProtoViewMergeMapping} from 'angular2/src/core/compiler/view';
import {AppProtoView, AppView, AppProtoViewMergeInfo} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {
DirectiveBinding,
@ -208,16 +206,19 @@ export function createInjector() {
function createElementInjector(parent = null) {
var host = new SpyElementInjector();
var elementInjector = new SpyElementInjector();
var res = SpyObject.stub(elementInjector,
{
'isExportingComponent': false,
'isExportingElement': false,
'getEventEmitterAccessors': [],
'getHostActionAccessors': [],
'getComponent': new Object(),
'getHost': host
},
{});
var _preBuiltObjects = null;
var res = SpyObject.stub(elementInjector, {
'isExportingComponent': false,
'isExportingElement': false,
'getEventEmitterAccessors': [],
'getHostActionAccessors': [],
'getComponent': new Object(),
'getHost': host
});
res.spy('getNestedView').andCallFake(() => _preBuiltObjects.nestedView);
res.spy('hydrate')
.andCallFake((mperativelyCreatedInjector: Injector, host: ElementInjector,
preBuiltObjects: PreBuiltObjects) => { _preBuiltObjects = preBuiltObjects; });
res.prop('parent', parent);
return res;
}
@ -232,7 +233,7 @@ export function createProtoElInjector(parent: ProtoElementInjector = null): Prot
export function createEmptyElBinder(parent: ElementBinder = null) {
var parentPeli = isPresent(parent) ? parent.protoElementInjector : null;
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null);
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null, null);
}
export function createNestedElBinder(nestedProtoView: AppProtoView) {
@ -241,78 +242,35 @@ export function createNestedElBinder(nestedProtoView: AppProtoView) {
var annotation = new DirectiveResolver().resolve(SomeComponent);
componentBinding = DirectiveBinding.createFromType(SomeComponent, annotation);
}
var binder = new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding);
binder.nestedProtoView = nestedProtoView;
return binder;
}
function countNestedElementBinders(pv: AppProtoView): number {
var result = pv.elementBinders.length;
pv.elementBinders.forEach(binder => {
if (isPresent(binder.nestedProtoView)) {
result += countNestedElementBinders(binder.nestedProtoView);
}
});
return result;
}
function calcHostElementIndicesByViewIndex(pv: AppProtoView, elementOffset = 0,
target: number[] = null): number[] {
if (isBlank(target)) {
target = [null];
}
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
var binder = pv.elementBinders[binderIdx];
if (isPresent(binder.nestedProtoView)) {
target.push(elementOffset + binderIdx);
calcHostElementIndicesByViewIndex(binder.nestedProtoView,
elementOffset + pv.elementBinders.length, target);
elementOffset += countNestedElementBinders(binder.nestedProtoView);
}
}
return target;
}
function countNestedProtoViews(pv: AppProtoView, target: number[] = null): number[] {
if (isBlank(target)) {
target = [];
}
target.push(null);
var resultIndex = target.length - 1;
var count = 0;
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
var binder = pv.elementBinders[binderIdx];
if (isPresent(binder.nestedProtoView)) {
var nextResultIndex = target.length;
countNestedProtoViews(binder.nestedProtoView, target);
count += target[nextResultIndex] + 1;
}
}
target[resultIndex] = count;
return target;
return new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding, nestedProtoView);
}
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
if (isBlank(binders)) {
binders = [];
}
var protoChangeDetector = <any>new SpyProtoChangeDetector();
protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector());
var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0, null);
res.elementBinders = binders;
var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res));
var res = new AppProtoView([], type, true, (_) => new SpyChangeDetector(), new Map<string, any>(),
null);
var mergedElementCount = 0;
var mergedEmbeddedViewCount = 0;
var mergedViewCount = 1;
for (var i = 0; i < binders.length; i++) {
var binder = binders[i];
mappedElementIndices[i] = i;
binder.protoElementInjector.index = i;
mergedElementCount++;
var nestedPv = binder.nestedProtoView;
if (isPresent(nestedPv)) {
mergedElementCount += nestedPv.mergeInfo.elementCount;
mergedEmbeddedViewCount += nestedPv.mergeInfo.embeddedViewCount;
mergedViewCount += nestedPv.mergeInfo.viewCount;
if (nestedPv.type === ViewType.EMBEDDED) {
mergedEmbeddedViewCount++;
}
}
}
var hostElementIndicesByViewIndex = calcHostElementIndicesByViewIndex(res);
if (type === ViewType.EMBEDDED || type === ViewType.HOST) {
res.mergeMapping = new AppProtoViewMergeMapping(
new RenderProtoViewMergeMapping(null, hostElementIndicesByViewIndex.length,
mappedElementIndices, mappedElementIndices.length, [],
hostElementIndicesByViewIndex, countNestedProtoViews(res)));
}
var mergeInfo =
new AppProtoViewMergeInfo(mergedEmbeddedViewCount, mergedElementCount, mergedViewCount);
res.init(null, binders, 0, mergeInfo, new Map<string, number>());
return res;
}

View File

@ -24,12 +24,10 @@ export function main() {
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
function createProtoView() {
return new AppProtoView(null, null, null, null, null, null, null, null);
}
function createProtoView() { return new AppProtoView(null, null, null, null, null, null); }
function createView(pv) {
return new AppView(null, pv, null, null, null, null, new Map<string, any>(), null, null);
return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null);
}
it('should support multiple AppProtoViews', () => {