refactor(proto_view_factory): expose data for generating change detectors

Also consolidates metadata handling in `ElementInjector`

BREAKING CHANGE:
- renames `DirectiveMetadataReader` into `DirectiveResolver`
  and removes `src/core/compiler/directive_metadata`.

Fixes #1712
Fixes #1713
This commit is contained in:
Tobias Bosch
2015-05-11 17:59:39 -07:00
parent 5114411749
commit ecb068019b
33 changed files with 685 additions and 436 deletions
@@ -41,99 +41,126 @@ export function runCompilerCommonTests() {
return new DomCompiler(mockStepFactory, tplLoader);
}
it('should run the steps and build the AppProtoView of the root element', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler((parent, current, control) => {
current.inheritedProtoView.bindVariable('b', 'a');
});
compiler.compile(new ViewDefinition({
componentId: 'someComponent',
template: '<div></div>'
})).then( (protoView) => {
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
}));
async.done();
});
}));
describe('compile', () => {
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler((parent, current, control) => {
current.inheritedProtoView.bindVariable('b', 'a');
});
var dirMetadata = new DirectiveMetadata({id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
compiler.compileHost(dirMetadata).then( (protoView) => {
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element)).toEqual('CUSTOM')
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
}));
async.done();
});
}));
it('should use the inline template and compile in sync', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP);
compiler.compile(new ViewDefinition({
componentId: 'someId',
template: 'inline component'
})).then( (protoView) => {
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('inline component');
async.done();
});
}));
it('should load url templates', inject([AsyncTestCompleter], (async) => {
var urlData = MapWrapper.createFromStringMap({
'someUrl': 'url component'
});
var compiler = createCompiler(EMPTY_STEP, urlData);
compiler.compile(new ViewDefinition({
componentId: 'someId',
absUrl: 'someUrl'
})).then( (protoView) => {
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('url component');
async.done();
});
}));
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP, MapWrapper.create());
PromiseWrapper.catchError(compiler.compile(new ViewDefinition({
componentId: 'someId',
absUrl: 'someUrl'
})), (e) => {
expect(e.message).toContain(`Failed to load the template "someId"`);
async.done();
});
}));
it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => {
var subTasksCompleted = false;
var completer = PromiseWrapper.completer();
var compiler = createCompiler( (parent, current, control) => {
ListWrapper.push(mockStepFactory.subTaskPromises, completer.promise.then((_) => {
subTasksCompleted = true;
}));
});
// It should always return a Promise because the subtask is async
var pvPromise = compiler.compile(new ViewDefinition({
componentId: 'someId',
template: 'some component'
it('should run the steps and build the AppProtoView of the root element', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler((parent, current, control) => {
current.inheritedProtoView.bindVariable('b', 'a');
});
compiler.compile(new ViewDefinition({
componentId: 'someComponent',
template: '<div></div>'
})).then( (protoView) => {
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
}));
async.done();
});
}));
expect(pvPromise).toBePromise();
expect(subTasksCompleted).toEqual(false);
// The Promise should resolve after the subtask is ready
completer.resolve(null);
pvPromise.then((protoView) => {
expect(subTasksCompleted).toEqual(true);
async.done();
});
}));
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler((parent, current, control) => {
current.inheritedProtoView.bindVariable('b', 'a');
});
var dirMetadata = new DirectiveMetadata({id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
compiler.compileHost(dirMetadata).then( (protoView) => {
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element)).toEqual('CUSTOM')
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
'a': 'b'
}));
async.done();
});
}));
it('should use the inline template and compile in sync', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP);
compiler.compile(new ViewDefinition({
componentId: 'someId',
template: 'inline component'
})).then( (protoView) => {
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('inline component');
async.done();
});
}));
it('should load url templates', inject([AsyncTestCompleter], (async) => {
var urlData = MapWrapper.createFromStringMap({
'someUrl': 'url component'
});
var compiler = createCompiler(EMPTY_STEP, urlData);
compiler.compile(new ViewDefinition({
componentId: 'someId',
absUrl: 'someUrl'
})).then( (protoView) => {
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('url component');
async.done();
});
}));
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP, MapWrapper.create());
PromiseWrapper.catchError(compiler.compile(new ViewDefinition({
componentId: 'someId',
absUrl: 'someUrl'
})), (e) => {
expect(e.message).toContain(`Failed to load the template "someId"`);
async.done();
});
}));
it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => {
var subTasksCompleted = false;
var completer = PromiseWrapper.completer();
var compiler = createCompiler( (parent, current, control) => {
ListWrapper.push(mockStepFactory.subTaskPromises, completer.promise.then((_) => {
subTasksCompleted = true;
}));
});
// It should always return a Promise because the subtask is async
var pvPromise = compiler.compile(new ViewDefinition({
componentId: 'someId',
template: 'some component'
}));
expect(pvPromise).toBePromise();
expect(subTasksCompleted).toEqual(false);
// The Promise should resolve after the subtask is ready
completer.resolve(null);
pvPromise.then((protoView) => {
expect(subTasksCompleted).toEqual(true);
async.done();
});
}));
it('should return ProtoViews of type COMPONENT_VIEW_TYPE', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP);
compiler.compile(new ViewDefinition({
componentId: 'someId',
template: 'inline component'
})).then( (protoView) => {
expect(protoView.type).toEqual(ProtoViewDto.COMPONENT_VIEW_TYPE);
async.done();
});
}));
});
describe('compileHost', () => {
it('should return ProtoViews of type HOST_VIEW_TYPE', inject([AsyncTestCompleter], (async) => {
var compiler = createCompiler(EMPTY_STEP);
compiler.compileHost(someComponent).then( (protoView) => {
expect(protoView.type).toEqual(ProtoViewDto.HOST_VIEW_TYPE);
async.done();
});
}));
});
});
@@ -195,3 +222,9 @@ class FakeTemplateLoader extends TemplateLoader {
return PromiseWrapper.reject('Load failed');
}
}
var someComponent = new DirectiveMetadata({
selector: 'some-comp',
id: 'someComponent',
type: DirectiveMetadata.COMPONENT_TYPE
});
@@ -204,6 +204,14 @@ export function main() {
);
}).toThrowError(new RegExp('Only one component directive is allowed per element' ));
});
it('should sort the directives and store the component as the first directive', () => {
var results = process(
el('<some-comp some-decor></some-comp>')
);
expect(annotatedDirectives[results[0].directives[0].directiveIndex].id).toEqual('someComponent');
expect(annotatedDirectives[results[0].directives[1].directiveIndex].id).toEqual('someDirective');
});
});
});
}
@@ -239,6 +247,7 @@ var someComponent2 = new DirectiveMetadata({
var someDirective = new DirectiveMetadata({
selector: '[some-decor]',
id: 'someDirective',
type: DirectiveMetadata.DIRECTIVE_TYPE
});
@@ -303,4 +312,4 @@ var componentWithNonElementSelector = new DirectiveMetadata({
id: 'componentWithNonElementSelector',
selector: '[attr]',
type: DirectiveMetadata.COMPONENT_TYPE
});
});
+2 -1
View File
@@ -9,6 +9,7 @@ import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step'
import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control';
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
import {ProtoViewDto} from 'angular2/src/render/api';
export function main() {
describe('compile_pipeline', () => {
@@ -39,7 +40,7 @@ export function main() {
var element = el('<div><div><span viewroot><span></span></span></div></div>');
var pipeline = new CompilePipeline([new MockStep((parent, current, control) => {
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
current.inheritedProtoView = new ProtoViewBuilder(current.element);
current.inheritedProtoView = new ProtoViewBuilder(current.element, ProtoViewDto.EMBEDDED_VIEW_TYPE);
}
})]);
var results = pipeline.process(element);
@@ -3,6 +3,7 @@ import {MapWrapper} from 'angular2/src/facade/collection';
import {ViewSplitter} from 'angular2/src/render/dom/compiler/view_splitter';
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
import {ProtoViewDto} from 'angular2/src/render/api';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Lexer, Parser} from 'angular2/change_detection';
@@ -56,6 +57,7 @@ export function main() {
var results = createPipeline().process(rootElement);
expect(results[2].inheritedProtoView).not.toBe(null);
expect(results[2].inheritedProtoView).toBe(results[1].inheritedElementBinder.nestedProtoView);
expect(results[2].inheritedProtoView.type).toBe(ProtoViewDto.EMBEDDED_VIEW_TYPE);
expect(DOM.getOuterHTML(results[2].inheritedProtoView.rootElement)).toEqual('<template>a</template>');
});