Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 08b32d2

Browse files
committed
Implemented template loader and block cache
1 parent 7400617 commit 08b32d2

File tree

3 files changed

+93
-21
lines changed

3 files changed

+93
-21
lines changed

lib/angular.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ class AngularModule extends Module {
127127
type(Interpolate, Interpolate);
128128
type(CacheFactory, CacheFactory);
129129
type(Http, Http);
130+
type(BlockCache, BlockCache);
131+
type(TemplateCache, TemplateCache);
130132

131133
value(ScopeDigestTTL, new ScopeDigestTTL(5));
132134

lib/block.dart

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ class Block implements ElementWrapper {
108108
if (ref.directive.isComponent) {
109109
//nodeModule.factory(type, new ComponentFactory(node, ref.directive), visibility: visibility);
110110
// TODO(misko): there should be no need to wrap function like this.
111-
nodeModule.factory(type, (Injector injector, Compiler compiler, Scope scope, Parser parser, Http $http) =>
112-
(new ComponentFactory(node, ref.directive))(injector, compiler, scope, parser, $http),
111+
nodeModule.factory(type, (Injector injector, Compiler compiler, Scope scope, Parser parser, BlockCache $blockCache) =>
112+
(new ComponentFactory(node, ref.directive))(injector, compiler, scope, parser, $blockCache),
113113
visibility: visibility);
114114
} else {
115115
nodeModule.type(type, type, visibility: visibility);
@@ -266,36 +266,45 @@ class ComponentFactory {
266266

267267
ComponentFactory(this.element, this.directive);
268268

269-
dynamic call(Injector injector, Compiler compiler, Scope scope, Parser parser, Http $http) {
269+
dynamic call(Injector injector, Compiler compiler, Scope scope,
270+
Parser parser, BlockCache $blockCache) {
270271
this.compiler = compiler;
271272
shadowDom = element.createShadowRoot();
272273
shadowScope = scope.$new(true);
273274
createAttributeMapping(scope, shadowScope, parser);
274-
var controller = createShadowInjector(injector).get(directive.type);
275275
if (directive.$cssUrl != null) {
276276
shadowDom.innerHtml = '<style>@import "${directive.$cssUrl}"</style>';
277277
}
278+
TemplateLoader templateLoader;
278279
if (directive.$template != null) {
279-
compileTemplate(directive.$template);
280+
var blockFuture = new async.Future.value().then((_) =>
281+
attachBlockToShadowDom($blockCache.fromHtml(directive.$template)));
282+
templateLoader = new TemplateLoader(blockFuture);
280283
} else if (directive.$templateUrl != null) {
281-
$http.
282-
getString(directive.$templateUrl).
283-
then((data) => shadowScope.$apply(() => compileTemplate(data)));
284+
var blockFuture = $blockCache.fromUrl(directive.$templateUrl)
285+
.then((BlockType blockType) => attachBlockToShadowDom(blockType));
286+
templateLoader = new TemplateLoader(blockFuture);
284287
}
288+
var controller =
289+
createShadowInjector(injector, templateLoader).get(directive.type);
285290
if (directive.$publishAs != null) {
286291
shadowScope[directive.$publishAs] = controller;
287292
}
288293
return controller;
289294
}
290295

291-
compileTemplate(html) {
292-
shadowDom.innerHtml += html;
293-
compiler(shadowDom.nodes)(shadowInjector, shadowDom.nodes);
296+
attachBlockToShadowDom(BlockType blockType) {
297+
var block = blockType(shadowInjector);
298+
shadowDom.nodes.addAll(block.elements);
299+
shadowInjector.get(Scope).$digest();
300+
return shadowDom;
294301
}
295302

296-
createShadowInjector(injector) {
297-
var shadowModule = new ScopeModule(shadowScope);
298-
shadowModule.type(directive.type, directive.type);
303+
createShadowInjector(injector, TemplateLoader templateLoader) {
304+
var shadowModule = new ScopeModule(shadowScope)
305+
..type(directive.type, directive.type)
306+
..value(TemplateLoader, templateLoader)
307+
..value(dom.ShadowRoot, shadowDom);
299308
shadowInjector = injector.createChild([shadowModule]);
300309
// TODO(misko): creazy hack to mark injector
301310
shadowInjector.instances[_SHADOW] = injector;
@@ -333,18 +342,70 @@ class ComponentFactory {
333342
}
334343
}
335344

345+
class BlockCache {
346+
Cache _blockCache;
347+
Http $http;
348+
TemplateCache $templateCache;
349+
Compiler compiler;
350+
351+
BlockCache(CacheFactory $cacheFactory, Http this.$http,
352+
TemplateCache this.$templateCache, Compiler this.compiler) {
353+
_blockCache = $cacheFactory('blocks');
354+
}
355+
356+
BlockType fromHtml(String html) {
357+
BlockType blockType = _blockCache.get(html);
358+
if (blockType == null) {
359+
var div = new dom.Element.tag('div');
360+
div.innerHtml = html;
361+
blockType = compiler(div.nodes);
362+
_blockCache.put(html, blockType);
363+
}
364+
return blockType;
365+
}
366+
367+
async.Future<BlockType> fromUrl(String url) {
368+
return $http.getString(url, cache: $templateCache).then((String tmpl) {
369+
return fromHtml(tmpl);
370+
});
371+
}
372+
}
373+
374+
/**
375+
* A convinience wrapper for "templates" cache.
376+
*/
377+
class TemplateCache implements Cache {
378+
Cache _cache;
379+
380+
TemplateCache(CacheFactory $cacheFactory) {
381+
_cache = $cacheFactory('templates');
382+
}
383+
384+
Object get(key) => _cache.get(key);
385+
Object put(key, Object value) => _cache.put(key, value);
386+
void remove(key) => _cache.remove(key);
387+
void removeAll() => _cache.removeAll();
388+
CacheInfo info() => _cache.info();
389+
void destroy() => _cache.destroy();
390+
}
391+
392+
class TemplateLoader {
393+
final async.Future<dom.ShadowRoot> _template;
394+
async.Future<dom.ShadowRoot> get template => _template;
395+
TemplateLoader(this._template);
396+
}
336397

337398
attrAccessorFactory(dom.Element element, String name) {
338399
return ([String value]) {
339400
if (value != null) {
340401
if (value == null) {
341-
element.removeAttribute(name);
402+
element.attributes.remove(name);
342403
} else {
343-
element.setAttribute(name, value);
404+
element.attributes[name] = value;
344405
}
345406
return value;
346407
} else {
347-
return element.getAttribute(name);
408+
return element.attributes[name];
348409
}
349410
};
350411
}

test/compiler_spec.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,9 @@ main() {
403403
Block block = blockType(injector, element);
404404
$rootScope.$digest();
405405

406-
expect(element.textWithShadow()).toEqual('OUTTER-_1:INNER_2(OUTTER-_1)');
406+
SimpleComponent.lastTemplateLoader.template.then(expectAsync1((_) {
407+
expect(element.textWithShadow()).toEqual('OUTTER-_1:INNER_2(OUTTER-_1)');
408+
}));
407409
}));
408410

409411
it('should create a component with IO', inject(() {
@@ -434,7 +436,9 @@ main() {
434436
var element = $(r'<div><publish-me></publish-me></div>');
435437
$compile(element)(injector, element);
436438
$rootScope.$apply();
437-
expect(element.textWithShadow()).toEqual('WORKED');
439+
PublishMeComponent.lastTemplateLoader.template.then(expectAsync1((_) {
440+
expect(element.textWithShadow()).toEqual('WORKED');
441+
}));
438442
}));
439443
});
440444

@@ -459,8 +463,10 @@ main() {
459463

460464
class SimpleComponent {
461465
static String $template = r'{{name}}{{sep}}{{$id}}(<content>SHADOW-CONTENT</content>)';
462-
SimpleComponent(Scope scope) {
466+
static TemplateLoader lastTemplateLoader;
467+
SimpleComponent(Scope scope, TemplateLoader templateLoader) {
463468
scope.name = 'INNER';
469+
lastTemplateLoader = templateLoader;
464470
}
465471
}
466472

@@ -477,7 +483,10 @@ class IoComponent {
477483
class PublishMeComponent {
478484
static String $template = r'<content>{{ctrlName.value}}</content>';
479485
static String $publishAs = 'ctrlName';
486+
static TemplateLoader lastTemplateLoader;
480487

481488
String value = 'WORKED';
482-
PublishMeComponent() {}
489+
PublishMeComponent(TemplateLoader templateLoader) {
490+
lastTemplateLoader = templateLoader;
491+
}
483492
}

0 commit comments

Comments
 (0)