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

Commit ae799c4

Browse files
committed
feat(platform): Make angular invoke web_component polyfills for browsers without native web_component implementations.
This reverts commit bc830ea. Conflicts: lib/core_dom/shadow_dom_component_factory.dart
1 parent f265cd5 commit ae799c4

14 files changed

+595
-20
lines changed

example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ dependencies:
55
path: ../
66
browser: any
77
unittest: any
8+
web_components: any
89

910
transformers:
1011
- angular
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
:host {
3+
border-top: 2px solid white;
4+
border-bottom: 2px solid white;
5+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
:host {
2+
display: block;
3+
margin-top: 5px;
4+
}
5+
6+
/* This must be shimmed with a polymer shim rule */
7+
polyfill-next-selector { content: ':host span:not([:host])'; }
8+
::content span {
9+
background-color: black;
10+
}
11+
12+
my-button::shadow .custom-class {
13+
background-color: rgba(0,0,0, .5);
14+
}
15+
16+
span {
17+
font-weight: bold;
18+
}
19+
20+
.custom-component {
21+
color: white;
22+
padding: 5px;
23+
}
24+
25+
.custom-component a {
26+
color: white;
27+
}
28+
29+
.red {
30+
background-color: #FF1F1F;
31+
}
32+
33+
.green {
34+
background-color: #1BC123;
35+
}
36+
37+
.blue {
38+
background-color: #4D90FE;
39+
}
40+
41+
.grey {
42+
background-color: #E0E0E0;
43+
}

example/web/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<li><a href="bouncing_balls.html">bouncing_balls.html</a></li>
66
<li><a href="hello_world.html">hello_world.html</a></li>
77
<li><a href="todo.html">todo.html</a></li>
8+
<li><a href="shadow_dom_components.html">shadow_dom_components.html</a></li>
89
</ul>
910
</body>
1011
</html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import 'dart:html';
2+
3+
import 'package:angular/angular.dart';
4+
import 'package:angular/animate/module.dart';
5+
import 'package:angular/application_factory.dart';
6+
import 'package:di/di.dart';
7+
8+
main() {
9+
var app = applicationFactory();
10+
app.modules.add(new Module()
11+
..bind(MyComponent)
12+
..bind(BracketButton));
13+
app.selector("body");
14+
app.run();
15+
}
16+
17+
@Component(
18+
selector: "my-component",
19+
publishAs: "ctrl",
20+
template: """
21+
<div class="custom-component" ng-class="ctrl.color">
22+
<span>Shadow [</span>
23+
<content></content>
24+
<span>]</span>
25+
<a href="#" ng-click="ctrl.on=!ctrl.on"><my-button>
26+
Toggle</my-button></a>
27+
<span ng-if="ctrl.on">off</span>
28+
<span ng-if="!ctrl.on">on</span>
29+
</div>
30+
""",
31+
cssUrl: "/css/shadow_dom_components.css")
32+
class MyComponent {
33+
@NgAttr('color')
34+
String color;
35+
36+
bool on = false;
37+
}
38+
39+
@Component(
40+
selector: "my-button",
41+
template: """<span class="custom-bracket">[[[<content>
42+
</content>]]]</span>""",
43+
cssUrl: "/css/shadow_dom_bracket.css")
44+
class BracketButton {
45+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!DOCTYPE html>
2+
3+
<html>
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Web Components test page</title>
8+
<style>
9+
body {
10+
margin: 0;
11+
/* Extra spacing at top and bottom */
12+
padding: 20px 20px 0 20px;
13+
min-height: 100%;
14+
font-family: sans-serif;
15+
}
16+
[ng-cloak] {
17+
display: none;
18+
}
19+
</style>
20+
21+
<script src="packages/web_components/platform.js"></script>
22+
<script src="packages/web_components/dart_support.js"></script>
23+
</head>
24+
25+
<body>
26+
<p ng-if="false">Just waiting for this demo app to load...</p>
27+
<div class="content" ng-cloak>
28+
<h1>Hi</h1>
29+
30+
<p>There should be <strong>three</strong> components here. One red,
31+
one green, one blue:</p>
32+
33+
<my-component color="red">I'm a red component</my-component>
34+
<my-component color="green">I'm a green component</my-component>
35+
<my-component color="blue">I'm a blue component</my-component>
36+
37+
<br />
38+
<my-component color="grey">I'm a
39+
<span>content span</span></my-component>
40+
<br />
41+
<div class="red">I'm just a div with the <code>.red</code> class</div>
42+
<div class="green">I'm just a div with the <code>.green</code> class</div>
43+
<div class="blue">I'm just a div with the <code>.blue</code> class</div>
44+
<br />
45+
<h4>Nesting</h4>
46+
47+
48+
<my-component color="red">
49+
<my-component color="green">
50+
<my-component color="blue">
51+
I'm a blue component.
52+
</my-component>
53+
</my-component>
54+
</my-component>
55+
56+
<my-component color="green">
57+
<my-component color="blue">
58+
<my-component color="red">
59+
I'm a red component.
60+
</my-component>
61+
</my-component>
62+
</my-component>
63+
64+
<my-component color="blue">
65+
<my-component color="red">
66+
<my-component color="green">
67+
I'm a green component.
68+
</my-component>
69+
</my-component>
70+
</my-component>
71+
</div>
72+
<script type="application/dart" src="shadow_dom_components.dart"></script>
73+
<script src="packages/browser/dart.js"></script>
74+
</body>
75+
</html>

lib/core/module.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ export "package:angular/core_dom/module_internal.dart" show
6565

6666
export "package:angular/core/module_internal.dart" show
6767
CacheStats,
68+
ComponentCssRewriter,
6869
ExceptionHandler,
6970
Interpolate,
7071
VmTurnZone,
72+
WebPlatform,
7173
PrototypeMap,
7274
RootScope,
7375
Scope,

lib/core_dom/module_internal.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ library angular.core.dom_internal;
33
import 'dart:async' as async;
44
import 'dart:convert' show JSON;
55
import 'dart:html' as dom;
6+
import 'dart:js' as js;
67

78
import 'package:di/di.dart';
89
import 'package:perf_api/perf_api.dart';
@@ -37,6 +38,7 @@ part 'event_handler.dart';
3738
part 'http.dart';
3839
part 'mustache.dart';
3940
part 'node_cursor.dart';
41+
part 'web_platform.dart';
4042
part 'selector.dart';
4143
part 'shadow_dom_component_factory.dart';
4244
part 'shadowless_shadow_root.dart';
@@ -72,6 +74,8 @@ class CoreDomModule extends Module {
7274
bind(TranscludingComponentFactory);
7375
bind(Content);
7476
bind(ContentPort, toValue: null);
77+
bind(ComponentCssRewriter);
78+
bind(WebPlatform);
7579

7680
bind(Http);
7781
bind(UrlRewriter);

lib/core_dom/shadow_dom_component_factory.dart

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ class ShadowDomComponentFactory implements ComponentFactory {
3434
cacheRegister.registerCache("ShadowDomComponentFactoryStyles", _styleElementCache);
3535
}
3636

37-
final Map<String, async.Future<dom.StyleElement>> _styleElementCache = {};
38-
39-
37+
final Map<_ComponentAssetKey, async.Future<dom.StyleElement>> _styleElementCache = {};
38+
4039
FactoryFn call(dom.Node node, DirectiveRef ref) {
4140
return (Injector injector) {
4241
var component = ref.annotation as Component;
@@ -47,8 +46,16 @@ class ShadowDomComponentFactory implements ComponentFactory {
4746
DirectiveMap directives = injector.getByKey(DIRECTIVE_MAP_KEY);
4847
NgBaseCss baseCss = component.useNgBaseCss ? injector.getByKey(NG_BASE_CSS_KEY) : null;
4948
// This is a bit of a hack since we are returning different type then we are.
50-
var componentFactory = new _ComponentFactory(node, ref.typeKey, component,
51-
injector.getByKey(NODE_TREE_SANITIZER_KEY), _expando, baseCss, _styleElementCache, _config);
49+
var componentFactory = new _ComponentFactory(node,
50+
ref.typeKey,
51+
component,
52+
injector.getByKey(NODE_TREE_SANITIZER_KEY),
53+
injector.getByKey(WEB_PLATFORM_KEY),
54+
injector.getByKey(COMPONENT_CSS_REWRITER_KEY),
55+
_expando,
56+
baseCss,
57+
_styleElementCache,
58+
_config);
5259
var controller = componentFactory.call(injector, scope, viewCache, http, templateCache,
5360
directives);
5461

@@ -72,7 +79,10 @@ class _ComponentFactory implements Function {
7279
final dom.NodeTreeSanitizer treeSanitizer;
7380
final Expando _expando;
7481
final NgBaseCss _baseCss;
75-
final Map<String, async.Future<dom.StyleElement>> _styleElementCache;
82+
final Map<_ComponentAssetKey, async.Future<dom.StyleElement>>
83+
_styleElementCache;
84+
final ComponentCssRewriter componentCssRewriter;
85+
final WebPlatform platform;
7686
final CompilerConfig _config;
7787

7888
dom.ShadowRoot shadowDom;
@@ -81,7 +91,8 @@ class _ComponentFactory implements Function {
8191
var controller;
8292

8393
_ComponentFactory(this.element, this.typeKey, this.component, this.treeSanitizer,
84-
this._expando, this._baseCss, this._styleElementCache, this._config);
94+
this.platform, this.componentCssRewriter, this._expando,
95+
this._baseCss, this._styleElementCache, this._config);
8596

8697
dynamic call(Injector injector, Scope scope,
8798
ViewCache viewCache, Http http, TemplateCache templateCache,
@@ -100,22 +111,57 @@ class _ComponentFactory implements Function {
100111
var cssUrls = _baseCss != null ?
101112
([]..addAll(_baseCss.urls)..addAll(component.cssUrls)) :
102113
component.cssUrls;
114+
var tag = element.tagName.toLowerCase();
103115
if (cssUrls.isNotEmpty) {
104-
cssFutures = cssUrls.map((cssUrl) => _styleElementCache.putIfAbsent(cssUrl, () =>
116+
cssFutures = cssUrls.map((cssUrl) => _styleElementCache.putIfAbsent(
117+
new _ComponentAssetKey(tag, cssUrl), () =>
105118
http.get(cssUrl, cache: templateCache)
106119
.then((resp) => resp.responseText,
107120
onError: (e) => '/*\n$e\n*/\n')
108-
.then((styleContent) => new dom.StyleElement()..appendText(styleContent))
121+
.then((String css) {
122+
123+
// Shim CSS if required
124+
if (platform.cssShimRequired) {
125+
css = platform.shimCss(css, selector: tag, cssUrl: cssUrl);
126+
}
127+
128+
// If a css rewriter is installed, run the css through a rewriter
129+
var styleElement = new dom.StyleElement()
130+
..appendText(componentCssRewriter(css, selector: tag,
131+
cssUrl: cssUrl));
132+
133+
// ensure there are no invalid tags or modifications
134+
treeSanitizer.sanitizeTree(styleElement);
135+
136+
// If the css shim is required, it means that scoping does not
137+
// work, and adding the style to the head of the document is
138+
// preferrable.
139+
if (platform.cssShimRequired) {
140+
dom.document.head.append(styleElement);
141+
}
142+
143+
return styleElement;
144+
})
109145
)).toList();
110146
} else {
111147
cssFutures = [new async.Future.value(null)];
112148
}
113-
var viewFuture = ComponentFactory._viewFuture(component, viewCache, directives);
149+
150+
var platformViewCache = new PlatformViewCache(viewCache, tag, platform);
151+
152+
var viewFuture = ComponentFactory._viewFuture(component, platformViewCache,
153+
directives);
154+
114155
TemplateLoader templateLoader = new TemplateLoader(
115156
async.Future.wait(cssFutures).then((Iterable<dom.StyleElement> cssList) {
116-
cssList
117-
.where((styleElement) => styleElement != null)
118-
.forEach((styleElement) => shadowDom.append(styleElement.clone(true)));
157+
// This prevents style duplication by only adding css to the shadow
158+
// root if there is a native implementation of shadow dom.
159+
if (!platform.cssShimRequired) {
160+
cssList.where((styleElement) => styleElement != null)
161+
.forEach((styleElement) {
162+
shadowDom.append(styleElement.clone(true));
163+
});
164+
}
119165
if (viewFuture != null) {
120166
return viewFuture.then((ViewFactory viewFactory) {
121167
return (!shadowScope.isAttached) ?
@@ -163,3 +209,33 @@ class _ComponentFactory implements Function {
163209
return shadowInjector;
164210
}
165211
}
212+
213+
class _ComponentAssetKey {
214+
final String tag;
215+
final String assetUrl;
216+
217+
final String _key;
218+
219+
_ComponentAssetKey(String tag, String assetUrl)
220+
: _key = "$tag|$assetUrl",
221+
this.tag = tag,
222+
this.assetUrl = assetUrl;
223+
224+
@override
225+
String toString() => _key;
226+
227+
@override
228+
int get hashCode => _key.hashCode;
229+
230+
bool operator ==(key) =>
231+
key is _ComponentAssetKey
232+
&& tag == key.tag
233+
&& assetUrl == key.assetUrl;
234+
}
235+
236+
@Injectable()
237+
class ComponentCssRewriter {
238+
String call(String css, { String selector, String cssUrl} ) {
239+
return css;
240+
}
241+
}

0 commit comments

Comments
 (0)