|
| 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 — and greater folder depth — 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 — the developers of the application — |
| 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). |
0 commit comments