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

Commit

Permalink
feat(compiler): Shadow DOM-less components
Browse files Browse the repository at this point in the history
  • Loading branch information
jbdeboer authored and [email protected] committed Apr 23, 2014
1 parent 3bc5ec9 commit 26ad880
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 27 deletions.
6 changes: 6 additions & 0 deletions lib/core_dom/module_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ part 'mustache.dart';
part 'node_cursor.dart';
part 'selector.dart';
part 'shadow_dom_component_factory.dart';
part 'shadowless_shadow_root.dart';
part 'tagging_compiler.dart';
part 'tagging_view_factory.dart';
part 'template_cache.dart';
part 'transcluding_component_factory.dart';
part 'tree_sanitizer.dart';
part 'walking_compiler.dart';
part 'ng_element.dart';
Expand All @@ -53,7 +55,11 @@ class CoreDomModule extends Module {
type(AttrMustache);

type(Compiler, implementedBy: TaggingCompiler);

type(ComponentFactory, implementedBy: ShadowDomComponentFactory);
type(Content);
value(ContentPort, null);

type(Http);
type(UrlRewriter);
type(HttpBackend);
Expand Down
40 changes: 24 additions & 16 deletions lib/core_dom/shadow_dom_component_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ part of angular.core.dom_internal;

abstract class ComponentFactory {
FactoryFn call(dom.Node node, DirectiveRef ref);

static async.Future<ViewFactory> _viewFuture(
Component component, ViewCache viewCache, DirectiveMap directives) {
if (component.template != null) {
return new async.Future.value(viewCache.fromHtml(component.template, directives));
}
if (component.templateUrl != null) {
return viewCache.fromUrl(component.templateUrl, directives);
}
return null;
}

static TemplateLoader _setupOnShadowDomAttach(controller, templateLoader, shadowScope) {
if (controller is ShadowRootAware) {
templateLoader.template.then((shadowDom) {
if (!shadowScope.isAttached) return;
(controller as ShadowRootAware).onShadowRoot(shadowDom);
});
}
}
}

class ShadowDomComponentFactory implements ComponentFactory {
Expand All @@ -12,7 +32,6 @@ class ShadowDomComponentFactory implements ComponentFactory {
FactoryFn call(dom.Node node, DirectiveRef ref) {
return (Injector injector) {
var component = ref.annotation as Component;
Compiler compiler = injector.get(Compiler);
Scope scope = injector.get(Scope);
ViewCache viewCache = injector.get(ViewCache);
Http http = injector.get(Http);
Expand Down Expand Up @@ -78,13 +97,7 @@ class _ComponentFactory implements Function {
} else {
cssFutures.add(new async.Future.value(null));
}
var viewFuture;
if (component.template != null) {
viewFuture = new async.Future.value(viewCache.fromHtml(
component.template, directives));
} else if (component.templateUrl != null) {
viewFuture = viewCache.fromUrl(component.templateUrl, directives);
}
var viewFuture = ComponentFactory._viewFuture(component, viewCache, directives);
TemplateLoader templateLoader = new TemplateLoader(
async.Future.wait(cssFutures).then((Iterable<String> cssList) {
if (cssList != null) {
Expand All @@ -98,19 +111,14 @@ class _ComponentFactory implements Function {
if (viewFuture != null) {
return viewFuture.then((ViewFactory viewFactory) {
return (!shadowScope.isAttached) ?
shadowDom :
attachViewToShadowDom(viewFactory);
shadowDom :
attachViewToShadowDom(viewFactory);
});
}
return shadowDom;
}));
controller = createShadowInjector(injector, templateLoader).get(type);
if (controller is ShadowRootAware) {
templateLoader.template.then((_) {
if (!shadowScope.isAttached) return;
(controller as ShadowRootAware).onShadowRoot(shadowDom);
});
}
ComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);
return controller;
}

Expand Down
12 changes: 12 additions & 0 deletions lib/core_dom/shadowless_shadow_root.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
part of angular.core.dom_internal;

@proxy
class ShadowlessShadowRoot implements dom.ShadowRoot {
dom.Element _element;

ShadowlessShadowRoot(this._element);

noSuchMethod(Invocation invocation) {
throw new UnimplementedError("Not yet implemented in ShadowlessShadowRoot.");
}
}
121 changes: 121 additions & 0 deletions lib/core_dom/transcluding_component_factory.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
part of angular.core.dom_internal;

@Decorator(
selector: 'content')
class Content implements AttachAware, DetachAware {
final ContentPort _port;
final dom.Element _element;
dom.Comment _beginComment;
Content(this._port, this._element);

void attach() {
if (_port == null) return;
_beginComment = _port.content(_element);
}

void detach() {
if (_port == null) return;
_port.detachContent(_beginComment);
}
}

class ContentPort {
dom.Element _element;
var _childNodes = [];

ContentPort(this._element);

void pullNodes() {
_childNodes.addAll(_element.nodes);
_element.nodes = [];
}

content(dom.Element elt) {
var hash = elt.hashCode;
var beginComment = new dom.Comment("content $hash");

if (_childNodes.isNotEmpty) {
elt.parent.insertBefore(beginComment, elt);
elt.parent.insertAllBefore(_childNodes, elt);
elt.parent.insertBefore(new dom.Comment("end-content $hash"), elt);
_childNodes = [];
}
elt.remove();
return beginComment;
}

void detachContent(dom.Comment _beginComment) {
// Search for endComment and extract everything in between.
// TODO optimize -- there may be a better way of pulling out nodes.

var endCommentText = "end-${_beginComment.text}";

var next;
for (next = _beginComment.nextNode;
next.nodeType != dom.Node.COMMENT_NODE && next.text != endCommentText;
next = _beginComment.nextNode) {
_childNodes.add(next);
next.remove();
}
assert(next.nodeType == dom.Node.COMMENT_NODE && next.text == endCommentText);
next.remove();
}
}

class TranscludingComponentFactory implements ComponentFactory {
final Expando _expando;

TranscludingComponentFactory(this._expando);

FactoryFn call(dom.Node node, DirectiveRef ref) {
// CSS is not supported.
assert((ref.annotation as Component).cssUrls == null ||
(ref.annotation as Component).cssUrls.isEmpty);

var element = node as dom.Element;
return (Injector injector) {
var childInjector;
var component = ref.annotation as Component;
Scope scope = injector.get(Scope);
ViewCache viewCache = injector.get(ViewCache);
Http http = injector.get(Http);
TemplateCache templateCache = injector.get(TemplateCache);
DirectiveMap directives = injector.get(DirectiveMap);
NgBaseCss baseCss = injector.get(NgBaseCss);

var contentPort = new ContentPort(element);

// Append the component's template as children
var viewFuture = ComponentFactory._viewFuture(component, viewCache, directives);

if (viewFuture != null) {
viewFuture = viewFuture.then((ViewFactory viewFactory) {
contentPort.pullNodes();
element.nodes.addAll(viewFactory(childInjector).nodes);
return element;
});
} else {
viewFuture = new async.Future.microtask(() => contentPort.pullNodes());
}
TemplateLoader templateLoader = new TemplateLoader(viewFuture);

Scope shadowScope = scope.createChild({});

var probe;
var childModule = new Module()
..type(ref.type)
..type(NgElement)
..value(ContentPort, contentPort)
..value(Scope, shadowScope)
..value(TemplateLoader, templateLoader)
..value(dom.ShadowRoot, new ShadowlessShadowRoot(element))
..factory(ElementProbe, (_) => probe);
childInjector = injector.createChild([childModule], name: SHADOW_DOM_INJECTOR_NAME);

var controller = childInjector.get(ref.type);
shadowScope.context[component.publishAs] = controller;
ComponentFactory._setupOnShadowDomAttach(controller, templateLoader, shadowScope);

This comment has been minimized.

Copy link
@vicb

vicb May 1, 2014

Contributor

@jbdeboer Does this make sense for transcluding cmp ?

return controller;
};
}
}
2 changes: 2 additions & 0 deletions lib/directive/ng_template.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ class NgTemplate {
? (element as dom.TemplateElement).content.innerHtml
: element.innerHtml));
}


2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ packages:
barback:
description: barback
source: hosted
version: "0.13.0"
version: "0.12.0"
benchmark_harness:
description: benchmark_harness
source: hosted
Expand Down
Loading

6 comments on commit 26ad880

@bwnyasse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While launching my application , I've got the following error :

'package:angular/core_dom/transcluding_component_factory.dart': Failed assertion: line 72 pos 12: '(ref.annotation as Component).cssUrls == null ||
(ref.annotation as Component).cssUrls.isEmpty' is not true.

STACKTRACE:
#0 TranscludingComponentFactory.call (package:angular/core_dom/transcluding_component_factory.dart:72:12)
#1 ElementBinder._createDirectiveFactories (package:angular/core_dom/element_binder.dart:239:48)
#2 ElementBinder.bind. (package:angular/core_dom/element_binder.dart:282:34)
#3 List.forEach (dart:core-patch/growable_array.dart:240)

....

The TranscludingComponentFactory call function ensure that the CSS is null or empty.

Does it mean that we can't define a cssUrl for a Component anymore ?

@zoechi
Copy link
Contributor

@zoechi zoechi commented on 26ad880 Apr 23, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to shadow-DOM-less?
I thought this is what shadow-DOM-less is all about, that you don't have encapsulation and the page CSS applies to its content.

@bwnyasse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly , it is when I specify the option useShadowDom.

@vicb
Copy link
Contributor

@vicb vicb commented on 26ad880 Apr 23, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bwnyasse from your stack trace, the error is in "TranscludingComponentFactory".

May be you can put a small example in a gist ?

@jbdeboer
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bwnyasse Adding a cssUrl to a shadow DOM-less component is not allowed since we are not doing any style encapsulation with these components.

Style encapsulation will be implemented in the future; at this point we are collecting use-cases to understand how much of the shadow DOM spec we need to mimic.

Does your app behave as you want it to by moving all your CSS into document.head? Or do you need something else?

@bwnyasse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbdeboer Thank you for the answer. I've moved all my CSS file into document.head and everything is ok right now.

Please sign in to comment.