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.
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.
app.routing.ts looked like this:
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.
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. (
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:
Sometimes we have to specify query params for our Angular SPA. In that case, our application URL may look like:
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:
The simplest. Just use the direct HomeComponent route URL with all params:
Redirect is not happening in this case, so queryParams will not be lost.
The hardest. Listen to all route events and in case of redirect just copy query params and restore/assign them for /home page.
And one more nice solution - query params are not lost if we just remove '/' sign in route path, like this:
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
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:
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:
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:
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:
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
And apply this code in
Now it should work as we want:
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!