5 tips to boost your Angular skills
By: Alexander Inkin
Tags: Declarative Programming, Angular, Dependency Injection, Rxjs, Typescript

1. Know how change detection works in Angular

There are many great articles going deep into change detection mechanism. Like this one. So let’s just recap the basics quickly and get to the tips.

Recap

Angular has two change detection modes: Default and OnPush . First one triggers change detection for every tick happened anywhere in the app. Second only marks view for checking if an event has happened in this view or if input data has changed.

Default vs OnPush

There’s really no reason for you to use Default . All you need to do is write your code the way framework expects it to and you won’t get into trouble with OnPush . This means, you should never mutate your data. If your input arrays or objects change immutably, OnPush would pick this up and refresh the view.

When you subscribe to events with @HostListener Angular has got you covered as well. But what if you are working with RxJS streams? You can always inject ChangeDetectorRef and do markForCheck() when you need it. But a declarative option would be to end up with async pipe in your template. It would trigger change detection on each emit.

You might have seen this pattern:

<div *ngIf="stream$ | async as result">

...

</div>

But what do you do when you need falsy results too? You can strip condition logic from ngIf and create a simple structural directive. It would be used only to add context to the view:

NgZone

If you cannot fully switch to OnPush there are still things you could optimize. You can inject NgZone and do performance sensitive actions in .runOutsideAngular() . This way there will be no extra ticks in the change detection mechanism. Even components with Default strategy will not perform change detection cycle. You should do this for events that trigger rapidly like mousemove , scroll . To do it declaratively with RxJS streams you can create two operators. One for leaving the zone and another to return to it if you need to trigger change detection:

Another option you can have with @HostListener decorator is custom event manager plugin. We released an open-source library called ng-event-plugins. It allows you to filter out unnecessary change detection cycles. Read more about it in this article.

2. Learn RxJS. Seriously!

RxJS is a very powerful tool. We all use it to some extent when working with Angular but really mastering it can help you a lot. From very simple streams that allow you to reload your component…

…to complex operators combination. Like this example recognizing various types of scroll:

Just take a look how easy it is to create a sticky header that disappears when you scroll down. A little bit of CSS and pretty basic RxJS:

I cannot stress enough how important RxJS knowledge is to an Angular developer long term. While it’s simple on the first look, RxJS requires a little bit of paradigm shift. You need to start thinking in terms of streams. But once you do, your code would become more declarative and easier to maintain.

There is not much I can suggest here other than practice. If you see a case that you can solve with RxJS — try to do so. Avoid side-effects and nested subscribes. Keep your streams organized and watch out for memory leaks (read further).

3. Max out TypeScript

We all use TypeScript in our Angular applications. But to get the most of it we need to push it to the limits. I rarely see projects with enabled strict: true . This is something you totally should do. It will save you from lots of cannot read property of null s and undefined is not a function s.

Generics

In TypeScript we can use generics when the type we work with is unclear. Combination of generics, overloads and type narrowing can make a pretty solid API. You almost never will have to typecast. Take a look at this example of strongly typed RxJS method fromEvent :

With it you can be sure that target in the event has the same type as the element you listen the event on. And event has properly narrowed type.

Generics based APIs have the benefit of being data-model agnostic. This means people can use it without being forced to a particular interface

There are articles dealing with things like type inference, advanced types, unions. I suggest you educate yourself in the subject, it would help you make robust code in the long run. My one last advice here is never use any . You can almost always replace it with generic or unknown which is a safer version of any .

Decorators

Don’t forget about other TypeScript features, such as decorators. When used well, they can really improve your code. There are cases when type is correct, but logically value is unacceptable. Like when you have a number input for quantity — you cannot really have negative or decimal values. But according to TypeScript they are correct. You can protect your components for such invalid inputs with assertion decorator:

Did you know that if you decorate your abstract classes you do not have to pass arguments to super() ? Angular will handle it for you:

Another good case for custom decorator is some reusable processing logic. For example, in our Web Audio API library for Angular we turn declarative input bindings to imperative native commands with a strongly typed decorator:

You can read about this library in detail here

4. Angular’s DI is king! Use it. Then use it more!

Dependency Injection is part of the reason Angular is such a powerful framework. Arguably it is THE reason. Too often it is not used to its full potential.

Head over to this dedicated article about DI we wrote to deepen you knowledge about it

RxJS

When it comes to practical advises, I mentioned before to watch out for memory leaks in your RxJS streams. Mainly this means: if you manually subscribe to a stream you need to unsubscribe yourself. An idiomatic solution in Angular would be to encapsulate this logic into a service:

You can also create shared streams and add them to DI tree. There’s no reason to have separate requestAnimationFrame based Observables across the app. Create a token for it and reuse. You can also add zone operators discussed above to it:

Tokens

DI is also a good way to make your components more abstract. If you do not rely on global objects, such as window or navigator — you are ready for Angular Universal which renders you app on the server side. As you know, node.js does not have DOM and many of the global objects we are used to. It is also much easier to test such code because dependencies are effortlessly mocked. It is not difficult at all to tokenize these objects. We can use factories when we define injection token and top level injector is available to us. Using built-in DOCUMENT it takes just a few lines to create WINDOW token:

To save time, use this open-source library where we already created some of those tokens. There’s also its Angular Universal counterpart with mocks. Feel free to request other tokens to be added!

Tokens and factories are very powerful tools. Combined with hierarchical nature of dependency injection they can help you make your app very modular. Read more about clever use of providers in this article.

5. Throw emperor down the abyss. Like Vader.

Angular with its template bindings and decorators clearly pushes us to write declarative code. I’ve mentioned this approach above. I will not get into discussion of benefits it has over imperative code here. Instead I’ll give you a solid advice: write declarative code. When you get used to it, you’d appreciate it.

Imperative approach is not how we do things ‘round here

Getters

What does it mean to write declarative code? First of all, try to use ngOnChanges as rare as you can. It’s a poorly typed side-effect which is really only needed if you must perform an action upon multiple inputs change. On a single one a setter is a more streamlined solution. And if instead of action you wanted to update some internal state, think if you can remove that manual state and replace it with calculated getter.

Performance and getters is a subject for a separate article which I hope to get to writing soon. For now, just take a rule of thumb to not create new arrays or objects in getters. If you need to calculate a new object, use memoization techniques like pipes, for example.

Here’s a good case for a setter which I’ve omitted for the brevity of the example (but was reminded of by an attentive follower).

Template reference variables

Instead of manually requesting elements for the view, Angular has @ViewChild decorator. Quite often though, you don’t really need it if you can enclose logic in the template:

<input #input>

<button (click)="onClick(input)">Focus</button>

Here we pass template reference variable directly to the method where we need it. This makes our component code clear. Think of it as sort of a closure in a template. But what do we do if we need a DOM element of an underlying component? We could have @ViewChild(MyComponent, {read: ElementRef} but we can do this without decorator if we create a directive with exportAs :

Dynamic content

People often use ComponentFactoryResolver to create dynamic components imperatively. Why, if there is ngComponentOutlet directive? Because this way you have access to the instance and can pass some data to it. A proper way to solve this issue is, once again, dependency injection. ngComponentOutlet allows you to pass custom Injector which you can create with data provided through token.

In fact, to create dynamic content you have interpolation, templates and components. And there is not much difference between these approaches. You have context and your have a representation: