Tweaking the base path
When we began working on our application routing, we defined the base href
of our application at index.html
, so the Angular router is able to locate any resource to load apart from the components themselves. We obviously configured the root /
path, but what if, for some reason, we need to deploy our application with another base URL path while ensuring the views are still able to locate any required resource regardless of the URL they’re being served under? Or perhaps we do not have access to the HEAD
tag in order to drop a <base href="/">
tag, because we are just building a redistributable component and do not know where this component will wind up later. Whatever the reason is, we can easily circumvent this issue by overriding the value of the APP_BASE_HREF
token, which represents the base href
to be used with our LocationStrategy
of choice.
Try it for yourself. Open the main.ts
file where we bootstrap the application, import the required tokens, and override the value of the aforementioned base href
application variable by a custom value:
app/main.ts
import 'rxjs/add/operator/map'; import { bootstrap } from '@angular/platform-browser-dynamic'; import AppComponent from './app.component'; import { provide } from '@angular/core'; import { APP_BASE_HREF } from '@angular/common'; bootstrap(AppComponent, [provide(APP_BASE_HREF, { useValue: '/my-apps/pomodoro-app' })]);
Reload the app and see the resulting URL in your browsers.
Finetuning our generated URLs with location strategies
As you have seen, whenever the browser navigates to a path by command of a routerLink
or as a result of the execution of the navigate method of the Router
object, the URL showing up in the browser’s location bar conforms to the standardized URLs we are used to seeing, but it is in fact a local URL. No call to the server is ever made. The fact that the URL shows off a natural structure is because of the pushState
method of the HTML5 history API that is executed under the folds and allows the navigation to add and modify the browser history in a transparent fashion.
There are two main providers, both inherited from the LocationStrategy
type, for representing and parsing state from the browser’s URL:
PathLocationStrategy
: This is the strategy used by default by the location service, honoring the HTML5pushState
mode, yielding clean URLs with no hash-banged fragments (example.com/foo/bar/baz
).HashLocationStrategy
: This strategy makes use of hash fragments to represent state in the browser URL (example.com/#foo/bar/baz
).
Regardless of the strategy chosen by default by the Location
service, you can fallback to the old hashbang-based navigation by picking the HashLocationStrategy
as the LocationStrategy
type of choice.
In order to do so, go to main.ts
and tell the Angular global injector that, from now on, any time the injector requires binding the LocationStrategy
type for representing or parsing state (which internally picks PathLocationStrategy
), it should use not the default type, but use HashLocationStrategy
instead.
It just takes to override a default provider injection:
app/main.ts
import 'rxjs/add/operator/map'; import { bootstrap } from '@angular/platform-browser-dynamic'; import AppComponent from './app.component'; import { provide } from '@angular/core'; import { LocationStrategy, HashLocationStrategy } from '@angular/common'; bootstrap(AppComponent, [provide(LocationStrategy, { useClass: HashLocationStrategy })]);
Save your changes and reload the application, requesting a new route. You’ll see the resulting URL in the browser.
Note
Please note that any location-related token in the example is not imported from ‘@angular/router-deprecated’ but from ‘@angular/common’ instead.
Loading components asynchronously with AsyncRoutes
As you have seen in this chapter, each route definition needs to be configured with a component
property that will inform the router about what to load into the router outlet when the browsers reach that URL. However, we might sometimes find ourselves in a scenario where this component needs to be fetched at runtime or is just the by-product of an asynchronous operation. In these cases, we need to apply a different strategy to pick up the component we need. Here’s where a new type of router definition named AsyncRoute comes to the rescue. This specific kind of route exposes the same properties of the already familiar RouteDefinition
class we have been using along this chapter. It replaces the component
property with a loader
property that will be linked to a Promise
that resolves asynchronously to a component loaded on demand.
Let’s see this with an actual example. In order to keep things simple, we will not be importing the component we want to load at runtime, rather we will return it from an asynchronous operation. Open the top root component module and replace the route pointing to TimerComponent
with this async route definition:
app/app.component.ts
... @RouteConfig([{ path: '', name: 'Home', redirectTo: ['TasksComponent'] }, { path: 'tasks', name: 'TasksComponent', component: TasksComponent, useAsDefault: true }, { path: 'tasks/editor', name: 'TaskEditorComponent', component: TaskEditorComponent }, { path: '/timer/...', name: 'TimerComponent', loader: () => { return new Promise(resolve => { setTimeout(() => resolve(TimerComponent), 1000); }); } } ]) export default class AppComponent {}
The next time we attempt to load any route belonging to the timer branch (either the generic timer accessible from the nav bar or any task-specific timer), we will have to wait until the Promise
resolves to the component we need. Obviously, the goal of this example is not to teach how to make things load slower, but to provide a simple example of loading a component asynchronously.
We have now uncovered the power of the Angular router and we hope you have enjoyed this journey into the intricacies of this library. One of the things that definitely shines in the Router
module is the vast number of options and scenarios we can cover with such a simple but powerful implementation.
In this chapter, we discussed how to install and provide support for routing in our applications and how to turn any given component into a routing component by decorating it with the router configuration decorator and placing a router outlet in its template, even spreading routers downward in the components tree. We also saw how to define regular routes and some other advanced types such as redirect or async routes. The routing lifecycle has no secrets for us anymore and harnessing its power will open the door to deliver advanced functionalities in our applications with no effort. The possibilities are endless and, most importantly, routing contributes to delivering a better browsing experience.