Skip to content

Commit e6c8bde

Browse files
committed
feat(Compiler): Multiple template per component
fixes angular#596 - TemplateConfig becomes Template - introduce a TemplateResolver to pick the cmp template, - @component and @template are disociated
1 parent 52b0626 commit e6c8bde

35 files changed

+955
-726
lines changed

modules/angular2/core.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export * from './src/core/annotations/annotations';
22
export * from './src/core/annotations/visibility';
33
export * from './src/core/compiler/interfaces';
4-
export * from './src/core/annotations/template_config';
4+
export * from './src/core/annotations/template';
55
export * from './src/core/application';
66

77
export * from './src/core/compiler/compiler';

modules/angular2/src/core/annotations/annotations.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {ABSTRACT, CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang';
22
import {ListWrapper, List} from 'angular2/src/facade/collection';
3-
import {TemplateConfig} from './template_config';
43

54
@ABSTRACT()
65
export class Directive {
@@ -38,7 +37,6 @@ export class Directive {
3837

3938
export class Component extends Directive {
4039
//TODO: vsavkin: uncomment it once the issue with defining fields in a sublass works
41-
template:any; //TemplateConfig;
4240
lightDomServices:any; //List;
4341
shadowDomServices:any; //List;
4442
componentServices:any; //List;
@@ -48,7 +46,6 @@ export class Component extends Directive {
4846
constructor({
4947
selector,
5048
bind,
51-
template,
5249
lightDomServices,
5350
shadowDomServices,
5451
componentServices,
@@ -57,7 +54,6 @@ export class Component extends Directive {
5754
}:{
5855
selector:String,
5956
bind:Object,
60-
template:TemplateConfig,
6157
lightDomServices:List,
6258
shadowDomServices:List,
6359
componentServices:List,
@@ -73,7 +69,6 @@ export class Component extends Directive {
7369
lifecycle: lifecycle
7470
});
7571

76-
this.template = template;
7772
this.lightDomServices = lightDomServices;
7873
this.shadowDomServices = shadowDomServices;
7974
this.componentServices = componentServices;
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
22
import {List} from 'angular2/src/facade/collection';
33

4-
export class TemplateConfig {
4+
export class Template {
55
url:any; //string;
66
inline:any; //string;
77
directives:any; //List<Type>;
88
formatters:any; //List<Type>;
9-
source:any;//List<TemplateConfig>;
9+
source:any;//List<Template>;
10+
locale:any; //string
11+
device:any; //string
1012
@CONST()
1113
constructor({
1214
url,
1315
inline,
1416
directives,
1517
formatters,
16-
source
18+
source,
19+
locale,
20+
device
1721
}: {
1822
url: string,
1923
inline: string,
2024
directives: List<Type>,
2125
formatters: List<Type>,
22-
source: List<TemplateConfig>
26+
source: List<Template>,
27+
locale: string,
28+
device: string
2329
})
2430
{
2531
this.url = url;
2632
this.inline = inline;
2733
this.directives = directives;
2834
this.formatters = formatters;
2935
this.source = source;
36+
this.locale = locale;
37+
this.device = device;
3038
}
3139
}

modules/angular2/src/core/application.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {ProtoView} from './compiler/view';
66
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
77
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection, jitChangeDetection} from 'angular2/change_detection';
88
import {TemplateLoader} from './compiler/template_loader';
9+
import {TemplateResolver} from './compiler/template_resolver';
910
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
1011
import {DirectiveMetadata} from './compiler/directive_metadata';
1112
import {List, ListWrapper} from 'angular2/src/facade/collection';
@@ -28,6 +29,7 @@ var _rootBindings = [
2829
Compiler,
2930
CompilerCache,
3031
TemplateLoader,
32+
TemplateResolver,
3133
DirectiveMetadataReader,
3234
Parser,
3335
Lexer,
@@ -62,7 +64,7 @@ function _injectorBindings(appComponentType): List<Binding> {
6264

6365
bind(appViewToken).toAsyncFactory((changeDetection, compiler, injector, appElement,
6466
appComponentAnnotatedType, strategy, eventManager) => {
65-
return compiler.compile(appComponentAnnotatedType.type, null).then(
67+
return compiler.compile(appComponentAnnotatedType.type).then(
6668
(protoView) => {
6769
var appProtoView = ProtoView.createRootProtoView(protoView, appElement,
6870
appComponentAnnotatedType, changeDetection.createProtoChangeDetector('root'),

modules/angular2/src/core/compiler/compiler.js

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import {CompilePipeline} from './pipeline/compile_pipeline';
1111
import {CompileElement} from './pipeline/compile_element';
1212
import {createDefaultSteps} from './pipeline/default_steps';
1313
import {TemplateLoader} from './template_loader';
14+
import {TemplateResolver} from './template_resolver';
1415
import {DirectiveMetadata} from './directive_metadata';
1516
import {Component} from '../annotations/annotations';
17+
import {Template} from '../annotations/template';
1618
import {Content} from './shadow_dom_emulation/content_tag';
1719
import {ShadowDomStrategy} from './shadow_dom_strategy';
1820
import {CompileStep} from './pipeline/compile_step';
@@ -55,13 +57,15 @@ export class Compiler {
5557
_compiling:Map<Type, Promise>;
5658
_shadowDomStrategy: ShadowDomStrategy;
5759
_shadowDomDirectives: List<DirectiveMetadata>;
60+
_templateResolver: TemplateResolver;
5861

5962
constructor(changeDetection:ChangeDetection,
6063
templateLoader:TemplateLoader,
6164
reader: DirectiveMetadataReader,
6265
parser:Parser,
6366
cache:CompilerCache,
64-
shadowDomStrategy: ShadowDomStrategy) {
67+
shadowDomStrategy: ShadowDomStrategy,
68+
templateResolver: TemplateResolver) {
6569
this._changeDetection = changeDetection;
6670
this._reader = reader;
6771
this._parser = parser;
@@ -74,63 +78,71 @@ export class Compiler {
7478
for (var i = 0; i < types.length; i++) {
7579
ListWrapper.push(this._shadowDomDirectives, reader.read(types[i]));
7680
}
81+
this._templateResolver = templateResolver;
7782
}
7883

79-
createSteps(component:DirectiveMetadata):List<CompileStep> {
80-
var directives = []
81-
var cmpDirectives = ListWrapper.map(component.componentDirectives, (d) => this._reader.read(d));
82-
directives = ListWrapper.concat(directives, cmpDirectives);
83-
directives = ListWrapper.concat(directives, this._shadowDomDirectives);
84-
return createDefaultSteps(this._changeDetection, this._parser, component, directives,
84+
createSteps(component:Type, template: Template):List<CompileStep> {
85+
// Merge directive metadata (from the template and from the shadow dom strategy)
86+
var dirMetadata = [];
87+
var tplMetadata = ListWrapper.map(this._flattenDirectives(template),
88+
(d) => this._reader.read(d));
89+
dirMetadata = ListWrapper.concat(dirMetadata, tplMetadata);
90+
dirMetadata = ListWrapper.concat(dirMetadata, this._shadowDomDirectives);
91+
92+
var cmpMetadata = this._reader.read(component);
93+
94+
return createDefaultSteps(this._changeDetection, this._parser, cmpMetadata, dirMetadata,
8595
this._shadowDomStrategy);
8696
}
8797

88-
compile(component:Type, templateRoot:Element = null):Promise<ProtoView> {
89-
var protoView = this._compile(this._reader.read(component), templateRoot);
98+
compile(component: Type):Promise<ProtoView> {
99+
var protoView = this._compile(component);
90100
return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
91101
}
92102

93103
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
94-
_compile(cmpMetadata: DirectiveMetadata, templateRoot:Element = null) {
95-
var protoView = this._compilerCache.get(cmpMetadata.type);
104+
_compile(component: Type) {
105+
var protoView = this._compilerCache.get(component);
96106
if (isPresent(protoView)) {
97107
// The component has already been compiled into a ProtoView,
98108
// returns a resolved Promise.
99109
return protoView;
100110
}
101111

102-
var pvPromise = MapWrapper.get(this._compiling, cmpMetadata.type);
112+
var pvPromise = MapWrapper.get(this._compiling, component);
103113
if (isPresent(pvPromise)) {
104114
// The component is already being compiled, attach to the existing Promise
105115
// instead of re-compiling the component.
106116
// It happens when a template references a component multiple times.
107117
return pvPromise;
108118
}
109119

110-
var template = isBlank(templateRoot) ? this._templateLoader.load(cmpMetadata) : templateRoot;
120+
var template = this._templateResolver.resolve(component);
111121

112-
if (PromiseWrapper.isPromise(template)) {
113-
pvPromise = PromiseWrapper.then(template,
114-
(el) => this._compileTemplate(el, cmpMetadata),
115-
(_) => { throw new BaseException(`Failed to load the template for ${stringify(cmpMetadata.type)}`); }
122+
var tplElement = this._templateLoader.load(template);
123+
124+
if (PromiseWrapper.isPromise(tplElement)) {
125+
pvPromise = PromiseWrapper.then(tplElement,
126+
(el) => this._compileTemplate(template, el, component),
127+
(_) => { throw new BaseException(`Failed to load the template for ${stringify(component)}`); }
116128
);
117-
MapWrapper.set(this._compiling, cmpMetadata.type, pvPromise);
129+
MapWrapper.set(this._compiling, component, pvPromise);
118130
return pvPromise;
119131
}
120132

121-
return this._compileTemplate(template, cmpMetadata);
133+
return this._compileTemplate(template, tplElement, component);
122134
}
123135

124136
// TODO(vicb): union type return ProtoView or Promise<ProtoView>
125-
_compileTemplate(template: Element, cmpMetadata) {
126-
var pipeline = new CompilePipeline(this.createSteps(cmpMetadata));
127-
var compileElements = pipeline.process(template);
137+
_compileTemplate(template: Template, tplElement: Element, component: Type) {
138+
var pipeline = new CompilePipeline(this.createSteps(component, template));
139+
var compileElements = pipeline.process(tplElement);
128140
var protoView = compileElements[0].inheritedProtoView;
129141

130142
// Populate the cache before compiling the nested components,
131143
// so that components can reference themselves in their template.
132-
this._compilerCache.set(cmpMetadata.type, protoView);
133-
MapWrapper.delete(this._compiling, cmpMetadata.type);
144+
this._compilerCache.set(component, protoView);
145+
MapWrapper.delete(this._compiling, component);
134146

135147
// Compile all the components from the template
136148
var nestedPVPromises = [];
@@ -146,17 +158,16 @@ export class Compiler {
146158
// The promise will resolved after nested ProtoViews are compiled.
147159
return PromiseWrapper.then(PromiseWrapper.all(nestedPVPromises),
148160
(_) => protoView,
149-
(e) => { throw new BaseException(`${e.message} -> Failed to compile ${stringify(cmpMetadata.type)}`); }
161+
(e) => { throw new BaseException(`${e.message} -> Failed to compile ${stringify(component)}`); }
150162
);
151163
}
152164

153165
// When there is no asynchronous nested ProtoViews, return the ProtoView
154166
return protoView;
155167
}
156168

157-
_compileNestedProtoView(ce: CompileElement, promises: List<Promise>)
158-
{
159-
var protoView = this._compile(ce.componentDirective);
169+
_compileNestedProtoView(ce: CompileElement, promises: List<Promise>) {
170+
var protoView = this._compile(ce.componentDirective.type);
160171

161172
if (PromiseWrapper.isPromise(protoView)) {
162173
ListWrapper.push(promises, protoView);
@@ -167,4 +178,27 @@ export class Compiler {
167178
ce.inheritedElementBinder.nestedProtoView = protoView;
168179
}
169180
}
181+
182+
_flattenDirectives(template: Template):List<Type> {
183+
if (isBlank(template.directives)) return [];
184+
185+
var directives = [];
186+
this._flattenList(template.directives, directives);
187+
188+
return directives;
189+
}
190+
191+
_flattenList(tree:List<any>, out:List<Type>) {
192+
for (var i = 0; i < tree.length; i++) {
193+
var item = tree[i];
194+
if (ListWrapper.isList(item)) {
195+
this._flattenList(item, out);
196+
} else {
197+
ListWrapper.push(out, item);
198+
}
199+
}
200+
}
201+
170202
}
203+
204+
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import {Type} from 'angular2/src/facade/lang';
22
import {Directive} from 'angular2/src/core/annotations/annotations'
3-
import {List} from 'angular2/src/facade/collection'
4-
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
53

64
/**
75
* Combination of a type with the Directive annotation
86
*/
97
export class DirectiveMetadata {
108
type:Type;
119
annotation:Directive;
12-
componentDirectives:List<Type>;
1310

14-
constructor(type:Type,
15-
annotation:Directive,
16-
componentDirectives:List<Type>) {
11+
constructor(type:Type, annotation:Directive) {
1712
this.annotation = annotation;
1813
this.type = type;
19-
this.componentDirectives = componentDirectives;
2014
}
2115
}
Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
2-
import {List, ListWrapper} from 'angular2/src/facade/collection';
3-
import {Directive, Component} from '../annotations/annotations';
2+
import {Directive} from '../annotations/annotations';
43
import {DirectiveMetadata} from './directive_metadata';
54
import {reflector} from 'angular2/src/reflection/reflection';
6-
import {ShadowDom, ShadowDomStrategy, ShadowDomNative} from './shadow_dom_strategy';
75

86
export class DirectiveMetadataReader {
97
read(type:Type):DirectiveMetadata {
@@ -12,39 +10,12 @@ export class DirectiveMetadataReader {
1210
for (var i=0; i<annotations.length; i++) {
1311
var annotation = annotations[i];
1412

15-
if (annotation instanceof Component) {
16-
return new DirectiveMetadata(
17-
type,
18-
annotation,
19-
this.componentDirectivesMetadata(annotation)
20-
);
21-
}
22-
2313
if (annotation instanceof Directive) {
24-
return new DirectiveMetadata(type, annotation, null);
14+
return new DirectiveMetadata(type, annotation);
2515
}
2616
}
2717
}
2818
throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
2919
}
3020

31-
componentDirectivesMetadata(annotation:Component):List<Type> {
32-
var template = annotation.template;
33-
var result:List<Type> = ListWrapper.create();
34-
if (isPresent(template) && isPresent(template.directives)) {
35-
this._buildList(result, template.directives);
36-
}
37-
return result;
38-
}
39-
40-
_buildList(out:List<Type>, tree:List<any>) {
41-
for (var i = 0; i < tree.length; i++) {
42-
var item = tree[i];
43-
if (ListWrapper.isList(item)) {
44-
this._buildList(out, item);
45-
} else {
46-
ListWrapper.push(out, item);
47-
}
48-
}
49-
}
5021
}

0 commit comments

Comments
 (0)