This article originally appeared on dormoshe.io
Angular is a new platform. We learn how to use it, how to optimize the application and how to improve our code. As part of being team members, we do code reviews and trying to improve each other skills. As part of the code reviews and reading of code examples on the web, I pay attention to some common mistakes of Angular developers. It can be a junior or an expert developer, we all make mistakes in the code.
In this article, we will cover the top common mistakes, and we will understand how to fix them. Each of those can be an idea to a full article. We will cover them briefly but in an exhaustive manner.
The new Angular is not just a new version of Angular. It’s a rewrite of the framework. So now we have the old Angular 1 and Angular 2 and Angular 4 and in the near future Angular 5. But the changes between Angular 2 and Angular 4 aren’t as the changes between Angular 1 and Angular 2. Angular 4 is not a rewrite. It’s a new version of Angular 2 with bug fixes and new features. Actually, in the semantic versioning approach, every six months we will get a new version of Angular. So, with all the new versions, there is a confusion with the terminology. It’s not convenient to search for learning materials in Google, questions in Stack Overflow or just talk with your partners. In order to solve this frustrating issue, the Angular team decided about the names terminology. The old Angular, Angular 1, is called AngularJS and the new Angular 2/2+/4/5 is called Angular, just Angular.
Another terminology change is the 3rd party libraries names. Sometimes, a library is called by the ng-
prefix. When Angular version 2 was released, libraries started to use the prefix ng2-
. When Angular version 4 was released, a new terminology trend appeared. The terminology is to use the ngx-
prefix, because Angular use the semantic versioning and every six months a new version will be released. So, for example, the name of ng2-bootstrap
, changed to ngx-bootstrap
and there is no reason to change it in the future.
AngularJS has the watch feature to notify on value change. Angular dropped the watch and scope, and now we have the component inputs as properties. Besides, Angular gives us the ngOnChanges
lifecycle hook. In order to improve the run time of the updates, the **OnChanges**
event doesn’t emit when a deep field of the input property changed. The value of the input, in this case, is the reference to the object.
The log entries appear as the string value of the power property changes. But the ngOnChanges
does not catch changes to hero.name
Many developers don’t know this and fall into this trap. To solve this problem, there are various solutions:
ngDoCheck
The ngDoCheck
lifecycle hook is the common way to solving this issue. This hook is called when the change detection process runs. You need to be careful on using this hook because the changed detection usually runs a lot of time per minute.
When you subscribe to an observable or event in JavaScript, you need to unsubscribe to release memory in the system when you finish to use it. Otherwise, you will have a memory leak.
Example for unsubscribing in a component’s ngOnDestroy hook
Mostly, when you subscribe in a component, the better place to unsubscribe is in the OnDestroy
lifecycle hook. When you subscribe in a service, there is no lifecycle hook, so you need to initiate it by yourself.
The lack of unsubscriptions is because the developers don’t know that they need to do so or because they forget to unsubscribing. In order to help us to solve this problem, libraries and methods were developed.
Besides the mistakes of zombie subscriptions, there are cases that the unsubscribe operation is automatically done by Angular. An example of such cases is when you use the async pipe.
Over unsubscription operation example
Another examples can be finit subscriptions like Observable.timer(1000).subscribe(…)
and http.get(‘http://medium.com’).subscribe(…)
.
There are Rxjs’ methods that unsubscribe by their-self. For example, take(n), takeWhile(predicate), first() and first(predicate) are some of them. So, you need to be careful with the mission of unsubscription and knowing those methods is viable to make this mission correctly.
As part of the new mechanism of Angular, the Hierarchical Dependency Injection, we can instantiate a service more than one time, unlike in AngularJS. In the old AngularJS, the services are singletons.
Suppose we have a heroes service to fetch our lovely heroes:
Hero service
As we can see, the service fetches the data in the constructor. Also, there is a method getHeroes
to get the heroes. Everything is fine.
Now the hero component:
Wrong use of provider
The HeroComponent
declare the HeroesService
provider in the @Component.providers
array and inject it in the constructor. The problem withthis code is that every **HeroComponent**
instance will instantiate a new instance of the **HeroesService**
. So, the service will fetch the data by HTTP request multiple times because of the Hierarchical DI.
The solution to this problem is to declare the service in the @NgModule.providers
:
Correct use of provider
Now, the provider will be instantiated only one time for all the HeroComponent
instances. This will happen because, when a provider is declared in the **NgModule**
, it will be a singleton and all the other modules will be able to use it. There is no need to export a provider in the @NgModule.exports
array. It will be done automatically.
Angular is no more a framework for the web. Angular is a platform. One of its strengths is that it allows us to decouple the application code from the renderer, which in turn makes it possible to write applications that can be executed in the browser, on the server, or even as native apps.
The decoupling also gives us more abilities like using AOT (ahead of time compilation) or web workers. Ahead of time compilation means to compile the application templates in the build time of our server. AOT can be used instead of the standard JIT compilation that runs in the browser. When we use AoT there is no need to include the big @angular/compiler
package in our bundle, so the bundle size and the load time are lower. So it is crucial.
If we want to use this ability now or in the future, we need to keep some constraints. One of them is not to mutate the DOM directly using jQuery, document object or ElementRef.nativeElement
.
Mutate the dom directly — the bad way
As you can see, the doBadThings
method has three lines of code, which demonstrate three ways to mutate the DOM directly. The first line is by jQuery. The second is by the ElementRef.nativeElement
, and the third is by the global document
object.
The Angular way to mutate the DOM is via the [Renderer2](https://angular.io/api/core/Renderer2)
service (in v4, Renderer in v2).
Mutate the dom via Renderer — the recommended way
In this way, we call the setElementProperty
of the renderer with three arguments. This function goal is to change/add a property to an element. The three arguments are the elementRef
instance, the property name, and the property value. The Renderer
is a wrapper to the view mutation layer. When we are in the browser, the default renderer will be used. When the application runs on another platform, like a phone, the renderer will be replaced with another suitable renderer. This renderer needs to implement the renderer class interface and to beinjected as the default Renderer class by the DI mechanism.
“Don’t ever never” touch the DOM directly
A Component is the common building block in an Angular application. Every component needs to be declared in a NgModule
in order to be available for the views. To specify that a component is a member of a NgModule
, you should list it in the @NgModule.declarations
array.
There is no possibility to declare a component in more than one **NgModule**
. If a component is declared in multiple NgModule
-s, the Angular compiler will throw an error
. For example:
As you can see, the component is declared in the HeroesModule and also in the AnotherModule. The need for the same component in more than one module is ok. When that happens, we should think what is the relationship between the modules. If one module is a child of the other module, the solution will be:
NgModule.declaration
NgModule.exports
arrayNgModule.imports
array
Parent-child modules relation
If this is not the case, we need to declare another NgModule
that will be a module of shared stuff. The solution will be:
Shared module solution
Making a mistake it’s ok. All of us do that. Even when we write a bunch of code and read it later, sometimes we don’t understand why the code was written like that.
Turning a blind eye is the fatal error — learn from your mistakes
The big mistake is to see the mistake and turning a blind eye. As developers, we always need to improve our skills. Making a mistake is one of the best ways to become a stronger and a better developer. We need to write the mistakes as a side note, maybe in a checklist, and we should ensure that in the next time, the code will be written properly.
You can follow me on dormoshe.io or Twitter to read more about Angular, JavaScript and web development.