docs(component-relative-paths): new cookbook
This commit is contained in:
parent
da48db3c77
commit
f9fea00824
|
@ -0,0 +1,27 @@
|
|||
// gulp run-e2e-tests --filter=cb-set-document-title
|
||||
describe('Set Document Title', function () {
|
||||
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should set the document title', function () {
|
||||
|
||||
var titles = [
|
||||
'Good morning!',
|
||||
'Good afternoon!',
|
||||
'Good evening!'
|
||||
];
|
||||
|
||||
element.all( by.css( 'ul li a' ) ).each(
|
||||
function iterator( element, i ) {
|
||||
|
||||
element.click();
|
||||
expect( browser.getTitle() ).toEqual( titles[ i ] );
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { SomeAbsoluteComponent, SomeRelativeComponent} from './some.component';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template:
|
||||
`<h1>Absolute & <i>Component-Relative</i> Paths</h1>
|
||||
<absolute-path></absolute-path>
|
||||
<relative-path></relative-path>
|
||||
`,
|
||||
directives: [SomeAbsoluteComponent, SomeRelativeComponent]
|
||||
})
|
||||
export class AppComponent {}
|
|
@ -0,0 +1,5 @@
|
|||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
bootstrap(AppComponent);
|
|
@ -0,0 +1,22 @@
|
|||
/* #docregion */
|
||||
div.absolute {
|
||||
background: beige;
|
||||
border: 1px solid darkred;
|
||||
color: red;
|
||||
margin: 8px;
|
||||
max-width: 20em;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.relative {
|
||||
background: powderblue;
|
||||
border: 1px solid darkblue;
|
||||
color: Blue;
|
||||
font-style: italic;
|
||||
margin: 8px;
|
||||
max-width: 20em;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<!-- #docregion -->
|
||||
<div class={{class}}>
|
||||
{{type}}<br>{{path}}
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
///////// Using Absolute Paths ///////
|
||||
|
||||
// #docregion absolute-config
|
||||
@Component({
|
||||
selector: 'absolute-path',
|
||||
templateUrl: 'app/some.component.html',
|
||||
styleUrls: ['app/some.component.css']
|
||||
})
|
||||
// #enddocregion absolute-config
|
||||
export class SomeAbsoluteComponent {
|
||||
class = 'absolute';
|
||||
type = 'Absolute template & style URLs';
|
||||
path = 'app/path.component.html';
|
||||
}
|
||||
|
||||
///////// Using Relative Paths ///////
|
||||
|
||||
// #docregion relative-config
|
||||
@Component({
|
||||
// #docregion module-id
|
||||
moduleId: module.id,
|
||||
// #enddocregion module-id
|
||||
selector: 'relative-path',
|
||||
templateUrl: 'some.component.html',
|
||||
styleUrls: ['some.component.css']
|
||||
})
|
||||
// #enddocregion relative-config
|
||||
|
||||
export class SomeRelativeComponent {
|
||||
class = 'relative';
|
||||
type = 'Component-relative template & style URLs';
|
||||
path = 'path.component.html';
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<base href="/">
|
||||
|
||||
<title>
|
||||
Component-Relative Paths
|
||||
</title>
|
||||
|
||||
<!-- #docregion style -->
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<!-- #enddocregion style -->
|
||||
|
||||
<!-- Polyfill(s) for older browsers -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<my-app>Loading app...</my-app>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"description": "Module-relative Paths",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags": [ "cookbook" ]
|
||||
}
|
|
@ -17,6 +17,12 @@
|
|||
"intro": "Share information between different directives and components"
|
||||
},
|
||||
|
||||
"component-relative-paths": {
|
||||
"title": "Component-relative Paths",
|
||||
"intro": "Use relative URLs for component templates and styles.",
|
||||
"hide": true
|
||||
},
|
||||
|
||||
"dependency-injection": {
|
||||
"title": "Dependency Injection",
|
||||
"intro": "Techniques for Dependency Injection",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!= partial("../../../_includes/_ts-temp")
|
|
@ -16,6 +16,11 @@
|
|||
"intro": "Share information between different directives and components"
|
||||
},
|
||||
|
||||
"component-relative-paths": {
|
||||
"title": "Component-relative Paths",
|
||||
"intro": "Use relative URLs for component templates and styles."
|
||||
},
|
||||
|
||||
"dependency-injection": {
|
||||
"title": "Dependency Injection",
|
||||
"intro": "Techniques for Dependency Injection"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!= partial("../../../_includes/_ts-temp")
|
|
@ -16,6 +16,11 @@
|
|||
"intro": "Share information between different directives and components"
|
||||
},
|
||||
|
||||
"component-relative-paths": {
|
||||
"title": "Component-relative Paths",
|
||||
"intro": "Use relative URLs for component templates and styles."
|
||||
},
|
||||
|
||||
"dependency-injection": {
|
||||
"title": "Dependency Injection",
|
||||
"intro": "Techniques for Dependency Injection"
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
include ../_util-fns
|
||||
|
||||
:marked
|
||||
## Write *Component-Relative* URLs to component templates and style files
|
||||
|
||||
Our components ofter refer to external template and style files.
|
||||
We identify those files with a URL in the `templateUrl` and `styleUrls` properties of the `@Component` metadata
|
||||
as seen here:
|
||||
|
||||
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','absolute-config')(format='.')
|
||||
:marked
|
||||
By default, we *must* specify the full path back to the application root.
|
||||
We call this an ***absolute path*** because it is *absolute* with respect to the application root.
|
||||
|
||||
There are two problems with an *absolute path*
|
||||
|
||||
1. We have to remember the full path back to the application root.
|
||||
|
||||
1. We have to update the URL when we move the component around in the application files structure.
|
||||
|
||||
It would be much easier to write and maintain our application components if we could specify template and style locations
|
||||
*relative* to their component class file.
|
||||
|
||||
*We can!*
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
We can if we build our application as `commonjs` modules and load those modules
|
||||
with a suitable package loader such as `systemjs` or `webpack`.
|
||||
Learn why [below](#why-default).
|
||||
|
||||
The Angular 2 CLI uses these technologies and defaults to the
|
||||
*component-relative path* approach described here.
|
||||
CLI users can skip this chapter or read on to understand
|
||||
how it works.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## _Component-Relative_ Paths
|
||||
|
||||
Our goal is to specify template and style URLs *relative* to their component class files,
|
||||
hence the term ***component-relative path***.
|
||||
|
||||
The key to success is following a convention that puts related component files in well-known locations.
|
||||
|
||||
We recommend keeping component template and component-specific style files as *siblings* of their
|
||||
companion component class files.
|
||||
Here we see the three files for `SomeComponent` sitting next to each other in the `app` folder.
|
||||
|
||||
.filetree
|
||||
.file app
|
||||
.children
|
||||
.file some.component.css
|
||||
.file some.component.html
|
||||
.file some.component.ts
|
||||
.file ...
|
||||
:marked
|
||||
We'll have more files and folders — and greater folder depth — as our application grows.
|
||||
We'll be fine as long as the component files travel together as the inseparable siblings they are.
|
||||
|
||||
### Set the *moduleId*
|
||||
|
||||
Having adopted this file structure convention, we can specify locations of the template and style files
|
||||
relative to the component class file simply by setting the `moduleId` property of the `@Component` metadata like this
|
||||
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
|
||||
:marked
|
||||
We strip the `app/` base path from the `templateUrl` and `styleUrls`. The result looks like this:
|
||||
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','relative-config')(format='.')
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
Webpack users may prefer [an alternative approach](#webpack) that uses `require`.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Source
|
||||
|
||||
**We can see the [live example](/resources/live-examples/cb-component-relative-paths/ts/plnkr.html)**
|
||||
and download the source code from there
|
||||
or simply read the pertinent source here.
|
||||
+makeTabs(
|
||||
`cb-component-relative-paths/ts/app/some.component.ts,
|
||||
cb-component-relative-paths/ts/app/some.component.html,
|
||||
cb-component-relative-paths/ts/app/some.component.css,
|
||||
cb-component-relative-paths/ts/app/app.component.ts`,
|
||||
null,
|
||||
`app/some.component.ts, app/some.html, app/some.component.css, app/app.component.ts`)
|
||||
|
||||
a#why-default
|
||||
.l-main-section
|
||||
:marked
|
||||
## Appendix: why *component-relative* is not the default
|
||||
|
||||
A *component-relative* path is obviously superior to an *absolute* path.
|
||||
Why did Angular default to the *absolute* path?
|
||||
Why do *we* have to set the `moduleId`? Why can't Angular set it?
|
||||
|
||||
First, let's look at what happens if we use a relative path and omit the `moduleId`.
|
||||
|
||||
`EXCEPTION: Failed to load some.component.html`
|
||||
|
||||
Angular can't find the file so it throws an error.
|
||||
|
||||
Why can't Angular calculate the template and style URLs from the component file's location?
|
||||
|
||||
Because the location of the component can't be determined without the developer's help.
|
||||
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
|
||||
from CommonJS packages, to name a few.
|
||||
We might generate modules in any of several formats.
|
||||
We might not be writing modular code at all!
|
||||
|
||||
With this diversity of packaging and module load strategies,
|
||||
it's not possible for Angular to know with certainty where these files reside at runtime.
|
||||
|
||||
The only location Angular can be sure of is the URL of the `index.html` home page, the application root.
|
||||
So by default it resolves template and style paths relative to the URL of `index.html`.
|
||||
That's why we previously wrote our file URLs with an `app/` base path prefix.
|
||||
|
||||
But *if* we follow the recommended guidelines and we write modules in `commonjs` format
|
||||
and we use a module loader that *plays nice*,
|
||||
*then* we — the developers of the application —
|
||||
know that the semi-global `module.id` variable is available and contains
|
||||
the absolute URL of the component class module file.
|
||||
|
||||
That knowledge enables us to tell Angular where the *component* file is
|
||||
by setting the `moduleId`:
|
||||
+makeExample('cb-component-relative-paths/ts/app/some.component.ts','module-id')(format='.')
|
||||
|
||||
a#webpack
|
||||
.l-main-section
|
||||
:marked
|
||||
## Webpack: load templates and styles with *require*
|
||||
Webpack developers have an alternative to `moduleId`.
|
||||
|
||||
They can load templates and styles at runtime by setting the component metadata `template` and `style` properties
|
||||
with `require` statements that reference *component-relative* URLS.
|
||||
|
||||
+makeExample('webpack/ts/src/app/app.component.ts')(format='.')
|
||||
:marked
|
||||
See the [Introduction to Webpack](../guide/webpack.html).
|
|
@ -311,55 +311,9 @@ code-example(format='').
|
|||
|
||||
block module-id
|
||||
:marked
|
||||
We'd *prefer* to write this:
|
||||
|
||||
+makeExample('component-styles/ts/app/quest-summary.component.ts', 'urls')(format='.')
|
||||
|
||||
:marked
|
||||
We can't do that by default. Angular can't find the files and throws an error:
|
||||
|
||||
`EXCEPTION: Failed to load quest-summary.component.html`
|
||||
|
||||
Why can't Angular calculate the HTML and CSS URLs from the component file's location?
|
||||
|
||||
Unfortunately, that location is not readily known.
|
||||
Angular apps can be loaded in many ways: from individual files, from SystemJS packages, or
|
||||
from CommonJS packages, to name a few.
|
||||
With this diversity of load strategies, it's not easy to tell at runtime where these files actually reside.
|
||||
|
||||
The only location Angular can be sure of is the URL of the `index.html` home page.
|
||||
So by default it resolves template and style paths relative to the URL of `index.html`.
|
||||
That's why we previously wrote our CSS file URLs with an `app/` base path prefix.
|
||||
|
||||
Although this works with any code loading scheme, it is very inconvenient.
|
||||
We move file folders around all the time during the evolution of our applications.
|
||||
It's no fun patching the style and template URLs when we do.
|
||||
|
||||
### *moduleId*
|
||||
|
||||
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property.
|
||||
|
||||
If we knew the component file's base path, we'd set `moduleId` to that and
|
||||
let Angular construct the full URL from this base path plus the CSS and template file names.
|
||||
|
||||
Our challenge is to calculate the base path with minimal effort.
|
||||
If it's too hard, we shouldn't bother; we should just write the full path to the root and move on.
|
||||
Fortunately, *certain* module loaders make it relatively easy to find the base path.
|
||||
|
||||
SystemJS (starting in v.0.19.19) sets a *semi-global* variable to the URL of the component file.
|
||||
That makes it trivial to set the component metadata `moduleId` property to the component's URL
|
||||
and let Angular determine the module-relative paths for style and template URLs from there.
|
||||
|
||||
The name of the *semi-global* variable depends upon whether we told TypeScript to transpile to
|
||||
'system' or 'commonjs' format (see the `module` option in the
|
||||
[TypeScript compiler documentation](http://www.typescriptlang.org/docs/handbook/compiler-options.html)).
|
||||
The variables are `__moduleName` and `module.id` respectively.
|
||||
|
||||
Here's an example in which we set the metadata `moduleId` to `module.id`.
|
||||
We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`.
|
||||
|
||||
+makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts')
|
||||
:marked
|
||||
Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
With a module bundler like Webpack we are more likely to set the `styles` and `template` properties with the bundler's
|
||||
`require` mechanism rather than bother with `styleUrls` and `templateUrl`.
|
||||
|
|
|
@ -282,7 +282,9 @@ code-example(format="." language="bash").
|
|||
+makeExample('toh-5/ts/app/dashboard.component.ts', 'template-url', 'app/dashboard.component.ts (templateUrl)')(format=".")
|
||||
.l-sub-section
|
||||
:marked
|
||||
We specify the path _all the way back to the application root_. Angular doesn't support module-relative paths.
|
||||
We specify the path _all the way back to the application root_ — `app/` in this case —
|
||||
because Angular doesn't support relative paths _by default_.
|
||||
We _can_ switch to [component-relative paths](../cookbook/component-relative-paths) if we prefer.
|
||||
:marked
|
||||
Create that file with these contents:
|
||||
+makeExample('toh-5/ts/app/dashboard.component.html', null, 'dashboard.component.html')(format=".")
|
||||
|
|
Loading…
Reference in New Issue