Lesson Weekend

In the last lesson, we mapped out a plan for the structure of our online store application:

route-planning-4

The application will contain multiple routes. Depending on the route to which the user navigates, the URL in the browser and the child component displayed in the root component (between the <router-outlet> tags) will change.

In this lesson, we'll walk through the process of creating a router and implementing it in an Angular application. We'll create a route to display the WelcomeComponent we described in the last lesson.

Angular Routers

In most MVCs, including Angular, we can create multiple-page applications by using a router. In terms of the MVC architecture, a router is responsible for navigating between different views in an application.

As the user clicks navigational links within our app or uses the 'back' and 'forward' browser buttons in our application, a new URL is produced. This invokes the router, which matches the path of the URL with the defined route that matches that path. It then loads the components and other content that route requires.

Implementing a Router

Base Tag

We'll need to have a special tag called a base tag in our index.html file. Angular CLI has already added this base tag for us. Look below the <title> tags to find the line <base href="/">:

index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>OnlineStore</title>
    <base href="/">

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
  </head>
  <body>
    <app-root>Loading...</app-root>
  </body>
</html>

The base tag enables an HTML 5 feature called pushState routing, which Angular's own routing depends on. This allows us to make our in-app URL paths look the way we choose (ie: localhost:4200/about).

You're not required to know the details of this for our course. If you'd like to explore this concept further, check out the Mozilla Developer Network entry on pushState() and the Angular Documentation on Base Tags

Router File

Next we need to create the router itself. It will reside in a file known as the routes or router file. We'll create this now.

In the app directory, create a file named app.routing.ts. This file name is special. All route files should be named app.routing.ts.

(Note: At the time of this writing, the Angular CLI tool has disabled route creation directly from the command line. Check the Generating a Route section of their documentation in the future for updates regarding when/whether this feature will be available again.)

Within our router file, we'll import two important pieces of the Angular framework:

online-retail-store/src/app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
  • The ModuleWithProviders package from the Angular core helps provide our router to the rest of the application. We'll learn about "providers" in detail in an upcoming lesson when we discuss something called dependency injection.

  • Routes and RouterModule contain code that will help us render specific components when specific URLs are provided to the router. They're not part of the Angular core by default, so we must import them here.

Routes Array: appRoutes

Next, we'll define an array called appRoutes. It will contain the master list of all available routes in our application:

online-retail-store/src/app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const appRoutes: Routes = [ ];

Let's walk through what's going on here:

  • Notice appRoutes has been declared as the Routes data type. This file has access to Routes because we've imported { Routes, RouterModule }.

  • Also note that the keyword const precedes our new appRoutes array. Including const before declaring a property or variable makes something a constant. A constant is a value that other code in our application cannot change. It's a read-only reference that cannot be redefined. Check out the Mozilla Developer Network's const entry for more details. We don't want to risk any other portion of our application altering our appRoutes array, so we declare it a constant.

  • Additionally, keep in mind that all routes must be included in the appRoutes list. That means we'll have to manually update appRoutes to include each new route we create.

Exporting Routes

Next, our file needs to export our routes to the rest of the application. We do this by passing our appRoutes variable into the forRoot() method of the RouterModule we imported, like this:

online-retail-store/src/app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const appRoutes: Routes = [ ];

export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
  • The return value of forRoot() method is passed into the new variable called routing.

  • Our routing object is a ModuleWithProviders data type. This is simply a special type of module that includes something called providers to help make information (like our routes) available to the rest of the application. Again, we'll learn about providers in an upcoming lesson on dependency injection.

  • Notice that routing is being exported with the export keyword and that it's a constant.

This makes our appRoutes list of routes available to our root module in app.module.ts. We'll also update our root module before the end of this lesson.

Defining a New Route

At the end of the lesson, our application structure will look like this:

route-planning-2

Let's create a WelcomeComponent and a route to it.

First, generate the new component:

ng g component welcome

Don't worry about adding content to this component yet. Next, let's add the route.

Each route in an Angular application is actually a special type of object with code that looks like this:

{
  path: '',
  component: WelcomeComponent
},

Note: The code above is only for demonstration purposes. Don't worry about adding this to your application yet!

Every route has path and component properties.

  • path refers to the URL segment that should correspond with this route. For instance, if we wanted to create a route users could navigate to by visiting localhost:4200/super-crazy-route, the path property in our route object would read super-crazy-route. If a route has a blank string as its path property, as seen above, that means it's the index path located at localhost:4200.

  • component refers to the primary component for a route. That is, the component that should be rendered when the user navigates to this route. The route object above will ensure the WelcomeComponent is displayed when the user visits the application's root path URL at http://localhost:4200/.

Let's add this first Route object into our app.routing.ts router file. First, we'll need to import any components our new route will need:

online-retail-store/src/app/app.routing.ts
...
import { WelcomeComponent } from './welcome/welcome.component';
...

We'll also add the new route object to our appRoutes array:

online-retail-store/src/app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { WelcomeComponent } from './welcome/welcome.component';

const appRoutes: Routes = [
  {
    path: '',
    component: WelcomeComponent
  }
];

export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

Root Module

Next, we need to add our routes file and any components it loads to our root module. (Angular CLI should have imported our WelcomeComponent when we generated it, but always make sure to double-check.)

First, we import them:

online-retail-store/src/app/app.module.ts
...
import { WelcomeComponent } from './welcome/welcome.component';
import { routing } from './app.routing';
...

routing refers to the constant being exported at the bottom of our routes file:

online-retail-store/src/app/app.routing.ts
...

export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

We'll also need to add the routing constant to our root module's imports array:

online-retail-store/src/app/app.module.ts
...
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    routing
  ],
...

The imports array is used to import other modules into the current module. Note that this differs from the import statements at the top of our files. (The ones that look like import { WelcomeComponent } from './welcome/welcome.component';. Import statements simply import other modules' code into a single file.

Rendering Routes with a Router Outlet

Finally, we need to update our root AppComponent. We must designate where the router should load content for our different routes.

For instance, we told our router to load the WelcomeComponent when the user visits the root path at localhost:4200/. We need to tell Angular where the WelcomeComponent should be rendered.

We do this with a special tag called <router-outlet>. This tag denotes exactly where a route's components will be rendered. Let's place <router-outlet></router-outlet> tags in our root component's template now.

src/app/app.component.html
<h1>{{title}}</h1>
<router-outlet></router-outlet>

Let's go ahead and add a div and page title to our root component:

src/app/app.component.html
<div class="container">
  <h1>{{title}}</h1>
  <router-outlet></router-outlet>
</div>

Let's change the title property in our AppComponent class:

src/app/app.component.ts
...
export class AppComponent {
  title = 'Epicodus Tunes';
}

And the template of our WelcomeComponent:

src/app/welcome/welcome.component.html
<h2>Welcome to our store!</h2>

Launch the application with the $ ng serve command and the WelcomeComponent will be displayed at the default root path:

router-works-demo

Now that we have a router and our very first route, we'll discuss how to navigate between routes and manage multiple routes in an Angular application in the next lesson. We'll also create our remaining About, Marketplace and Album Detail pages.