Skip to content

Commit 3ea6559

Browse files
committed
refactor(Compiler): inline styles before compiling the template
1 parent 3875f02 commit 3ea6559

30 files changed

+288
-468
lines changed

modules/angular2/src/core/application.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
} from 'angular2/change_detection';
2626
import {ExceptionHandler} from './exception_handler';
2727
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
28+
import {StyleUrlResolver} from 'angular2/src/render/dom/compiler/style_url_resolver';
29+
import {StyleInliner} from 'angular2/src/render/dom/compiler/style_inliner';
2830
import {TemplateResolver} from './compiler/template_resolver';
2931
import {DirectiveResolver} from './compiler/directive_resolver';
3032
import {List, ListWrapper} from 'angular2/src/facade/collection';
@@ -42,8 +44,6 @@ import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
4244
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
4345
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
4446
import {UrlResolver} from 'angular2/src/services/url_resolver';
45-
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
46-
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
4747
import {AppRootUrl} from 'angular2/src/services/app_root_url';
4848
import {
4949
ComponentRef,
@@ -105,9 +105,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
105105
},
106106
[NgZone]),
107107
bind(ShadowDomStrategy)
108-
.toFactory((styleInliner, styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(
109-
styleInliner, styleUrlResolver, doc.head),
110-
[StyleInliner, StyleUrlResolver, DOCUMENT_TOKEN]),
108+
.toFactory((doc) => new EmulatedUnscopedShadowDomStrategy(doc.head), [DOCUMENT_TOKEN]),
111109
DomRenderer,
112110
DefaultDomCompiler,
113111
bind(Renderer).toAlias(DomRenderer),

modules/angular2/src/core/compiler/template_resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class TemplateResolver {
2222
return view;
2323
}
2424

25-
_resolve(component: Type) {
25+
_resolve(component: Type): View {
2626
var annotations = reflector.annotations(component);
2727
for (var i = 0; i < annotations.length; i++) {
2828
var annotation = annotations[i];

modules/angular2/src/dom/html_adapter.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ class Html5LibDomAdapter implements DomAdapter {
6767
throw 'not implemented';
6868
}
6969
querySelector(el, String selector) {
70-
throw 'not implemented';
70+
return el.querySelector(selector);
7171
}
7272
List querySelectorAll(el, String selector) {
73-
throw 'not implemented';
73+
return el.querySelectorAll(selector);
7474
}
7575
on(el, evt, listener) {
7676
throw 'not implemented';
@@ -94,7 +94,7 @@ class Html5LibDomAdapter implements DomAdapter {
9494
return el.innerHtml;
9595
}
9696
getOuterHTML(el) {
97-
throw 'not implemented';
97+
return el.outerHtml;
9898
}
9999
String nodeName(node) {
100100
switch (node.nodeType) {
@@ -130,12 +130,12 @@ class Html5LibDomAdapter implements DomAdapter {
130130
}
131131

132132
parentElement(el) {
133-
throw 'not implemented';
133+
return el.parent;
134134
}
135135
List childNodes(el) => el.nodes;
136136
List childNodesAsList(el) => el.nodes;
137137
clearNodes(el) {
138-
throw 'not implemented';
138+
el.nodes.forEach((e) => e.remove());
139139
}
140140
appendChild(el, node) => el.append(node.remove());
141141
removeChild(el, node) {
@@ -153,7 +153,7 @@ class Html5LibDomAdapter implements DomAdapter {
153153
throw 'not implemented';
154154
}
155155
setInnerHTML(el, value) {
156-
throw 'not implemented';
156+
el.innerHtml = value;
157157
}
158158
getText(el) {
159159
return el.text;

modules/angular2/src/render/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ export class RenderCompiler {
303303
* we don't need to serialize all possible components over the wire,
304304
* but only the needed ones based on previous calls.
305305
*/
306-
compile(template: ViewDefinition): Promise<ProtoViewDto> { return null; }
306+
compile(view: ViewDefinition): Promise<ProtoViewDto> { return null; }
307307
}
308308

309309
export interface RenderElementRef {
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import {List} from 'angular2/src/facade/collection';
2-
import {Promise} from 'angular2/src/facade/async';
3-
42
import {Parser} from 'angular2/change_detection';
53
import {ViewDefinition} from '../../api';
64
import {CompileStep} from './compile_step';
@@ -12,21 +10,19 @@ import {ShadowDomCompileStep} from '../shadow_dom/shadow_dom_compile_step';
1210
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
1311

1412
export class CompileStepFactory {
15-
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>): List<CompileStep> {
16-
return null;
17-
}
13+
createSteps(template: ViewDefinition): List<CompileStep> { return null; }
1814
}
1915

2016
export class DefaultStepFactory extends CompileStepFactory {
2117
constructor(public _parser: Parser, public _shadowDomStrategy: ShadowDomStrategy) { super(); }
2218

23-
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>) {
19+
createSteps(template: ViewDefinition): List<CompileStep> {
2420
return [
2521
new ViewSplitter(this._parser),
2622
new PropertyBindingParser(this._parser),
2723
new DirectiveParser(this._parser, template.directives),
2824
new TextInterpolationParser(this._parser),
29-
new ShadowDomCompileStep(this._shadowDomStrategy, template, subTaskPromises)
25+
new ShadowDomCompileStep(this._shadowDomStrategy, template)
3026
];
3127
}
3228
}

modules/angular2/src/render/dom/compiler/compiler.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,11 @@ export class DomCompiler extends RenderCompiler {
2828
super();
2929
}
3030

31-
compile(template: ViewDefinition): Promise<ProtoViewDto> {
32-
var tplPromise = this._templateLoader.load(template);
31+
compile(view: ViewDefinition): Promise<ProtoViewDto> {
32+
var tplPromise = this._templateLoader.load(view);
3333
return PromiseWrapper.then(
34-
tplPromise, (el) => this._compileTemplate(template, el, ViewType.COMPONENT), (e) => {
35-
throw new BaseException(
36-
`Failed to load the template for "${template.componentId}" : ${e}`);
34+
tplPromise, (el) => this._compileTemplate(view, el, ViewType.COMPONENT), (e) => {
35+
throw new BaseException(`Failed to load the template for "${view.componentId}" : ${e}`);
3736
});
3837
}
3938

@@ -51,17 +50,10 @@ export class DomCompiler extends RenderCompiler {
5150

5251
_compileTemplate(viewDef: ViewDefinition, tplElement,
5352
protoViewType: ViewType): Promise<ProtoViewDto> {
54-
var subTaskPromises = [];
55-
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
53+
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef));
5654
var compileElements = pipeline.process(tplElement, protoViewType, viewDef.componentId);
5755

58-
var protoView = compileElements[0].inheritedProtoView.build();
59-
60-
if (subTaskPromises.length > 0) {
61-
return PromiseWrapper.all(subTaskPromises).then((_) => protoView);
62-
} else {
63-
return PromiseWrapper.resolve(protoView);
64-
}
56+
return PromiseWrapper.resolve(compileElements[0].inheritedProtoView.build());
6557
}
6658
}
6759

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import {Injectable} from 'angular2/di';
2-
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
3-
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
2+
import {isBlank, isPresent, BaseException, stringify, isPromise} from 'angular2/src/facade/lang';
3+
import {Map, MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
44
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
55
import {DOM} from 'angular2/src/dom/dom_adapter';
66

77
import {XHR} from 'angular2/src/render/xhr';
88

99
import {ViewDefinition} from '../../api';
10-
import {UrlResolver} from 'angular2/src/services/url_resolver';
10+
11+
import {StyleInliner} from './style_inliner';
12+
import {StyleUrlResolver} from './style_url_resolver';
1113

1214
/**
1315
* Strategy to load component templates.
@@ -17,37 +19,36 @@ import {UrlResolver} from 'angular2/src/services/url_resolver';
1719
export class TemplateLoader {
1820
_cache: Map<string, Promise<string>> = new Map();
1921

20-
constructor(private _xhr: XHR, urlResolver: UrlResolver) {}
22+
constructor(private _xhr: XHR, private _styleInliner: StyleInliner,
23+
private _styleUrlResolver: StyleUrlResolver) {}
2124

2225
load(view: ViewDefinition): Promise</*element*/ any> {
23-
let html;
24-
let fetchedStyles;
26+
let tplElAndStyles: List<string | Promise<string>> = [this._loadHtml(view)];
2527

26-
// Load the HTML
27-
if (isPresent(view.template)) {
28-
html = PromiseWrapper.resolve(view.template);
29-
} else if (isPresent(view.templateAbsUrl)) {
30-
html = this._loadText(view.templateAbsUrl);
31-
} else {
32-
throw new BaseException('View should have either the templateUrl or template property set');
28+
if (isPresent(view.styles)) {
29+
view.styles.forEach((cssText: string) => {
30+
let textOrPromise = this._resolveAndInlineCssText(cssText, view.templateAbsUrl);
31+
tplElAndStyles.push(textOrPromise);
32+
});
3333
}
3434

35-
// Load the styles
36-
if (isPresent(view.styleAbsUrls) && view.styleAbsUrls.length > 0) {
37-
fetchedStyles = ListWrapper.map(view.styleAbsUrls, url => this._loadText(url));
38-
} else {
39-
fetchedStyles = [];
35+
if (isPresent(view.styleAbsUrls)) {
36+
view.styleAbsUrls.forEach(url => {
37+
let promise = this._loadText(url).then(
38+
cssText => this._resolveAndInlineCssText(cssText, view.templateAbsUrl));
39+
tplElAndStyles.push(promise);
40+
});
4041
}
4142

42-
// Inline the styles and return a template element
43-
return PromiseWrapper.all(ListWrapper.concat([html], fetchedStyles))
43+
// Inline the styles from the @View annotation and return a template element
44+
return PromiseWrapper.all(tplElAndStyles)
4445
.then((res: List<string>) => {
45-
let html = res[0];
46-
let fetchedStyles = ListWrapper.slice(res, 1);
46+
let tplEl = res[0];
47+
let cssTexts = ListWrapper.slice(res, 1);
4748

48-
html = _createStyleTags(view.styles) + _createStyleTags(fetchedStyles) + html;
49+
_insertCssTexts(DOM.content(tplEl), cssTexts);
4950

50-
return DOM.createTemplate(html);
51+
return tplEl;
5152
});
5253
}
5354

@@ -67,10 +68,74 @@ export class TemplateLoader {
6768

6869
return response;
6970
}
71+
72+
// Load the html and inline any style tags
73+
private _loadHtml(view: ViewDefinition): Promise<any /* element */> {
74+
let html;
75+
76+
// Load the HTML
77+
if (isPresent(view.template)) {
78+
html = PromiseWrapper.resolve(view.template);
79+
} else if (isPresent(view.templateAbsUrl)) {
80+
html = this._loadText(view.templateAbsUrl);
81+
} else {
82+
throw new BaseException('View should have either the templateUrl or template property set');
83+
}
84+
85+
// Inline the style tags from the html
86+
return html.then(html => {
87+
var tplEl = DOM.createTemplate(html);
88+
let styleEls = DOM.querySelectorAll(DOM.content(tplEl), 'STYLE');
89+
90+
let promises: List<Promise<string>> = [];
91+
for (let i = 0; i < styleEls.length; i++) {
92+
let promise = this._resolveAndInlineElement(styleEls[i], view.templateAbsUrl);
93+
if (isPromise(promise)) {
94+
promises.push(promise);
95+
}
96+
}
97+
98+
return promises.length > 0 ? PromiseWrapper.all(promises).then(_ => tplEl) : tplEl;
99+
});
100+
}
101+
102+
/**
103+
* Inlines a style element.
104+
*
105+
* @param styleEl The style element
106+
* @param baseUrl The base url
107+
* @returns {Promise<any>} null when no @import rule exist in the css or a Promise
108+
* @private
109+
*/
110+
private _resolveAndInlineElement(styleEl, baseUrl: string): Promise<any> {
111+
let textOrPromise = this._resolveAndInlineCssText(DOM.getText(styleEl), baseUrl);
112+
113+
if (isPromise(textOrPromise)) {
114+
return (<Promise<string>>textOrPromise).then(css => { DOM.setText(styleEl, css); });
115+
} else {
116+
DOM.setText(styleEl, <string>textOrPromise);
117+
return null;
118+
}
119+
}
120+
121+
private _resolveAndInlineCssText(cssText: string, baseUrl: string): string | Promise<string> {
122+
cssText = this._styleUrlResolver.resolveUrls(cssText, baseUrl);
123+
return this._styleInliner.inlineImports(cssText, baseUrl);
124+
}
70125
}
71126

72-
function _createStyleTags(styles?: List<string>): string {
73-
return isBlank(styles) ?
74-
'' :
75-
ListWrapper.map(styles, css => `<style type='text/css'>${css}</style>`).join('');
127+
function _insertCssTexts(element, cssTexts: List<string>): void {
128+
if (cssTexts.length == 0) return;
129+
130+
let insertBefore = DOM.firstChild(element);
131+
132+
for (let i = cssTexts.length - 1; i >= 0; i--) {
133+
let styleEl = DOM.createStyleElement(cssTexts[i]);
134+
if (isPresent(insertBefore)) {
135+
DOM.insertBefore(insertBefore, styleEl);
136+
} else {
137+
DOM.appendChild(element, styleEl);
138+
}
139+
insertBefore = styleEl;
140+
}
76141
}

modules/angular2/src/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy.ts

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import {isBlank, isPresent, isPromise} from 'angular2/src/facade/lang';
2-
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
1+
import {isBlank, isPresent} from 'angular2/src/facade/lang';
32

43
import {DOM} from 'angular2/src/dom/dom_adapter';
54

6-
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
7-
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
85
import {EmulatedUnscopedShadowDomStrategy} from './emulated_unscoped_shadow_dom_strategy';
96
import {
107
getContentAttribute,
@@ -27,39 +24,21 @@ import {
2724
* - see `ShadowCss` for more information and limitations.
2825
*/
2926
export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomStrategy {
30-
constructor(styleInliner: StyleInliner, styleUrlResolver: StyleUrlResolver, styleHost) {
31-
super(styleInliner, styleUrlResolver, styleHost);
32-
}
33-
34-
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): Promise<any> {
35-
var cssText = DOM.getText(styleEl);
36-
37-
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
38-
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
39-
40-
var ret = null;
41-
if (isPromise(inlinedCss)) {
42-
DOM.setText(styleEl, '');
43-
ret = (<Promise<string>>inlinedCss)
44-
.then((css) => {
45-
css = shimCssForComponent(css, hostComponentId);
46-
DOM.setText(styleEl, css);
47-
});
48-
} else {
49-
var css = shimCssForComponent(<string>inlinedCss, hostComponentId);
50-
DOM.setText(styleEl, css);
51-
}
27+
constructor(styleHost) { super(styleHost); }
5228

29+
processStyleElement(hostComponentId: string, templateUrl: string, styleEl): void {
30+
let cssText = DOM.getText(styleEl);
31+
cssText = shimCssForComponent(cssText, hostComponentId);
32+
DOM.setText(styleEl, cssText);
5333
this._moveToStyleHost(styleEl);
54-
return ret;
5534
}
5635

57-
_moveToStyleHost(styleEl) {
36+
_moveToStyleHost(styleEl): void {
5837
DOM.remove(styleEl);
5938
insertStyleElement(this.styleHost, styleEl);
6039
}
6140

62-
processElement(hostComponentId: string, elementComponentId: string, element) {
41+
processElement(hostComponentId: string, elementComponentId: string, element): void {
6342
// Shim the element as a child of the compiled component
6443
if (isPresent(hostComponentId)) {
6544
var contentAttribute = getContentAttribute(getComponentId(hostComponentId));

0 commit comments

Comments
 (0)