Lesson Weekend

Remember, we eventually want our application structure to include welcome, marketplace, about, and album detail pages.

app-planning-diagram

We haven't created our Album Detail Page yet but we already know that we'll need dynamic routing to display individual albums. If we want to view the very first Album listed in our store, the URL for the album detail page will probably look like localhost:4200/albums/1 and the page will display information specific to that single album. Other albums, however, will have different unique IDs and different content.

We'll need to create a dynamic route to access individual albums. In this lesson, we'll explore dynamic routing as we create the AlbumDetailComponent and the necessary route to load it. We'll also begin creating a model for our Album objects.

Adding a Model

In order to display data, we must model it. Let's create an Album model class now. Each Album will have the following properties:

  • title
  • artist
  • description
  • id

We can use Angular CLI to create our new model class with the following command:

$ ng g class album.model

This will create an album.model.ts file in our src/app directory. We'll place the following inside:

src/app/album.model.ts
export class Album {
  constructor (public title: string, public artist: string, public description: string, public id: number) { }
}

Let's add some Albums to our marketplace. We'll import our Album class and add a hard-coded list of Albums to the MarketplaceComponent:

src/app/marketplace/marketplace.component.ts
import { Component } from '@angular/core';
import { Album } from '../album.model';

@Component({
  selector: 'app-marketplace',
  templateUrl: './marketplace.component.html',
  styleUrls: ['./marketplace.component.css']
})


export class MarketplaceComponent {
  albums: Album[] = [
   new Album("Pulse", "Pink Floyd", 
       "A live  album by the English progressive rock band originally released in 1995, on the label EMI in the United Kingdom.", 1),
   new Album("Funhouse", "The Stooges", 
       "The second  album from the American rock band, released in 1970 by Elektra Records.", 2),
   new Album("Twilight of the Thunder God", "Amon Amarth", 
       "Seventh album by the Swedish band, released in 2008, based on Thor's battle with the serpent Jörmungandr.", 3),
   new Album("Dilate", "Ani DiFranco", 
       "Her highest-selling and most acclaimed album, released in 1996.", 4),
   new Album("Chopin - Complete Nocturnes", "Brigitte Engerer", 
       "Released in 2010, this is Engerer's own rendition of the classical composer Chopin.", 5),
   new Album("Axis Bold As Love", "The Jimi Hendrix Experience", 
       "Second studio album by the English-American band, released in 1967.", 6)
 ];
}

We'll also add code to list these albums in the MarketplaceComponent template:

src/app/marketplace/marketplace.component.html
<h2>Marketplace</h2>

<div *ngFor="let album of albums">
    <h3><em>{{album.title}}</em> by {{album.artist}}</h3>
</div>

Let's add basic styling and Bootstrap classes while we're here, too. We'll place each Album in a Bootstrap panel:

src/app/marketplace/marketplace.component.html
...
<h2>Marketplace</h2>

<div *ngFor="let album of albums" class="panel panel-default">
  <div class="panel-body">
    <h3><em>{{album.title}}</em> by {{album.artist}}</h3>
  </div>
</div>

Dynamic Routing

We'll want to click an individual Album in our MarketplaceComponent to visit a page displaying its details. However, both the URL for our Album Detail page and the content it displays will need to change depending on which Album the user selects, which means we need a dynamic route.

In Angular, a dynamic route is a route whose path and content is capable of changing depending on the circumstances. A dynamic route's paths include dynamic segments. A dynamic segment is a placeholder that is filled with different information depending on the circumstances.

For instance, the path in our new dynamic route will look like this:

localhost:4200/albums/:id

Here, :id is the dynamic segment because it will change depending on which Album we're visiting.

Create the Component

Let's make a new AlbumDetailComponent. The AlbumDetailComponent will be responsible for displaying each Album's details in our Album Detail page:

$ ng g component album-detail

This time, we won't remove the ngOnInit() method, OnInit interface, or constructor() from the new component's file.

Create a Router Entry

Let's define our new dynamic route in our Router:

src/app/app.routing.ts
...
import { AlbumDetailComponent }   from './album-detail/album-detail.component';

const appRoutes: Routes = [
  ...
  {
    path: 'albums/:id',
    component: AlbumDetailComponent
  }
 ];

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

Notice the dynamic :id segment in the route's path property above. By including the : before id we make the last portion of the path a variable instead of a literal string. :id will be replaced with the id number of the Album whose details we're viewing.

Navigating to Dynamic Routes

Now we need code to navigate from the Marketplace page listing all albums to a specific Album Detail page. Let's attach an event binding to each Album's div in the MarketplaceComponent:

src/app/marketplace/marketplace.component.html
<h2>Marketplace</h2>

<div *ngFor="let album of albums" (click)="goToDetailPage(album)" class="panel panel-default">
  <div class="panel-body">
    <h3><em>{{album.title}}</em> by {{album.artist}}</h3>
  </div>
</div>

As you can see, this event binding will trigger goToDetailPage() when a click event is registered. The specific Album attached to the click is provided as an argument to this method, too. Let's define goToDetailPage() in the MarketplaceComponent class:

src/app/marketplace/marketplace.component.ts
...
export class MarketplaceComponent {
...
   goToDetailPage(clickedAlbum: Album) {
     this.router.navigate(['albums', clickedAlbum.id]);
   };
...

Here we're referring to a router variable that does not yet exist. Let's create it now.

Referencing the Router

First, we'll need to import Angular's Router package:

src/app/marketplace/marketplace.component.ts
...
import { Router } from '@angular/router';
...

Then we'll create a new Router object in the constructor() method of MarketplaceComponent.

src/app/marketplace/marketplace.component.ts
...
export class MarketplaceComponent {

  constructor(private router: Router){}

  albums: Album[] = [
   ...
  ];

 goToDetailPage(clickedAlbum: Album) {
   this.router.navigate(['albums', clickedAlbum.id]);
 };
}

Here we've added a parameter to the component's constructor() method: a private Router object named router. This ensures that every instance of MarketplaceComponent has a Router object available when it is instantiated.

As we know, constructor methods are called when new instances of an object are created. Therefore, the constructor() method here in the MarketplaceComponent class will be called when new instances of our MarketplaceComponent are created. By passing a Router into constructor(), each instance of MarketplaceComponent will have an instance of the Router available to it. A constructor with empty curly-braces may look odd, but TypeScript constructor parameters automatically create a class property of the same name. The MarketplaceComponent now has a property called router that refers to our application's router.

We'll further explore the process of placing content in a component's constructor when we discuss services and dependency injection in upcoming lessons.

Building Route Paths

Now that our router variable is in place, let's revisit our goToDetailPage() method:

src/app/marketplace/marketplace.component.ts
...
 goToDetailPage(clickedAlbum: Album) {
   this.router.navigate(['albums', clickedAlbum.id]);
 };
}
...

When triggered, this method will gather the router instance provided in the constructor and call the built-in navigate() method on it, providing an array as an argument. The array contains the string 'albums' and clickedAlbum.id.

These arguments are used to construct the URL to our route. 'albums' refers to the first portion of the route's path. clickedAlbum.id refers to the dynamic segment of the path. If clickedAlbum.id is 37, this would create a route path of albums/37.

If we build and launch our application at this point, we should be able to click any Album and navigate to the detail view:

album-detail-view

Notice the Album's id is correctly included in the browser URL.

In the next lesson we'll explore how to pass information into a dynamic route so we may display details about specific Albums on this page.