Mastering Angular 8: five things that are good to know to save your time.

Summary


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 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.

#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:

And feature.module:

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


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

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.

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:

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.

Let's check how it works:


You can play with code in a Stackblitz playground. Also, the full project code is uploaded to GitHub "Mastering Angular" repo.

#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:

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.


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.

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

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


You can play with different methods here in a playground and check the code in an article repo.

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>

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

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

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:

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:

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

You can play with code on a Stackblitz page or check it in GitHub repo. You can read more about ::ng-deep and styles encapsulation here.

#4. Http with no subscribe

If you monitoring the Angular community on Twitter then you may notice that template-driven development is becoming a new trend.

Good examples of that approach are:

  1. Async-pipeline from @NikPoltoratsky - a set of interesting pipes that apply different RxJS operators in a template, for example:

  2. ngx-template-streams by Dominic Elm - allows to assign domain events to specific properties in component class in a reactive way:

  3. ngxf/platform by ReactiveFox with a very good doc - a set of structured directives to reduce angular code boilerplate, for example:

  4. And interesting "RFC: Component proposal" issue thread from Michael Hladky 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:

But, as Michael Hladky mentions in his article "How to Avoid Observables in Angular" - 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:

But what about error handling in case of network 404 response? Let's apply catchError operator to handle network request failure - we will show an error message from its callback to notify the user of what happened.

Time to check how it works:


LGTM! You can play with this example in a Stackblitz playground and check code in the article GitHub repo.

#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". 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.


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. To be short - solution code is simple and nice:

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

And apply this code in app.component.ts:

Now it should work as we want:

Cool!

You can check it in Stackblitz codepen and review the code in article github repo.

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!

More to read and watch:

  1. Beware! Angular can steal your time.

  2. Mastering RxJS: operators and functions that can bite you when you don’t expect

  3. Angular: Refetch data on same URL navigation

  4. Hands-on RxJS for Web Development

  5. RxJS unit testing in Angular 8. Whole picture (Free video-course).