From f9fea008246171604d86ede3b6d990121b7f9d2e Mon Sep 17 00:00:00 2001 From: Ward Bell Date: Fri, 20 May 2016 15:14:13 -0700 Subject: [PATCH] docs(component-relative-paths): new cookbook --- .../cb-component-relative-paths/e2e-spec.js | 27 ++++ .../ts/app/app.component.ts | 15 ++ .../ts/app/main.ts | 5 + .../ts/app/some.component.css | 22 +++ .../ts/app/some.component.html | 4 + .../ts/app/some.component.ts | 37 +++++ .../ts/example-config.json | 0 .../cb-component-relative-paths/ts/index.html | 31 ++++ .../cb-component-relative-paths/ts/plnkr.json | 8 + public/docs/dart/latest/cookbook/_data.json | 6 + .../cookbook/component-relative-paths.jade | 1 + public/docs/js/latest/cookbook/_data.json | 5 + .../cookbook/component-relative-paths.jade | 1 + public/docs/ts/latest/cookbook/_data.json | 5 + .../cookbook/component-relative-paths.jade | 140 ++++++++++++++++++ .../ts/latest/guide/component-styles.jade | 52 +------ public/docs/ts/latest/tutorial/toh-pt5.jade | 4 +- 17 files changed, 313 insertions(+), 50 deletions(-) create mode 100644 public/docs/_examples/cb-component-relative-paths/e2e-spec.js create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/app.component.ts create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/main.ts create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/some.component.css create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/some.component.html create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/some.component.ts create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/example-config.json create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/index.html create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/plnkr.json create mode 100644 public/docs/dart/latest/cookbook/component-relative-paths.jade create mode 100644 public/docs/js/latest/cookbook/component-relative-paths.jade create mode 100644 public/docs/ts/latest/cookbook/component-relative-paths.jade diff --git a/public/docs/_examples/cb-component-relative-paths/e2e-spec.js b/public/docs/_examples/cb-component-relative-paths/e2e-spec.js new file mode 100644 index 0000000000..52516f4d4b --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/e2e-spec.js @@ -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 ] ); + + } + ); + + }); + +}); diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/app.component.ts b/public/docs/_examples/cb-component-relative-paths/ts/app/app.component.ts new file mode 100644 index 0000000000..09ccc94591 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/app.component.ts @@ -0,0 +1,15 @@ +// #docregion +import { Component } from '@angular/core'; + +import { SomeAbsoluteComponent, SomeRelativeComponent} from './some.component'; + +@Component({ +selector: 'my-app', +template: + `

Absolute & Component-Relative Paths

+ + + `, + directives: [SomeAbsoluteComponent, SomeRelativeComponent] +}) +export class AppComponent {} diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts b/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts new file mode 100644 index 0000000000..42dbeb9f7d --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts @@ -0,0 +1,5 @@ +import { bootstrap } from '@angular/platform-browser-dynamic'; + +import { AppComponent } from './app.component'; + +bootstrap(AppComponent); diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.css b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.css new file mode 100644 index 0000000000..f43e11f60e --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.css @@ -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; +} + diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.html b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.html new file mode 100644 index 0000000000..027741ce55 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.html @@ -0,0 +1,4 @@ + +
+ {{type}}
{{path}} +
diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.ts b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.ts new file mode 100644 index 0000000000..2bd56c6930 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/some.component.ts @@ -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'; + +} diff --git a/public/docs/_examples/cb-component-relative-paths/ts/example-config.json b/public/docs/_examples/cb-component-relative-paths/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/cb-component-relative-paths/ts/index.html b/public/docs/_examples/cb-component-relative-paths/ts/index.html new file mode 100644 index 0000000000..cc0d972df2 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/index.html @@ -0,0 +1,31 @@ + + + + + + + + + Component-Relative Paths + + + + + + + + + + + + + + + + + + Loading app... + + diff --git a/public/docs/_examples/cb-component-relative-paths/ts/plnkr.json b/public/docs/_examples/cb-component-relative-paths/ts/plnkr.json new file mode 100644 index 0000000000..a4c33bbde3 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/plnkr.json @@ -0,0 +1,8 @@ +{ + "description": "Module-relative Paths", + "files": [ + "!**/*.d.ts", + "!**/*.js" + ], + "tags": [ "cookbook" ] +} diff --git a/public/docs/dart/latest/cookbook/_data.json b/public/docs/dart/latest/cookbook/_data.json index 2c28e02169..9b2545ec5e 100644 --- a/public/docs/dart/latest/cookbook/_data.json +++ b/public/docs/dart/latest/cookbook/_data.json @@ -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", diff --git a/public/docs/dart/latest/cookbook/component-relative-paths.jade b/public/docs/dart/latest/cookbook/component-relative-paths.jade new file mode 100644 index 0000000000..6778b6af28 --- /dev/null +++ b/public/docs/dart/latest/cookbook/component-relative-paths.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") diff --git a/public/docs/js/latest/cookbook/_data.json b/public/docs/js/latest/cookbook/_data.json index f4f89de38c..2d9ada59f1 100644 --- a/public/docs/js/latest/cookbook/_data.json +++ b/public/docs/js/latest/cookbook/_data.json @@ -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" diff --git a/public/docs/js/latest/cookbook/component-relative-paths.jade b/public/docs/js/latest/cookbook/component-relative-paths.jade new file mode 100644 index 0000000000..6778b6af28 --- /dev/null +++ b/public/docs/js/latest/cookbook/component-relative-paths.jade @@ -0,0 +1 @@ +!= partial("../../../_includes/_ts-temp") diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index 211e12bfd4..a90160de7b 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -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" diff --git a/public/docs/ts/latest/cookbook/component-relative-paths.jade b/public/docs/ts/latest/cookbook/component-relative-paths.jade new file mode 100644 index 0000000000..6266730f92 --- /dev/null +++ b/public/docs/ts/latest/cookbook/component-relative-paths.jade @@ -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). diff --git a/public/docs/ts/latest/guide/component-styles.jade b/public/docs/ts/latest/guide/component-styles.jade index d7975713a6..4d090a2e9c 100644 --- a/public/docs/ts/latest/guide/component-styles.jade +++ b/public/docs/ts/latest/guide/component-styles.jade @@ -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`. diff --git a/public/docs/ts/latest/tutorial/toh-pt5.jade b/public/docs/ts/latest/tutorial/toh-pt5.jade index 2a72f68d22..9d7f43629e 100644 --- a/public/docs/ts/latest/tutorial/toh-pt5.jade +++ b/public/docs/ts/latest/tutorial/toh-pt5.jade @@ -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=".")