Skip to content

Commit d0ca07a

Browse files
vicbmhevery
authored andcommitted
refactor(Compiler): introduce ShimComponent to shim CSS & DOM in emulated mode
Closes angular#715
1 parent 5111f9a commit d0ca07a

File tree

12 files changed

+486
-705
lines changed

12 files changed

+486
-705
lines changed

modules/angular2/src/core/compiler/pipeline/default_steps.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import {ElementBindingMarker} from './element_binding_marker';
99
import {ProtoViewBuilder} from './proto_view_builder';
1010
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
1111
import {ElementBinderBuilder} from './element_binder_builder';
12-
import {ShadowDomTransformer} from './shadow_dom_transformer';
12+
import {ShimShadowCss} from './shim_shadow_css';
13+
import {ShimShadowDom} from './shim_shadow_dom';
1314
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
14-
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
15+
import {ShadowDomStrategy, EmulatedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
1516
import {stringify} from 'angular2/src/facade/lang';
1617
import {DOM} from 'angular2/src/facade/dom';
1718

@@ -31,8 +32,8 @@ export function createDefaultSteps(
3132

3233
var steps = [new ViewSplitter(parser, compilationUnit)];
3334

34-
if (!(shadowDomStrategy instanceof NativeShadowDomStrategy)) {
35-
var step = new ShadowDomTransformer(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head);
35+
if (shadowDomStrategy instanceof EmulatedShadowDomStrategy) {
36+
var step = new ShimShadowCss(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head);
3637
ListWrapper.push(steps, step);
3738
}
3839

@@ -46,5 +47,10 @@ export function createDefaultSteps(
4647
new ElementBinderBuilder(parser, compilationUnit)
4748
]);
4849

50+
if (shadowDomStrategy instanceof EmulatedShadowDomStrategy) {
51+
var step = new ShimShadowDom(compiledComponent, shadowDomStrategy);
52+
ListWrapper.push(steps, step);
53+
}
54+
4955
return steps;
5056
}

modules/angular2/src/core/compiler/pipeline/shadow_dom_transformer.js renamed to modules/angular2/src/core/compiler/pipeline/shim_shadow_css.js

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,20 @@ import {CompileControl} from './compile_control';
44

55
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
66
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
7-
import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css';
87

98
import {DOM, Element} from 'angular2/src/facade/dom';
10-
import {isPresent, isBlank} from 'angular2/src/facade/lang';
11-
import {StringMapWrapper} from 'angular2/src/facade/collection';
9+
import {isPresent, isBlank, Type} from 'angular2/src/facade/lang';
1210

13-
var _cssCache = StringMapWrapper.create();
14-
15-
export class ShadowDomTransformer extends CompileStep {
16-
_selector: string;
11+
export class ShimShadowCss extends CompileStep {
1712
_strategy: ShadowDomStrategy;
1813
_styleHost: Element;
1914
_lastInsertedStyle: Element;
15+
_component: Type;
2016

2117
constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy, styleHost: Element) {
2218
super();
2319
this._strategy = strategy;
24-
this._selector = cmpMetadata.annotation.selector;
20+
this._component = cmpMetadata.type;
2521
this._styleHost = styleHost;
2622
this._lastInsertedStyle = null;
2723
}
@@ -33,34 +29,13 @@ export class ShadowDomTransformer extends CompileStep {
3329
if (this._strategy.extractStyles()) {
3430
DOM.remove(current.element);
3531
var css = DOM.getText(current.element);
36-
if (this._strategy.shim()) {
37-
// The css generated here is unique for the component (because of the shim).
38-
// Then we do not need to cache it.
39-
css = shimCssText(css, this._selector);
40-
this._insertStyle(this._styleHost, css);
41-
} else {
42-
var seen = isPresent(StringMapWrapper.get(_cssCache, css));
43-
if (!seen) {
44-
StringMapWrapper.set(_cssCache, css, true);
45-
this._insertStyle(this._styleHost, css);
46-
}
47-
}
48-
}
49-
} else {
50-
if (this._strategy.shim()) {
51-
try {
52-
DOM.setAttribute(current.element, this._selector, '');
53-
} catch(e) {
54-
// TODO(vicb): for now only simple selector (tag name) are supported
55-
}
32+
var shimComponent = this._strategy.getShimComponent(this._component);
33+
css = shimComponent.shimCssText(css);
34+
this._insertStyle(this._styleHost, css);
5635
}
5736
}
5837
}
5938

60-
clearCache() {
61-
_cssCache = StringMapWrapper.create();
62-
}
63-
6439
_insertStyle(el: Element, css: string) {
6540
var style = DOM.createStyleElement(css);
6641
if (isBlank(this._lastInsertedStyle)) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {CompileStep} from './compile_step';
2+
import {CompileElement} from './compile_element';
3+
import {CompileControl} from './compile_control';
4+
5+
import {isPresent} from 'angular2/src/facade/lang';
6+
7+
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
8+
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
9+
10+
import {ShimComponent} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_component';
11+
12+
export class ShimShadowDom extends CompileStep {
13+
_strategy: ShadowDomStrategy;
14+
_shimComponent: ShimComponent;
15+
16+
constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy) {
17+
super();
18+
this._strategy = strategy;
19+
this._shimComponent = strategy.getShimComponent(cmpMetadata.type);
20+
}
21+
22+
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
23+
if (current.ignoreBindings) {
24+
return;
25+
}
26+
27+
// Shim the element as a child of the compiled component
28+
this._shimComponent.shimContentElement(current.element);
29+
30+
// If the current element is also a component, shim it as a host
31+
var host = current.componentDirective;
32+
if (isPresent(host)) {
33+
var shimComponent = this._strategy.getShimComponent(host.type);
34+
shimComponent.shimHostElement(current.element);
35+
}
36+
}
37+
}
38+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {Element, DOM} from 'angular2/src/facade/dom';
2+
import {Map, MapWrapper} from 'angular2/src/facade/collection';
3+
import {int, isBlank, Type} from 'angular2/src/facade/lang';
4+
5+
import {ShadowCss} from './shadow_css';
6+
7+
/**
8+
* Used to shim component CSS & DOM
9+
*/
10+
export class ShimComponent {
11+
constructor(component: Type) {
12+
}
13+
14+
shimCssText(cssText: string): string {
15+
return null
16+
}
17+
18+
shimContentElement(element: Element) {}
19+
20+
shimHostElement(element: Element) {}
21+
}
22+
23+
/**
24+
* Native components does not need to the shim.
25+
*
26+
* All methods are no-ops.
27+
*/
28+
export class ShimNativeComponent extends ShimComponent {
29+
constructor(component: Type) {
30+
super(component);
31+
};
32+
33+
shimCssText(cssText: string): string {
34+
return cssText;
35+
}
36+
37+
shimContentElement(element: Element) {
38+
}
39+
40+
shimHostElement(element: Element) {
41+
}
42+
}
43+
44+
var _componentCache: Map<Type, int> = MapWrapper.create();
45+
var _componentId: int = 0;
46+
47+
// Reset the component cache - used for tests only
48+
export function resetShimComponentCache() {
49+
MapWrapper.clear(_componentCache);
50+
_componentId = 0;
51+
}
52+
53+
/**
54+
* Emulated components need to be shimmed:
55+
* - An attribute needs to be added to the host,
56+
* - An attribute needs to be added to all nodes in their content,
57+
* - The CSS needs to be scoped.
58+
*/
59+
export class ShimEmulatedComponent extends ShimComponent {
60+
_cmpId: int;
61+
62+
constructor(component: Type) {
63+
super(component);
64+
65+
// Generates a unique ID for components
66+
var componentId = MapWrapper.get(_componentCache, component);
67+
if (isBlank(componentId)) {
68+
componentId = _componentId++;
69+
MapWrapper.set(_componentCache, component, componentId);
70+
}
71+
this._cmpId = componentId;
72+
};
73+
74+
// Scope the CSS
75+
shimCssText(cssText: string): string {
76+
var shadowCss = new ShadowCss();
77+
return shadowCss.shimCssText(cssText, this._getContentAttribute(), this._getHostAttribute());
78+
}
79+
80+
// Add an attribute on a content element
81+
shimContentElement(element: Element) {
82+
DOM.setAttribute(element, this._getContentAttribute(), '');
83+
}
84+
85+
// Add an attribute to the host
86+
shimHostElement(element: Element) {
87+
DOM.setAttribute(element, this._getHostAttribute(), '');
88+
}
89+
90+
// Return the attribute to be added to the component
91+
_getHostAttribute() {
92+
return `_nghost-${this._cmpId}`;
93+
}
94+
95+
// Returns the attribute to be added on every single nodes in the component
96+
_getContentAttribute() {
97+
return `_ngcontent-${this._cmpId}`;
98+
}
99+
}

0 commit comments

Comments
 (0)