Setting up basic Routing and Navigation in Angular

The way we navigate in an SPA(Single Page Application) is totally different from navigation we have been doing in our old traditional application. If you observe the difference between these two, you would find that applications with a Single Page have much better user experience as the pages served by the application would be much faster than traditional apps where the pages have to be requested from the server each time user makes requests.
I hope you would not mind if I start these articles with a few questions and answers.
Why do we need a Router in our application?
Well! you can say, because we need to display all the components one at a time, loading dynamically after clicking the specific the link for it in our app.
Hmmm. That's perfectly fine!
Let us create a basic project setup so we can apply our learnings one by one. We have a tiny project, where we would have some basic navigation links to navigate through. I have used bootstrap’s nav tabs classes for enhanced user experience, you could use too.
//app.component.html
<div class="container">
 <div class="row">
  <div class="col-xs-12">
   <ul class="nav nav-tabs">
    <li role="presentation" class="active">
    <a href="#">Home</a>
    </li>
    <li role="presentation"><a href="#">Angular</a></li>
    <li role="presentation"><a href="#">Firebase</a></li>
   </ul>
  </div>
 </div>
</div>
I have chosen to prepare those navigation structures in the app component view file since it is the main component our application and all the other component would be its child component. The above codes will render on the display something like below image.
insert
With the above picture, it has been cleared that we are going to load three components one at a time once we click that specific links. So, our three component would be HomeComponent, AngularComponent, and FirebaseComponent. Let me quickly create those ones.
insert
Now in the app.component.html file, we need to make some place for rendering these components. I would choose right below the navigation links row div, add a div with row class and inside the row div, add one more div with column class and there we would render these components.
<div class="row">
    <div class="col-xs-12">
      <app-home></app-home>
      <app-angular></app-angular>
      <app-firebase></app-firebase>
    </div>
  </div>
  
so the full structure will look like
<div class="container">
 <div class="row">
  <div class="col-xs-12">
   <ul class="nav nav-tabs">
    <li role="presentation" class="active"><a href="#">Home</a></li>
    <li role="presentation"><a href="#">Angular</a></li>
    <li role="presentation"><a href="#">Firebase</a></li>
   </ul>
  </div>
 </div>
  <div class="row">
    <div class="col-xs-12">
      <app-home></app-home>
      <app-angular></app-angular>
      <app-firebase></app-firebase>
    </div>
  </div>
</div>
insert
As of now, these links will no longer work. Since our application is Single Page type, we need to manage these routes internally inside the Angular, in order to navigate through one another.
When I say routes, it means I am talking about those navigation links and components and if we click one of those links, we must be routed to the component related to that link. Suppose if we click on Home, we must be routed to Home component view and same for rest of two links.
Ok, to make those navigation links work, we need to register those Routes inside our application so Angular can try to understand them.
For this, So, App Module(app.module.ts) seems to be a good place for it. Since, we configure our app here, loading all our components and so on, So this might be a good place to inform angular about our routes.
Declaring a Route constant.
Before registering those routes, we need to have a certain configuration, where we could precisely define the relationship between the router navigation links and their component they will load once they got clicked. These details will be stored in a Constant.
To declare a route constant which will contain all the routing details for all component, The route constant should be of a specific type which is Routes and its needs to be imported from a @angular/router
const appRoutes: Routes
so make sure to add this import at the top.
import { Routes } from '@angular/router';
This is the type informing angular for providing, routes a structure, we can say. Well, this is not necessarily to add it but can be a good practice.
What should this route constant hold?
This route constant should hold an array [] because we will have multiple routes inside it and each route is now just a javascript object in this array.
const appRoutes: Routes = [
{ }
];
How such routes should be configured in an angular App?
It has to follow a specific pattern or certain structure for the angular to use it and this structure always needs a path and this is what gets entered in the URL after our domain eg localhost:4200/angular where angular is a path
It should be added without slash or else it will be incorrect.
const appRoutes: Routes = [
{path: 'angular'}
];
Then we also need to define, what should happen when his path is reached, right now, it is a route but no action is attached to it. so the action is typically is a component so that we inform angular, once this path is reached, the certain component should be loaded and this component is a certain page which is to be loaded.
const appRoutes: Routes = [
  { path: 'angular', component: AngularComponent}
];
So this our angular component which is loaded when we target /angular
const appRoutes: Routes = [
{ path: "", component: HomeComponent },
{ path: "angular", component: AngularComponent },
{ path: "firebase", component: FirebaseComponent }
];
we have kept the path value blank for the Home Component so, when our application gets loaded for the first time, Home component will be loaded on the top of App component view.
Now how would angular know that we want to use this constant?
we could have added anything here, any constant and the name is up to us. So, this appRoutes constant will be ignored here and type Routes could not do anything here when it comes to this.
So, Somehow we need to register these routes in our app and we do this by adding new import to the imports array which is RouterModule and RouterModule should be imported from our @angular/router package
import {
  Routes,
  RouterModule
} from '@angular/router';
@NgModule({
  imports: [BrowserModule, FormsModule, RouterModule],
  declarations: [AppComponent, HelloComponent, HomeComponent, AngularComponent, FirebaseComponent],
  bootstrap: [AppComponent]
})
But still our routes are not registered yet, that is why we have a special method forRoot which will allow our routes to register here. forRoot will now receive our appRoutes constant as an argument here and with that our Routes are now registered in our angular app on this router module which gives us this routing functionality and now angular knows our routes.
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
  ],
  declarations: [AppComponent, HelloComponent, HomeComponent, AngularComponent, FirebaseComponent],
  bootstrap: [AppComponent]
})
Now, where do we render our selected component?
Ok, so now we if we go with domain/home, angular knows that it has to load home component but how does it will come to know about the place it should be loaded in the page. for example, it has to load anywhere in this app component page, for example at the header, at footer our between the row.
So, the right place is to inform angular to load that entered specific component is to get rid of all this selector component and now we don’t add the component with our selector but we add special directive shipping with Angular,
router-outlet
<div class="row">
  <div class="col-xs-12">
   <router-outlet></router-outlet>
  </div>
 </div>
Note: This look like a component but it still a directive, Keep in mind, the directive may have any selector including this component which is element style selector.
With this, This simply marks the place in our document where we want the angular router to load the component of the currently selected routes.
the entered paths in our address bar after domain will work in our application.
insert
But still, our application is not complete. Since the clicking, the navigation links won't allow loading the selected component.
Navigating with Router Links
Router Link(syntax: routerLink)
While using a router in angular, we could not use href=”/home” to navigate to the home component. Though, It will work by loading the requested component, but since we have a single page application and this will impact our app by re-loading the app which should not be done. This is a problem and needs to be fixed. This is however not the best behavior, that means it restart our app on every navigation, requesting the specific page from the server. We may lose the state and don’t want to really offer this behavior to our end user. So, This is not how we should implement navigation.
insert
There is a special directive angular offer us. Router Link
Let’s get rid of all those href attributes and replace it with routerLink
<li role="presentation" class="active">
<a routerLink="/">Home</a>
</li>
    <li role="presentation">
<a [routerLink]="'/angular'">Angular</a>
</li>
    <li role="presentation">
<a [routerLink]="['/firebase']">Firebase</a>
</li>
we can use with a single quotation mark between the string
<li role=”presentation” routerLinkActive=”active”><a [routerLink]=”’/Home’”>Home</a></li>
or better an array which will give us fine-grained control
<a [routerLink]="['/firebase']">Firebase</a>
and here we can specify all the segments of our path as an element in this array and if we have any second element, we will place after the first element without using slash /
<a [routerLink]="['/firebase', 'secondPath']">Firebase</a>
here leading slash is just used to make an absolute path.
This is how we can navigate around using router link, and this is how it should be used because it doesn’t reload the page, keep the app state and navigates much faster.
How does Router Link work?
If we now notice the reload icon of our browser, we could not find it re-loading while we switch our page via navigation.
Router Link catches the click on the element, prevent the defaults which would be to send the request and it then analyzes here, what we passed in the router link directive, so this path or array of path elements and then it parses it then it checks if it finds any fitting routes in our configuration in the app module and which of course does as we defined all the paths we will be using later.
By here, our application has working navigation but although we can notice one problem and once we navigated to another component, the currently selected tab is not highlighted except the Home tab which is always in the case.
insert
Router Link Active
Now, our routes are working an expected, but let’s say we have certain classes which need to be applied on the navigation link to mark it as active with respect to the currently loaded path. So in our app.component.html we will place this directive on every anchor tag element and will pass the active class to it. Now active on the link itself would not do anything when using bootstrap so we will apply it on the list.
Router Link Active Directive does one thing, it analyzes currently loaded path and checks which links lead to the route which uses this path and by default. it marks an element as active and its CSS class, here “active” if it contains the path you are on or if this link is part of the path which is currently loaded. So for the “/firebase” link or the “/angular”link and for the slash nothing “/” link which is the first.
<ul class="nav nav-tabs">
    <li role="presentation" routerLinkActive="active">
     <a routerLink="/">Home</a>
    </li>
    <li role="presentation" routerLinkActive="active">
     <a [routerLink]="'/angular'">Angular</a>
    </li>
    <li role="presentation" routerLinkActive="active">
     <a [routerLink]="['/firebase']">Firebase</a></li>
   </ul>
Now we will apply this on the all the list, theoretically all the elements will receive this active class when they are clicked and active.
insert
Well! we have solved our previous problem and the navigation tabs are getting highlighted with active class but I think we can make it still better by solving one more problem.
Why our Home component is always marked?
One thing we could notice in the above pic is, that the home tab is always active even after switching the links.
For slash nothing (“/”) on the home page. This is always the case. We always have Slash nothing at the beginning of our path here. So if we are on slash “angular” Well there is this empty path in between you could say the same for “firebase” and the same for just home of course. So the empty path segment is part of all paths. Still we don’t want to have the home being marked as active all the time.
routerLinkActiveOptions: To mark exact router link active
So to fix this we can add some configuration to this router link active directive we can add a special configuration to our router link active of the directive. So on the same element as routerLink active was added, so we can add routerLinkActiveOptions. And this now needs some property binding because we don’t just pass a string there.

Instead, we pass a javascript object and that would not work without having our square brackets. So with square brackets, we can pass anything which will be resolved dynamically like this javascript object.
[routerLinkActiveOptions]="{}"
And here we can basically add one important configuration say exact and set this to true.
<ul class="nav nav-tabs">
    <li role="presentation"
        routerLinkActive="active"
        [routerLinkActiveOptions]="{exact: true}">
     <a routerLink="/">Home</a>
    </li>
    <li role="presentation" routerLinkActive="active">
     <a [routerLink]="'/angular'">Angular</a>
    </li>
    <li role="presentation" routerLinkActive="active">
     <a [routerLink]="['/firebase']">Firebase</a></li>
</ul>
so exact as a kind of a reserved property on this object you passed to Router link active options. And this will now basically tell Angular only as this router link act of class. So this active CSS Class in this case. If the exact the full path is well whatever does link leads to. So only if everything is just a slash(/) and not if it is only part of the path so does.

With this, we are have fixed all the existing problem in our app and it is working better as expected.
insert
Well, we are near the end of this article, but I am interested to discuss one more functionality which is handling 404 not found error. It occurs when users try to access some page of your application which is unavailable at that moment.

Let’s say one of your application users tries to access localhost:4200/abc where you have not configured routes for “abc” in the router configuration. Then your application may break with these below errors.
insert
So, how can we handle this type of errors?
We will start will creating one more component called “page-not-found”, here you can name it anything. For me, the component name seems to be self-explanatory. Now we can add any message to the page-not-found template file. I will add “This page is not available”
//page-not-found.component.html
<p> This page is not available </p>
Now, we need to modify our router configuration with something below
const appRoutes: Routes = [{
    path: '',
    component: HomeComponent
  },
  {
    path: 'angular',
    component: AngularComponent
  },
  {
    path: 'firebase',
    component: FirebaseComponent
  },
  {
    path: 'page-not-found',
    component: PageNotFoundComponent
  },
  {
    path: "**",
    redirectTo: 'page-not-found'
  }
];
I have added ‘page-not-found’ route in the Routes array and at bottom, we have used double asterisks (**) which is a wildcard. So, if we receive route requests other than registered routes will be redirected to the Page not found the component. If you place that last line at the very top of the array then all the requested routes will be routed to Page not found the component as it will not allow matching remaining the configured routes in the array.

Thanks for reading this article. Please let me know your sugestions in the comment box.

People Reaction : 0

Rohit Sharma
Name : Email : Website :
© 2020 WriteSomeCode. All Right Reserved. A Rohit Sharma Blog. Creative Commons License licensed under a Creative Commons Attribution 4.0 International License