feat(compiler): allow recursive components
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import {Injector, bind, OpaqueToken} from 'di/di';
|
||||
import {Type, FIELD, isBlank, isPresent, BaseException} from 'facade/lang';
|
||||
import {DOM, Element} from 'facade/dom';
|
||||
import {Compiler} from './compiler/compiler';
|
||||
import {Compiler, CompilerCache} from './compiler/compiler';
|
||||
import {ProtoView} from './compiler/view';
|
||||
import {Reflector, reflector} from 'reflection/reflection';
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
@ -17,7 +17,7 @@ var _rootInjector: Injector;
|
||||
|
||||
// Contains everything that is safe to share between applications.
|
||||
var _rootBindings = [
|
||||
bind(Reflector).toValue(reflector), Compiler, TemplateLoader, DirectiveMetadataReader, Parser, Lexer
|
||||
bind(Reflector).toValue(reflector), Compiler, CompilerCache, TemplateLoader, DirectiveMetadataReader, Parser, Lexer
|
||||
];
|
||||
|
||||
export var appViewToken = new OpaqueToken('AppView');
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Type, FIELD, isBlank, isPresent} from 'facade/lang';
|
||||
import {Type, FIELD, isBlank, isPresent, BaseException, stringify} from 'facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'facade/async';
|
||||
import {List, ListWrapper} from 'facade/collection';
|
||||
import {List, ListWrapper, MapWrapper} from 'facade/collection';
|
||||
import {DOM, Element} from 'facade/dom';
|
||||
|
||||
import {Parser} from 'change_detection/parser/parser';
|
||||
@ -14,6 +14,30 @@ import {TemplateLoader} from './template_loader';
|
||||
import {AnnotatedType} from './annotated_type';
|
||||
import {Component} from '../annotations/annotations';
|
||||
|
||||
/**
|
||||
* Cache that stores the ProtoView of the template of a component.
|
||||
* Used to prevent duplicate work and resolve cyclic dependencies.
|
||||
*/
|
||||
export class CompilerCache {
|
||||
_cache:Map;
|
||||
constructor() {
|
||||
this._cache = MapWrapper.create();
|
||||
}
|
||||
|
||||
set(component:Type, protoView:ProtoView) {
|
||||
MapWrapper.set(this._cache, component, protoView);
|
||||
}
|
||||
|
||||
get(component:Type):ProtoView {
|
||||
var result = MapWrapper.get(this._cache, component);
|
||||
if (isBlank(result)) {
|
||||
// need to normalize undefined to null so that type checking passes :-(
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The compiler loads and translates the html templates of components into
|
||||
* nested ProtoViews. To decompose its functionality it uses
|
||||
@ -23,10 +47,12 @@ export class Compiler {
|
||||
_templateLoader:TemplateLoader;
|
||||
_reader: DirectiveMetadataReader;
|
||||
_parser:Parser;
|
||||
constructor(templateLoader:TemplateLoader, reader: DirectiveMetadataReader, parser:Parser) {
|
||||
_compilerCache:CompilerCache;
|
||||
constructor(templateLoader:TemplateLoader, reader: DirectiveMetadataReader, parser:Parser, cache:CompilerCache) {
|
||||
this._templateLoader = templateLoader;
|
||||
this._reader = reader;
|
||||
this._parser = parser;
|
||||
this._compilerCache = cache;
|
||||
}
|
||||
|
||||
createSteps(component:AnnotatedType):List<CompileStep> {
|
||||
@ -40,15 +66,22 @@ export class Compiler {
|
||||
}
|
||||
|
||||
compile(component:Type, templateRoot:Element = null):Promise<ProtoView> {
|
||||
// TODO load all components transitively from the cache first
|
||||
var cache = null;
|
||||
return PromiseWrapper.resolve(this.compileWithCache(
|
||||
cache, this._reader.annotatedType(component), templateRoot)
|
||||
var templateCache = null;
|
||||
// TODO load all components that have urls
|
||||
// transitively via the _templateLoader and store them in templateCache
|
||||
|
||||
return PromiseWrapper.resolve(this.compileAllLoaded(
|
||||
templateCache, this._reader.annotatedType(component), templateRoot)
|
||||
);
|
||||
}
|
||||
|
||||
// public so that we can compile in sync in performance tests.
|
||||
compileWithCache(cache, component:AnnotatedType, templateRoot:Element = null):ProtoView {
|
||||
compileAllLoaded(templateCache, component:AnnotatedType, templateRoot:Element = null):ProtoView {
|
||||
var rootProtoView = this._compilerCache.get(component.type);
|
||||
if (isPresent(rootProtoView)) {
|
||||
return rootProtoView;
|
||||
}
|
||||
|
||||
if (isBlank(templateRoot)) {
|
||||
// TODO: read out the cache if templateRoot = null. Could contain:
|
||||
// - templateRoot string
|
||||
@ -57,15 +90,18 @@ export class Compiler {
|
||||
var annotation:any = component.annotation;
|
||||
templateRoot = DOM.createTemplate(annotation.template.inline);
|
||||
}
|
||||
|
||||
var pipeline = new CompilePipeline(this.createSteps(component));
|
||||
var compileElements = pipeline.process(templateRoot);
|
||||
var rootProtoView = compileElements[0].inheritedProtoView;
|
||||
// TODO: put the rootProtoView into the cache to support recursive templates!
|
||||
rootProtoView = compileElements[0].inheritedProtoView;
|
||||
// Save the rootProtoView before we recurse so that we are able
|
||||
// to compile components that use themselves in their template.
|
||||
this._compilerCache.set(component.type, rootProtoView);
|
||||
|
||||
for (var i=0; i<compileElements.length; i++) {
|
||||
var ce = compileElements[i];
|
||||
if (isPresent(ce.componentDirective)) {
|
||||
ce.inheritedElementBinder.nestedProtoView = this.compileWithCache(cache, ce.componentDirective, null);
|
||||
ce.inheritedElementBinder.nestedProtoView = this.compileAllLoaded(templateCache, ce.componentDirective, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user