A Stock Portfolio using Angular 6 : Routing

Hashtags

Routing is a cornerstone of single page applications (SPA’s). In SPA’s, a single page is loaded and the application resides within it. Re-loading the page or navigating to a new page or url is a no-no.Keeping the url static for the lifetime of the SPA is equally bad. Static urls don’t allow the web browser to collect history. Without history the web browser’s back button will just go to whatever site you visited previously – even if you spent 10 minutes judiciously navigating around the SPA. Bookmarks will become meaningless as well.

The Hash

So changing the url is mostly bad. A way around this is using hashes (#) in the URL Path. Hashes were initially used to navigate within a page to different anchor tags ( <a/> ) on the same page. They don’t cause a page refresh and are collected in the web browser’s history. A good example is a page that has a table of contents in the header. Clicking the hyperlinks in the table of contents scrolls down to that particular section on the page which is tagged with anchor. Adding ( or removing ) hashes to the URL does not cause the page to refresh.

This doesn’t cause a page refresh :

http://www.myapplication.com/quote#addnews

…this does :

http://www.myapplication.com/quote/addnews

In general, routing in a SPA leverages URL hashes. Navigating to different parts of the application changes the page’s URL by manipulating hashes.

Routing in Angular

In Angular, Routing loads a Component given a URL Path. This was my first stab at a Routing Table for the Stock Portfolio Application.

const appRoutes: Routes = [

  { path: 'quote', component: TickerComponent },
  { path: 'quote/:id', component: TickerComponent,
    children:[
      { path: 'details', component: TickerContainerComponent},       
      { path: 'addNews', component: TickerNewsAddComponent}, 
      { path: 'addSymbol', component: TickerAddComponent},       
    ] },
  { path: '', redirectTo: '/quote', pathMatch: 'full'}
];

So, ../quote/msft will load the TickerComponent with ticker info for Microsoft.

And the “Child Route” ../quote/msft/details will load the TickerContainerComponent as a child element of the TickerComponent.

Child Routes

Child routes allow you to display a child component within a parent component based upon the URL. Typically, the Application component template contains a <router-outlet> directive that displays the initial Component. Parent Components follow a similar pattern. They will likewise have a <router-outlet> directive in which their child component gets loaded.

To clarify I should point out that the ultimate routing table that I ended up using for the Stock Portfolio does not use child routes. I just wanted to give a quick overview of them here as well, I almost used them.

The Routing Module

This is what the Routing Module ( minus the Routing Table ) looks like :

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { APP_BASE_HREF} from '@angular/common';

import { TickerComponent } from './components/ticker/ticker.component';
import { TickerAddComponent } from './components/ticker-add/ticker-add.component';
import { TickerNewsAddComponent } from './components/ticker-news-add/ticker-news-add.component';
import { TickerContainerComponent } from './components/ticker-container/ticker-container.component';

const appRoutes: Routes = [
	/* Routing Table */
];

@NgModule({
 providers: [
    {provide: APP_BASE_HREF, useValue: '/stockapp'}
  ],

  imports: [
    RouterModule.forRoot(
      appRoutes,
      { enableTracing: true } // <-- debugging purposes only
    )
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {}


Make sure that you import all of the Components that are loaded by the URL Paths into the Module. The router needs to know about the Components it should load.

What is APP_BASE_HREF?

After migrating routing to a Module I kept getting an error when running my unit tests for the “root” AppComponent:

“Please provide a value for the APP_BASE_HREF token or add a base element to the document.”

Strangely, the error was NOT occuring at runtime. To fix the error I followed these instructions on the Angular website. The router needs to know which “base url” to use when navigating to your routes.

Why a Separate Module?

Best practices dictate that routing should be kept in a separate module from the rest of the application. Another best practices should also be to explain why doing something as such is a best practice. Unfortunately, the official Angular documentation dictates it as such and glosses over the “why”. I’ll take a stab at it :

  1. Seperation of Concerns. Components shouldn’t care what URL invoked them. They should be stand-alone and isolated from such things.
  2. Strategy Pattern. Keeping routing in a seperate module makes it easier to change which URLs launch which Components.

In a hurry?

The finished source code is available on my GitHub repository.

The finished application is up-and-running on Heroku here. Be aware that it is running on a “free” development server so give it a minute or so to spool up.

That’s it for now

Please let me know if you have any questions or comments. If you want to start from the beginning please go here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s