Mastering Angular 8: five things that are good to know to save your time.
Tags: angular, webdev
Published: 2019-11-20 10:11:05

---
title: Mastering Angular 8: five things that are good to know to save your time.
published: true
description: Angular has some edge cases that can sometimes waste your time if you're not familiar with the details.
tags: angular, webdev
cover_image: https://s3.amazonaws.com/assets.fullstack.io/n/20191017144319392_titlepic.jpg
---

***This post was created for [ng-newsletter](ng-newsletter.com) and originally published on  [www.newline.co](www.newline.co)***

Angular has some edge cases that can sometimes waste your time if you're not familiar with the details. 
I've been working with Angular for the last 3,5+ years and here are some interesting things that I've learned that will save your time.  We're going to talk about:

* How HTTPInterceptors work with feature modules

* Query params are lost on angular router redirect

* ::ng-deep span - avoid encapsulation for specific elements (on an example of search words highlighting)

* Http with no subscribe

* Reload component on same URL navigation

Let's dig in.


*You can view the runnable demo here in Stackblitz and review the code in [this Github repo](https://github.com/kievsash/Mastering-angular-article/tree/5_same_route_reload).*

> If you want to get notified about my future video-course with advanced techniques of mastering Angular - [Subcribe!](http://eepurl.com/gHF0av) (And watch some free videos [here](https://www.youtube.com/playlist?list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu))

## #1. How HTTPInterceptors work with feature modules

Once upon a time, I had a mentoring session on codementor.io where we integrated some separate Angular application to work as a feature module of the main project app. Code was copied to the project features folder it was added a lazy-loaded module and added to the main app routing config.

The main `app.routing.ts` looked like this:

```js
const routes: Routes = [
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent},
  { path: 'feature',
    loadChildren: './some-feature/some-feature.module#SomeFeatureModule'}
];
```

And `feature.module`:

```js
@NgModule({
  declarations: [
    SomeComponentComponent
  ],
  imports: [
    CommonModule,
    SomeFeatureRoutingModule,
    HttpClientModule
  ],
  bootstrap: [
    SomeComponentComponent
  ]
})
export class SomeFeatureModule { }
```

At the end directory structure looks very similar to the code in our demo project:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191021104452041_1.4.png)

From a first glance - it should work like a charm. And actually it does.

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191021102328335_1.3.gif)Except for one small thing.

Main app component, as well as feature module component, perform some HTTP calls with standard Angular `HttpClientModule` and specific token-interceptor appends **Authorization** header to all outgoing network requests. 

But somehow feature module network requests were not handled by a token-interceptor of the main app.

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191021104822138_1.5.gif)But why?!


Well, I will not torment you with intrigue. The problem was that during independent development of feature - individual `HTTPClientModule` was added to *imports* array of `feature.module.ts`. And it made Angular create another instance of `HttpModule` with an empty custom interceptors list. 

How to avoid it? Just comment out `HTTPClientModule` in all other Angular modules except the main app module. (`App.module.ts`)

Now, the feature module looks like this:

```js
@NgModule({
  declarations: [
    SomeComponentComponent
  ],
  imports: [
    CommonModule,
    SomeFeatureRoutingModule,
    // HttpClientModule  <-- commented out
  ],
  bootstrap: [
    SomeComponentComponent
  ]
})
export class SomeFeatureModule { }
```

Knowing that peculiarity allows you to implement specific `HTTPInterceptors` for particular feature modules. All you have to do is to add `HTTPClientModule` to specific Angular feature module and define `HTTP_INTERCEPTORS` provider in the same module as well. Now in your feature module only its own interceptors will be used while all other application modules will use the root app module interceptor.

```js
@NgModule({
  declarations: [
    SomeComponentComponent
  ],
  imports: [
    CommonModule,
    SomeFeatureRoutingModule,
    HttpClientModule // separate feture module HTTPClientModule
  ],
  bootstrap: [
    SomeComponentComponent
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: FeatureInterceptor, // Hey, we have specific module interceptor now
      multi: true
    }
  ],
})
export class SomeFeatureModule { }
```

Let's check how it works:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191021110527593_1.8.gif)

You can play with code in a Stackblitz [playground](https://stackblitz.com/edit/angular-query-params-lost). Also, the full project code is [uploaded to GitHub "Mastering Angular" repo](https://github.com/kievsash/Mastering-angular-article/tree/1_HTTPInterceptor_in_modules).

## #2. Query params are lost on angular router redirect

Sometimes we have to specify query params for our Angular SPA. In that case, our application URL may look like:

***http://yourdomain.com/?serach=text&page=3***

It should work well. But very often Angular routing config uses ***redirectTo*** directive to lead request to a specific home component:

```js
const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent}
]
```

And we expect that Angular will change URL to: ***http://yourdomain.com/home?search=text&page=3***
But practically Angular changes it to:  ***http://yourdomain.com/home.***

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191022092621484_2.1.gif)

Query parameters are lost. ***HomeComponent*** will not be able to get them

How to deal with it?

There are three ways:

1. The simplest. Just use the direct **HomeComponent** route URL with all params:

***http://yourdomain.com/home?search=text&page=3***

Redirect is not happening in this case, so queryParams will not be lost.

2. The hardest. Listen to all route events and in case of redirect just copy query params and restore/assign them for /home page.

```js
import {Component, OnInit} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {filter, take, tap} from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'Mastering-angular';

  constructor(private route: Router) {
  }

  ngOnInit() {
    this.route.events.pipe(
      tap(() => console.log('tap')),
      filter((event) => event instanceof NavigationEnd
        && event.urlAfterRedirects === '/home'
        && event.url.search(/\?/) > -1 // look for '?' 
      ),
      take(1) // run handler once
    ).subscribe((event: NavigationEnd) => {
      console.log('Test')
      // extract queryParams as value: pair object.
      const queryParams = event.url.split('?')[1]
        .split('&')
        .reduce((acc, item) => ({...acc, [item.split('=')[0]]: item.split('=')[1]}), {});

      this.route.navigate([], {
        queryParams, // reload current route and add queryParams
        queryParamsHandling: 'merge'
      });
    });
  }
}
```

3. And one more nice solution - query params are not lost if we just remove '/' sign in route path, like this:

```js
const routes: Routes = [
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent}
]
```

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191021152629310_image%20%2817%29.png)![](https://s3.amazonaws.com/assets.fullstack.io/n/20191022092833434_2.2.gif)

You can play with different methods [here](https://angular-feature-module-interceptor2.stackblitz.io) in a playground and check the code in an [article repo](https://github.com/kievsash/Mastering-angular-article/tree/2_Query_params_on_redirect).

Which method is best for you? Leave your choice in comments.

## #3. ::ng-deep span - avoid encapsulation for specific elements (on an example of search words highlighting)

Ok, say you have a task make search text to be highlighted in specific paragraph.

How would you do that? The easiest way - just save the original text. And on each search just replace text we are looking for with `<span class="highlight">textToSearch</span>`

```js
  getHighlightedText(searchText) {
    const regexp = new RegExp(searchText, 'gi');
    const highlightedText = originalText.replace(regexp, '<span class="highlight">$&</span>');
    return highlightedText;
  }
```

And to highlight it in a `component.scss` file we put the rule:

```css
.highlight { background-color: yellow;}
```

You run it but...it doesn't work:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191022110415195_3.1.gif)What is your gut feeling why it doesn't work? My first thought was that somehow our CSS rule was not included in a final bundle. So I decided to search for it and this is what I've found:

```css
.highlight[_ngcontent-tls-c1] {
  background-color: yellow;
}
```

Angular applies CSS encapsulation for all component css rules. That's why our element is not highlighted. How to fix it? Just use ***::ng-deep*** prefix to tell Angular that this specific rule should not be modified:

```css
::ng-deep .highlight {
  background-color: yellow;
}
```

Now if we check our search with highlighting - it will work good:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191022110835162_3.2.gif)You can play with code on a [Stackblitz page](https://angular-search-and-highlight.stackblitz.io) or check it in [GitHub repo.](https://github.com/kievsash/Mastering-angular-article/tree/3_ng-deep_to_avoid_styles_incapsulation) You can read more about ***::ng-deep*** and styles encapsulation [here](https://blog.angular-university.io/angular-host-context/).

## #4. Http with no subscribe

If you monitoring the [Angular community on Twitter](https://twitter.com/JohannesHoppe/lists/angular-experts1/members) then you may notice that template-driven development is becoming a new trend. 

Good examples of that approach are:

1. **[Async-pipeline](https://github.com/Tibing/async-pipeline)**[ ](https://github.com/Tibing/async-pipeline)from [@NikPoltoratsky](https://twitter.com/NikPoltoratsky) - a set of interesting pipes that apply different RxJS operators in a template, for example:

   ```js
   // app.component.ts
   @Component({
     template: <code>
       {{ title$ | debounceTime:1000 | async }}
     </code>,
   })
   export class AppComponent {
     title$: Observable<string> = of('Hello, Async Pipeline!');
   }
   ```

2. **[ngx-template-streams ](https://github.com/typebytes/ngx-template-streams)**by [Dominic Elm](https://twitter.com/elmd_) - allows to assign domain events to specific properties in component class in a reactive way:

   ```js
   //html 
   <button (*click)="clicks$">Click Me (Stream)</button>
   
   //ts
   import { Component, OnInit } from '@angular/core';
   import { ObservableEvent } from '@typebytes/ngx-template-streams';
   import { Observable } from 'rxjs';
   
   @Component({...})
   export class AppComponent implements OnInit {
     @ObservableEvent()
     clicks$: Observable<any>;
   
     ngOnInit() {
       // we can either manually subscribe or use the async pipe
       this.clicks$.subscribe(console.log);
     }
   }
   ```

3. **[ngxf/platform](https://github.com/ngxf/platform)** by [ReactiveFox](Reactive Fox) with a [very good doc](https://ngxf.gitbook.io/platform/features/activatedroute) - a set of structured directives to reduce angular code boilerplate, for example:

   ```html
   <ng-container *route="let route">
       {{ route }} // is ActivatedRoute instance
   </ng-container>
   ```

4. And interesting ["RFC: Component proposal" issue thread](https://github.com/ngrx/platform/issues/2052) from [Michael Hladky](https://twitter.com/michael_hladky?lang=en) on how to make Angular even more reactive.

Let's apply this approach to quite a routine task when our Angular component does some network request inside *ngOnInit* hook. Very often code for that looks like this:

```js
export class SomeComponentComponent implements OnInit {

  data = {};

  constructor(private http: HttpClient) { }

  ngOnInit() {
    this.http.get('https://jsonplaceholder.typicode.com/todos/2')
      .subscribe((data) => {
        this.data = data;
    });
  }

}
```

```html
  <div class="card" style="width: 18rem;">
    <div class="card-body">
      <h5 class="card-title"><b>User id:</b> {{data.userId}} </h5>
      <p class="card-text"><b>Title:</b> {{data.title}}</p>
    </div>
  </div>
  <div class="wrapper">Raw user data: {{data | json}}</div>
```

But, as [Michael Hladky](https://twitter.com/michael_hladky?lang=en) mentions in his article "[How to Avoid Observables in Angular](https://dev.to/angular/how-to-avoid-observables-in-angular-273h)" - it is not reactive programming:). I would say - it is not a template-driven approach.

Can we do better?

Yes, we can omit the intermediary variable/property and use Angular async pipe. Now our code will look like this:

```js
export class HttpCallComponent {
  data$ = this.http.get('https://jsonplaceholder.typicode.com/todos/2');
  constructor(private http: HttpClient, private snackBar: MatSnackBar) { }
}
```

```html
<ng-container *ngIf="data$ | async as data">
  <div class="card" style="width: 18rem;">
    <div class="card-body">
      <h5 class="card-title"><b>User id:</b> {{data.userId}} </h5>
      <p class="card-text"><b>Title:</b> {{data.title}}</p>
    </div>
  </div>
  <div class="wrapper">Raw user data: {{data | json}}</div>
</ng-container>
```

But what about error handling in case of network 404 response? Let's apply  *[catchError](https://rxjs.dev/api/operators/catchError)* operator to handle network request failure - we will show an error message from its callback to notify the user of what happened.

```js
export class HttpCallComponent {

  data$ = this.http.get('https://jsonplaceholder.typicode.com/todos/2').pipe(
    catchError((err) => {
      this.snackBar.open('Network request is failed', 'Failed', {
        duration: 1500,
      });
      return of();
    })
  );

  constructor(private http: HttpClient, private snackBar: MatSnackBar) { }

}
```

Time to check how it works:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191023102117089_4.1.gif)

LGTM! You can play with this example in a Stackblitz [playground](https://angular-http-call-on-init-in-a-template.stackblitz.io) and check code in the article [GitHub repo](https://github.com/kievsash/Mastering-angular-article/tree/4_http_call_on_ngInit).

## #5. Reload component on same URL navigation

OK, so you got a legacy code where the main component logic is set in *ngOninit()*  lifecycle method. This means that if time is short - if we need to reset component values to initial - it is easier to re-create the whole component from scratch. But the problem is hidden here: Angular doesn't re-instantiate component when we try to navigate to the same URL that is already activated. 

OK, this is a problem. First what I did to find a solution - I read a nice article "*[Angular: Refetch data on same URL navigation](https://blog.angularindepth.com/refresh-current-route-in-angular-512a19d58f6e)*". But it doesn't provide solution with total component instance re-initialization - just methods to catch an event that activated route was clicked again. This is not what we want.

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191022172241550_5.1.gif)

What if it is possible to load some other route and then return to the current active one? But it may cause some visual artifacts on a page so a user can observe it. Can we somehow do that without any visual artifacts? 

The answer is "Yes" and I found it [here](https://stackoverflow.com/questions/40983055/how-to-reload-the-current-route-with-the-angular-2-router/53003923#53003923). To be short - solution code is simple and nice:

```js
this.router.navigateByUrl('/', {skipLocationChange: true})
  .then(()=>this.router.navigate([<route>]));
```

To apply it we should change our `routeLInk` to function call in `app.component.html`:

```html
  <div class="col-12 text-center">
      <a [routerLink]="['/home']" >Home</a>
      <a [routerLink]="['/feature']" >Feature</a>
      <a [routerLink]="['/search']" >Search</a>
      <a [routerLink]="['/httpOnInit']" >HttpOnInit</a>
<!--      <a [routerLink]="['/sameRoute']" >SameRouteReload</a>-->
      <a class="route_link" (click)="goToRandomNUmberPage($event)" >SameRouteReload</a>
  </div>
```

And apply this code in `app.component.ts`:

```js
  goToRandomNumberPage(event) {
    event.preventDefault();

    this.router.navigateByUrl('/', {skipLocationChange: true})
      .then(() => this.router.navigate(['/sameRoute']));
  }
```

Now it should work as we want:

![](https://s3.amazonaws.com/assets.fullstack.io/n/20191023103834343_5.2.gif)Cool!

You can check it in Stackblitz [codepen](https://angular-same-route-reload.stackblitz.io) and review the code in [article github repo](https://github.com/kievsash/Mastering-angular-article/tree/5_same_route_reload).

## Summary

Angular-city streets are a safe place for everyone whether you are a beginner or experienced developer, but in dark corners, there are entities that wait for their turn to waste your time. I hope this article will help you to eliminate time-waste and deliver maintainable clean code quickly and in high quality. 

If you liked this article - join me on [Twitter](https://twitter.com/El_Extremal) and [Buy me a coffee ☕️🤓](https://www.buymeacoffee.com/nqypxi6).

### More to read and watch:

1. [Beware! Angular can steal your time.](https://blog.angularindepth.com/beware-angular-can-steal-your-time-41fe589483df)

2. [Mastering RxJS: operators and functions that can bite you when you don’t expect](https://blog.angularindepth.com/mastering-rxjs-operators-and-functions-that-can-bite-you-when-you-dont-expect-cb2047cf5d4c)

3. [Angular: Refetch data on same URL navigation](https://blog.angularindepth.com/refresh-current-route-in-angular-512a19d58f6e)

4. [Hands-on RxJS for Web Development](https://udemy.com/hands-on-rxjs-for-web-development/)

5. [RxJS unit testing in Angular 8. Whole picture (Free video-course).](https://mailchi.mp/9c8299ec62c2/rxjsunittesting)
6. ["Angular can waste your time"](https://www.youtube.com/playlist?list=PLNadw4d8-KMVSOffiYBuOlzvF38sO9pdu) YouTube video-series.