Write Component-Relative URLs to component templates and style files
Our components often 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:
@Component({ selector: 'absolute-path', templateUrl: 'app/some.component.html', styleUrls: ['app/some.component.css'] })
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:
-
We have to remember the full path back to the application root.
-
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!
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.
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.
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.
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
moduleId: module.id,
We strip the app/
base path from the templateUrl
and styleUrls
. The result looks like this:
@Component({ moduleId: module.id, selector: 'relative-path', templateUrl: 'some.component.html', styleUrls: ['some.component.css'] })
Webpack users may prefer an alternative approach.
Source
We can see the and download the source code from there or simply read the pertinent source here.
import { Component } from '@angular/core'; ///////// Using Absolute Paths /////// @Component({ selector: 'absolute-path', templateUrl: 'app/some.component.html', styleUrls: ['app/some.component.css'] }) export class SomeAbsoluteComponent { class = 'absolute'; type = 'Absolute template & style URLs'; path = 'app/path.component.html'; } ///////// Using Relative Paths /////// @Component({ moduleId: module.id, selector: 'relative-path', templateUrl: 'some.component.html', styleUrls: ['some.component.css'] }) export class SomeRelativeComponent { class = 'relative'; type = 'Component-relative template & style URLs'; path = 'path.component.html'; }
<div class={{class}}> {{type}}<br>{{path}} </div>
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; }
import { Component } from '@angular/core'; @Component({ selector: 'my-app', template: `<h1>Absolute & <i>Component-Relative</i> Paths</h1> <absolute-path></absolute-path> <relative-path></relative-path> ` }) export class AppComponent {}
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
:
moduleId: module.id,
Webpack: load templates and styles
Webpack developers have an alternative to moduleId
.
They can load templates and styles at runtime by adding ./
at the beginning of the template
and styles
/ styleUrls
properties that reference *component-relative URLS.
import { Component } from '@angular/core'; import '../../public/css/styles.css'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { }
Webpack will do a require
behind the scenes to load the templates and styles. Read more here.
See the Introduction to Webpack.
Please login to continue.