Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit f9fea00

Browse files
committed
docs(component-relative-paths): new cookbook
1 parent da48db3 commit f9fea00

17 files changed

+313
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// gulp run-e2e-tests --filter=cb-set-document-title
2+
describe('Set Document Title', function () {
3+
4+
beforeAll(function () {
5+
browser.get('');
6+
});
7+
8+
it('should set the document title', function () {
9+
10+
var titles = [
11+
'Good morning!',
12+
'Good afternoon!',
13+
'Good evening!'
14+
];
15+
16+
element.all( by.css( 'ul li a' ) ).each(
17+
function iterator( element, i ) {
18+
19+
element.click();
20+
expect( browser.getTitle() ).toEqual( titles[ i ] );
21+
22+
}
23+
);
24+
25+
});
26+
27+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// #docregion
2+
import { Component } from '@angular/core';
3+
4+
import { SomeAbsoluteComponent, SomeRelativeComponent} from './some.component';
5+
6+
@Component({
7+
selector: 'my-app',
8+
template:
9+
`<h1>Absolute & <i>Component-Relative</i> Paths</h1>
10+
<absolute-path></absolute-path>
11+
<relative-path></relative-path>
12+
`,
13+
directives: [SomeAbsoluteComponent, SomeRelativeComponent]
14+
})
15+
export class AppComponent {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { bootstrap } from '@angular/platform-browser-dynamic';
2+
3+
import { AppComponent } from './app.component';
4+
5+
bootstrap(AppComponent);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* #docregion */
2+
div.absolute {
3+
background: beige;
4+
border: 1px solid darkred;
5+
color: red;
6+
margin: 8px;
7+
max-width: 20em;
8+
padding: 4px;
9+
text-align: center;
10+
}
11+
12+
div.relative {
13+
background: powderblue;
14+
border: 1px solid darkblue;
15+
color: Blue;
16+
font-style: italic;
17+
margin: 8px;
18+
max-width: 20em;
19+
padding: 4px;
20+
text-align: center;
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- #docregion -->
2+
<div class={{class}}>
3+
{{type}}<br>{{path}}
4+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// #docregion
2+
import { Component } from '@angular/core';
3+
4+
///////// Using Absolute Paths ///////
5+
6+
// #docregion absolute-config
7+
@Component({
8+
selector: 'absolute-path',
9+
templateUrl: 'app/some.component.html',
10+
styleUrls: ['app/some.component.css']
11+
})
12+
// #enddocregion absolute-config
13+
export class SomeAbsoluteComponent {
14+
class = 'absolute';
15+
type = 'Absolute template & style URLs';
16+
path = 'app/path.component.html';
17+
}
18+
19+
///////// Using Relative Paths ///////
20+
21+
// #docregion relative-config
22+
@Component({
23+
// #docregion module-id
24+
moduleId: module.id,
25+
// #enddocregion module-id
26+
selector: 'relative-path',
27+
templateUrl: 'some.component.html',
28+
styleUrls: ['some.component.css']
29+
})
30+
// #enddocregion relative-config
31+
32+
export class SomeRelativeComponent {
33+
class = 'relative';
34+
type = 'Component-relative template & style URLs';
35+
path = 'path.component.html';
36+
37+
}

public/docs/_examples/cb-component-relative-paths/ts/example-config.json

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<base href="/">
7+
8+
<title>
9+
Component-Relative Paths
10+
</title>
11+
12+
<!-- #docregion style -->
13+
<link rel="stylesheet" type="text/css" href="styles.css">
14+
<!-- #enddocregion style -->
15+
16+
<!-- Polyfill(s) for older browsers -->
17+
<script src="node_modules/core-js/client/shim.min.js"></script>
18+
19+
<script src="node_modules/zone.js/dist/zone.js"></script>
20+
<script src="node_modules/reflect-metadata/Reflect.js"></script>
21+
<script src="node_modules/systemjs/dist/system.src.js"></script>
22+
23+
<script src="systemjs.config.js"></script>
24+
<script>
25+
System.import('app').catch(function(err){ console.error(err); });
26+
</script>
27+
</head>
28+
<body>
29+
<my-app>Loading app...</my-app>
30+
</body>
31+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"description": "Module-relative Paths",
3+
"files": [
4+
"!**/*.d.ts",
5+
"!**/*.js"
6+
],
7+
"tags": [ "cookbook" ]
8+
}

public/docs/dart/latest/cookbook/_data.json

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
"intro": "Share information between different directives and components"
1818
},
1919

20+
"component-relative-paths": {
21+
"title": "Component-relative Paths",
22+
"intro": "Use relative URLs for component templates and styles.",
23+
"hide": true
24+
},
25+
2026
"dependency-injection": {
2127
"title": "Dependency Injection",
2228
"intro": "Techniques for Dependency Injection",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!= partial("../../../_includes/_ts-temp")

public/docs/js/latest/cookbook/_data.json

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
"intro": "Share information between different directives and components"
1717
},
1818

19+
"component-relative-paths": {
20+
"title": "Component-relative Paths",
21+
"intro": "Use relative URLs for component templates and styles."
22+
},
23+
1924
"dependency-injection": {
2025
"title": "Dependency Injection",
2126
"intro": "Techniques for Dependency Injection"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!= partial("../../../_includes/_ts-temp")

public/docs/ts/latest/cookbook/_data.json

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
"intro": "Share information between different directives and components"
1717
},
1818

19+
"component-relative-paths": {
20+
"title": "Component-relative Paths",
21+
"intro": "Use relative URLs for component templates and styles."
22+
},
23+
1924
"dependency-injection": {
2025
"title": "Dependency Injection",
2126
"intro": "Techniques for Dependency Injection"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
include ../_util-fns
2+
3+
:marked
4+
## Write *Component-Relative* URLs to component templates and style files
5+
6+
Our components ofter refer to external template and style files.
7+
We identify those files with a URL in the `templateUrl` and `styleUrls` properties of the `@Component` metadata
8+
as seen here:
9+
10+
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','absolute-config')(format='.')
11+
:marked
12+
By default, we *must* specify the full path back to the application root.
13+
We call this an ***absolute path*** because it is *absolute* with respect to the application root.
14+
15+
There are two problems with an *absolute path*
16+
17+
1. We have to remember the full path back to the application root.
18+
19+
1. We have to update the URL when we move the component around in the application files structure.
20+
21+
It would be much easier to write and maintain our application components if we could specify template and style locations
22+
*relative* to their component class file.
23+
24+
*We can!*
25+
26+
.alert.is-important
27+
:marked
28+
We can if we build our application as `commonjs` modules and load those modules
29+
with a suitable package loader such as `systemjs` or `webpack`.
30+
Learn why [below](#why-default).
31+
32+
The Angular 2 CLI uses these technologies and defaults to the
33+
*component-relative path* approach described here.
34+
CLI users can skip this chapter or read on to understand
35+
how it works.
36+
37+
.l-main-section
38+
:marked
39+
## _Component-Relative_ Paths
40+
41+
Our goal is to specify template and style URLs *relative* to their component class files,
42+
hence the term ***component-relative path***.
43+
44+
The key to success is following a convention that puts related component files in well-known locations.
45+
46+
We recommend keeping component template and component-specific style files as *siblings* of their
47+
companion component class files.
48+
Here we see the three files for `SomeComponent` sitting next to each other in the `app` folder.
49+
50+
.filetree
51+
.file app
52+
.children
53+
.file some.component.css
54+
.file some.component.html
55+
.file some.component.ts
56+
.file ...
57+
:marked
58+
We'll have more files and folders &mdash; and greater folder depth &mdash; as our application grows.
59+
We'll be fine as long as the component files travel together as the inseparable siblings they are.
60+
61+
### Set the *moduleId*
62+
63+
Having adopted this file structure convention, we can specify locations of the template and style files
64+
relative to the component class file simply by setting the `moduleId` property of the `@Component` metadata like this
65+
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
66+
:marked
67+
We strip the `app/` base path from the `templateUrl` and `styleUrls`. The result looks like this:
68+
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','relative-config')(format='.')
69+
70+
.alert.is-helpful
71+
:marked
72+
Webpack users may prefer [an alternative approach](#webpack) that uses `require`.
73+
74+
.l-main-section
75+
:marked
76+
## Source
77+
78+
**We can see the [live example](/resources/live-examples/cb-component-relative-paths/ts/plnkr.html)**
79+
and download the source code from there
80+
or simply read the pertinent source here.
81+
+makeTabs(
82+
`cb-component-relative-paths/ts/app/some.component.ts,
83+
cb-component-relative-paths/ts/app/some.component.html,
84+
cb-component-relative-paths/ts/app/some.component.css,
85+
cb-component-relative-paths/ts/app/app.component.ts`,
86+
null,
87+
`app/some.component.ts, app/some.html, app/some.component.css, app/app.component.ts`)
88+
89+
a#why-default
90+
.l-main-section
91+
:marked
92+
## Appendix: why *component-relative* is not the default
93+
94+
A *component-relative* path is obviously superior to an *absolute* path.
95+
Why did Angular default to the *absolute* path?
96+
Why do *we* have to set the `moduleId`? Why can't Angular set it?
97+
98+
First, let's look at what happens if we use a relative path and omit the `moduleId`.
99+
100+
`EXCEPTION: Failed to load some.component.html`
101+
102+
Angular can't find the file so it throws an error.
103+
104+
Why can't Angular calculate the template and style URLs from the component file's location?
105+
106+
Because the location of the component can't be determined without the developer's help.
107+
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
108+
from CommonJS packages, to name a few.
109+
We might generate modules in any of several formats.
110+
We might not be writing modular code at all!
111+
112+
With this diversity of packaging and module load strategies,
113+
it's not possible for Angular to know with certainty where these files reside at runtime.
114+
115+
The only location Angular can be sure of is the URL of the `index.html` home page, the application root.
116+
So by default it resolves template and style paths relative to the URL of `index.html`.
117+
That's why we previously wrote our file URLs with an `app/` base path prefix.
118+
119+
But *if* we follow the recommended guidelines and we write modules in `commonjs` format
120+
and we use a module loader that *plays nice*,
121+
*then* we &mdash; the developers of the application &mdash;
122+
know that the semi-global `module.id` variable is available and contains
123+
the absolute URL of the component class module file.
124+
125+
That knowledge enables us to tell Angular where the *component* file is
126+
by setting the `moduleId`:
127+
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
128+
129+
a#webpack
130+
.l-main-section
131+
:marked
132+
## Webpack: load templates and styles with *require*
133+
Webpack developers have an alternative to `moduleId`.
134+
135+
They can load templates and styles at runtime by setting the component metadata `template` and `style` properties
136+
with `require` statements that reference *component-relative* URLS.
137+
138+
+makeExample('webpack/ts/src/app/app.component.ts')(format='.')
139+
:marked
140+
See the [Introduction to Webpack](../guide/webpack.html).

public/docs/ts/latest/guide/component-styles.jade

+3-49
Original file line numberDiff line numberDiff line change
@@ -311,55 +311,9 @@ code-example(format='').
311311

312312
block module-id
313313
:marked
314-
We'd *prefer* to write this:
315-
316-
+makeExample('component-styles/ts/app/quest-summary.component.ts', 'urls')(format='.')
317-
318-
:marked
319-
We can't do that by default. Angular can't find the files and throws an error:
320-
321-
`EXCEPTION: Failed to load quest-summary.component.html`
322-
323-
Why can't Angular calculate the HTML and CSS URLs from the component file's location?
324-
325-
Unfortunately, that location is not readily known.
326-
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
327-
from CommonJS packages, to name a few.
328-
With this diversity of load strategies, it's not easy to tell at runtime where these files actually reside.
329-
330-
The only location Angular can be sure of is the URL of the `index.html` home page.
331-
So by default it resolves template and style paths relative to the URL of `index.html`.
332-
That's why we previously wrote our CSS file URLs with an `app/` base path prefix.
333-
334-
Although this works with any code loading scheme, it is very inconvenient.
335-
We move file folders around all the time during the evolution of our applications.
336-
It's no fun patching the style and template URLs when we do.
337-
338-
### *moduleId*
339-
340-
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property.
341-
342-
If we knew the component file's base path, we'd set `moduleId` to that and
343-
let Angular construct the full URL from this base path plus the CSS and template file names.
344-
345-
Our challenge is to calculate the base path with minimal effort.
346-
If it's too hard, we shouldn't bother; we should just write the full path to the root and move on.
347-
Fortunately, *certain* module loaders make it relatively easy to find the base path.
348-
349-
SystemJS (starting in v.0.19.19) sets a *semi-global* variable to the URL of the component file.
350-
That makes it trivial to set the component metadata `moduleId` property to the component's URL
351-
and let Angular determine the module-relative paths for style and template URLs from there.
352-
353-
The name of the *semi-global* variable depends upon whether we told TypeScript to transpile to
354-
'system' or 'commonjs' format (see the `module` option in the
355-
[TypeScript compiler documentation](http://www.typescriptlang.org/docs/handbook/compiler-options.html)).
356-
The variables are `__moduleName` and `module.id` respectively.
357-
358-
Here's an example in which we set the metadata `moduleId` to `module.id`.
314+
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`.
359315

360316
+makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts')
317+
:marked
318+
Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter.
361319

362-
.l-sub-section
363-
:marked
364-
With a module bundler like Webpack we are more likely to set the `styles` and `template` properties with the bundler's
365-
`require` mechanism rather than bother with `styleUrls` and `templateUrl`.

0 commit comments

Comments
 (0)