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

Commit

Permalink
feat(platform): Make angular invoke web_component polyfills for brows…
Browse files Browse the repository at this point in the history
…ers without native web_component implementations.
  • Loading branch information
codelogic committed May 20, 2014
1 parent 7466489 commit 0c22a3b
Show file tree
Hide file tree
Showing 15 changed files with 585 additions and 18 deletions.
1 change: 1 addition & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dependencies:
path: ../
browser: any
unittest: any
web_components: any

transformers:
- angular
5 changes: 5 additions & 0 deletions example/web/css/shadow_dom_bracket.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

:host {
border-top: 2px solid white;
border-bottom: 2px solid white;
}
43 changes: 43 additions & 0 deletions example/web/css/shadow_dom_components.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
:host {
display: block;
margin-top: 5px;
}

/* This must be shimmed with a polymer shim rule */
polyfill-next-selector { content: ':host span:not([:host])'; }
::content span {
background-color: black;
}

my-button::shadow .custom-class {
background-color: rgba(0,0,0, .5);
}

span {
font-weight: bold;
}

.custom-component {
color: white;
padding: 5px;
}

.custom-component a {
color: white;
}

.red {
background-color: #FF1F1F;
}

.green {
background-color: #1BC123;
}

.blue {
background-color: #4D90FE;
}

.grey {
background-color: #E0E0E0;
}
1 change: 1 addition & 0 deletions example/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<li><a href="bouncing_balls.html">bouncing_balls.html</a></li>
<li><a href="hello_world.html">hello_world.html</a></li>
<li><a href="todo.html">todo.html</a></li>
<li><a href="shadow_dom_components.html">shadow_dom_components.html</a></li>
</ul>
</body>
</html>
45 changes: 45 additions & 0 deletions example/web/shadow_dom_components.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'dart:html';

import 'package:angular/angular.dart';
import 'package:angular/animate/module.dart';
import 'package:angular/application_factory.dart';
import 'package:di/di.dart';

main() {
var app = applicationFactory();
app.modules.add(new Module()
..bind(MyComponent)
..bind(BracketButton));
app.selector("body");
app.run();
}

@Component(
selector: "my-component",
publishAs: "ctrl",
template: """
<div class="custom-component" ng-class="ctrl.color">
<span>Shadow [</span>
<content></content>
<span>]</span>
<a href="#" ng-click="ctrl.on=!ctrl.on"><my-button>
Toggle</my-button></a>
<span ng-if="ctrl.on">off</span>
<span ng-if="!ctrl.on">on</span>
</div>
""",
cssUrl: "/css/shadow_dom_components.css")
class MyComponent {
@NgAttr('color')
String color;

bool on = false;
}

@Component(
selector: "my-button",
template: """<span class="custom-bracket">[[[<content>
</content>]]]</span>""",
cssUrl: "/css/shadow_dom_bracket.css")
class BracketButton {
}
75 changes: 75 additions & 0 deletions example/web/shadow_dom_components.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Components test page</title>
<style>
body {
margin: 0;
/* Extra spacing at top and bottom */
padding: 20px 20px 0 20px;
min-height: 100%;
font-family: sans-serif;
}
[ng-cloak] {
display: none;
}
</style>

<script src="packages/web_components/platform.js"></script>
<script src="packages/web_components/dart_support.js"></script>
</head>

<body>
<p ng-if="false">Just waiting for this demo app to load...</p>
<div class="content" ng-cloak>
<h1>Hi</h1>

<p>There should be <strong>three</strong> components here. One red,
one green, one blue:</p>

<my-component color="red">I'm a red component</my-component>
<my-component color="green">I'm a green component</my-component>
<my-component color="blue">I'm a blue component</my-component>

<br />
<my-component color="grey">I'm a
<span>content span</span></my-component>
<br />
<div class="red">I'm just a div with the <code>.red</code> class</div>
<div class="green">I'm just a div with the <code>.green</code> class</div>
<div class="blue">I'm just a div with the <code>.blue</code> class</div>
<br />
<h4>Nesting</h4>


<my-component color="red">
<my-component color="green">
<my-component color="blue">
I'm a blue component.
</my-component>
</my-component>
</my-component>

<my-component color="green">
<my-component color="blue">
<my-component color="red">
I'm a red component.
</my-component>
</my-component>
</my-component>

<my-component color="blue">
<my-component color="red">
<my-component color="green">
I'm a green component.
</my-component>
</my-component>
</my-component>
</div>
<script type="application/dart" src="shadow_dom_components.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions lib/core/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ export "package:angular/core_dom/module_internal.dart" show

export "package:angular/core/module_internal.dart" show
CacheStats,
ComponentCssRewriter,
ExceptionHandler,
Interpolate,
VmTurnZone,
WebPlatform,
PrototypeMap,
RootScope,
Scope,
Expand Down
4 changes: 4 additions & 0 deletions lib/core_dom/module_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ library angular.core.dom_internal;
import 'dart:async' as async;
import 'dart:convert' show JSON;
import 'dart:html' as dom;
import 'dart:js' as js;

import 'package:di/di.dart';
import 'package:perf_api/perf_api.dart';
Expand Down Expand Up @@ -32,6 +33,7 @@ part 'event_handler.dart';
part 'http.dart';
part 'mustache.dart';
part 'node_cursor.dart';
part 'web_platform.dart';
part 'selector.dart';
part 'shadow_dom_component_factory.dart';
part 'shadowless_shadow_root.dart';
Expand Down Expand Up @@ -62,6 +64,8 @@ class CoreDomModule extends Module {
bind(TranscludingComponentFactory);
bind(Content);
bind(ContentPort, toValue: null);
bind(ComponentCssRewriter);
bind(WebPlatform);

bind(Http);
bind(UrlRewriter);
Expand Down
98 changes: 87 additions & 11 deletions lib/core_dom/shadow_dom_component_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ShadowDomComponentFactory implements ComponentFactory {

ShadowDomComponentFactory(this._expando);

final Map<String, async.Future<dom.StyleElement>> _styleElementCache = {};
final Map<_ComponentAssetKey, async.Future<dom.StyleElement>> _styleElementCache = {};

FactoryFn call(dom.Node node, DirectiveRef ref) {
return (Injector injector) {
Expand All @@ -42,8 +42,15 @@ class ShadowDomComponentFactory implements ComponentFactory {
DirectiveMap directives = injector.get(DirectiveMap);
NgBaseCss baseCss = injector.get(NgBaseCss);
// This is a bit of a hack since we are returning different type then we are.
var componentFactory = new _ComponentFactory(node, ref.type, component,
injector.get(dom.NodeTreeSanitizer), _expando, baseCss, _styleElementCache);
var componentFactory = new _ComponentFactory(node,
ref.type,
component,
injector.get(dom.NodeTreeSanitizer),
injector.get(WebPlatform),
injector.get(ComponentCssRewriter),
_expando,
baseCss,
_styleElementCache);
var controller = componentFactory.call(injector, scope, viewCache, http, templateCache,
directives);

Expand All @@ -67,15 +74,19 @@ class _ComponentFactory implements Function {
final dom.NodeTreeSanitizer treeSanitizer;
final Expando _expando;
final NgBaseCss _baseCss;
final Map<String, async.Future<dom.StyleElement>> _styleElementCache;
final Map<_ComponentAssetKey, async.Future<dom.StyleElement>>
_styleElementCache;
final ComponentCssRewriter componentCssRewriter;
final WebPlatform platform;

dom.ShadowRoot shadowDom;
Scope shadowScope;
Injector shadowInjector;
var controller;

_ComponentFactory(this.element, this.type, this.component, this.treeSanitizer,
this._expando, this._baseCss, this._styleElementCache);
this.platform, this.componentCssRewriter, this._expando,
this._baseCss, this._styleElementCache);

dynamic call(Injector injector, Scope scope,
ViewCache viewCache, Http http, TemplateCache templateCache,
Expand All @@ -92,22 +103,57 @@ class _ComponentFactory implements Function {
// better work around is found.
Iterable<async.Future<dom.StyleElement>> cssFutures;
var cssUrls = []..addAll(_baseCss.urls)..addAll(component.cssUrls);
var tag = element.tagName.toLowerCase();
if (cssUrls.isNotEmpty) {
cssFutures = cssUrls.map((cssUrl) => _styleElementCache.putIfAbsent(cssUrl, () =>
cssFutures = cssUrls.map((cssUrl) => _styleElementCache.putIfAbsent(
new _ComponentAssetKey(tag, cssUrl), () =>
http.get(cssUrl, cache: templateCache)
.then((resp) => resp.responseText,
onError: (e) => '/*\n$e\n*/\n')
.then((styleContent) => new dom.StyleElement()..appendText(styleContent))
.then((String css) {

// Shim CSS if required
if (platform.cssShimRequired) {
css = platform.shimCss(css, selector: tag, cssUrl: cssUrl);
}

// If a css rewriter is installed, run the css through a rewriter
var styleElement = new dom.StyleElement()
..appendText(componentCssRewriter(css, selector: tag,
cssUrl: cssUrl));

// ensure there are no invalid tags or modifications
treeSanitizer.sanitizeTree(styleElement);

// If the css shim is required, it means that scoping does not
// work, and adding the style to the head of the document is
// preferrable.
if (platform.cssShimRequired) {
dom.document.head.append(styleElement);
}

return styleElement;
})
)).toList();
} else {
cssFutures = [new async.Future.value(null)];
}
var viewFuture = ComponentFactory._viewFuture(component, viewCache, directives);

var platformViewCache = new PlatformViewCache(viewCache, tag, platform);

var viewFuture = ComponentFactory._viewFuture(component, platformViewCache,
directives);

TemplateLoader templateLoader = new TemplateLoader(
async.Future.wait(cssFutures).then((Iterable<dom.StyleElement> cssList) {
cssList
.where((styleElement) => styleElement != null)
.forEach((styleElement) => shadowDom.append(styleElement.clone(true)));
// This prevents style duplication by only adding css to the shadow
// root if there is a native implementation of shadow dom.
if (!platform.cssShimRequired) {
cssList.where((styleElement) => styleElement != null)
.forEach((styleElement) {
shadowDom.append(styleElement.clone(true));
});
}
if (viewFuture != null) {
return viewFuture.then((ViewFactory viewFactory) {
return (!shadowScope.isAttached) ?
Expand Down Expand Up @@ -144,3 +190,33 @@ class _ComponentFactory implements Function {
return shadowInjector;
}
}

class _ComponentAssetKey {
final String tag;
final String assetUrl;

final String _key;

_ComponentAssetKey(String tag, String assetUrl)
: _key = "$tag|$assetUrl",
this.tag = tag,
this.assetUrl = assetUrl;

@override
String toString() => _key;

@override
int get hashCode => _key.hashCode;

bool operator ==(key) =>
key is _ComponentAssetKey
&& tag == key.tag
&& assetUrl == key.assetUrl;
}

@Injectable()
class ComponentCssRewriter {
String call(String css, { String selector, String cssUrl} ) {
return css;
}
}
6 changes: 3 additions & 3 deletions lib/core_dom/view_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class WalkingViewFactory implements ViewFactory {
@Injectable()
class ViewCache {
// _viewFactoryCache is unbounded
final _viewFactoryCache = new LruCache<String, ViewFactory>();
final viewFactoryCache = new LruCache<String, ViewFactory>();
final Http http;
final TemplateCache templateCache;
final Compiler compiler;
Expand All @@ -127,12 +127,12 @@ class ViewCache {
ViewCache(this.http, this.templateCache, this.compiler, this.treeSanitizer);

ViewFactory fromHtml(String html, DirectiveMap directives) {
ViewFactory viewFactory = _viewFactoryCache.get(html);
ViewFactory viewFactory = viewFactoryCache.get(html);
if (viewFactory == null) {
var div = new dom.DivElement();
div.setInnerHtml(html, treeSanitizer: treeSanitizer);
viewFactory = compiler(div.nodes, directives);
_viewFactoryCache.put(html, viewFactory);
viewFactoryCache.put(html, viewFactory);
}
return viewFactory;
}
Expand Down
Loading

0 comments on commit 0c22a3b

Please sign in to comment.