We’ve all been there – you’re working on an Angular component and things aren’t quite working out as you expect. You need to quickly see what’s happening with your inputs, or maybe your component is being rendered conditionally and you need to understand at what point it’s being initialised and destroyed on the page.

Most of the time you end up doing something like this:

public ngOnDestroy(): void {
  console.log('SheepStacker component destroyed');
}

public ngOnChanges(changes: SimpleChanges): void {
  for (const prop in changes) {
    console.log(prop, changes.prop.currentValue);
  }
}

However there are problems with this code, and it’s a pain to write out each time.

ngOnChanges can be particularly tricky if the changed property is an object and you are not using immutable objects to help with the onPush change detection strategy. Due to the way JavaScript passes objects using a copy-of-reference technique, debugging object outputs to the console can lead to the time-sapping situation where the browser console will expand the object to show its current properties when you click to view it, not the properties at the time it was logged. As ngOnChanges is often called multiple times, this can be a real headache.

To avoid this, you might now change the method to be something like this:

public ngOnChanges(changes: SimpleChanges): void {
  for (const prop in changes) {
    console.log(prop, JSON.stringify(changes.prop.currentValue));
  }
}

It’s a lot of boilerplate to have to write each time, and this still doesn’t consider other issues such as viewing the previous value, dealing with large or non-stringifiable objects… What if you also need to check what’s happening in ngAfterViewInit and ngOnInit? There must be an easier way?! Fortunately there is!

Using TypeScript Decorators

Decorators are a way of introducing additional functionality to a class, method or property through simple annotations – if you develop in Angular you will already be familiar with them through the use of @Input, @Component, @ViewChild and many others. Class decorators have access to the constructor function at the time the class is being initialised, making this an ideal time to add to or modify the functions on the constructor prototype.

Using a TypeScript decorator allows us to add lifecycle logging functionality in a clean and unobtrusive manner. It can be added and removed quickly and easily, avoiding the need to proliferate your code with console.logs everywhere.

I have used this capability to create a decorator called DebugLifeCycle that will take care of most of the boilerplate code you would normally write to debug Angular lifecycle events. In its simplest configuration it will log events to the console for the following lifecycle hooks: ngOnInit, ngOnDestroy, ngOnChanges and ngAfterViewInit. The output for ngOnChanges takes care of logging objects by using JSON.stringify to present them in a static and readable manner. The configuration also allows for the use of custom functions, perhaps if you want to output a different format or send the logs to an output other than the console.

Installing and using ng-debug-lifecycle

The readme for the package has comprehensive instructions for how to install and use the decorator, as well as details of all the configuration options. The simplest way to get started is to simply install the package with

npm install --save-dev ng-lifecycle-debug

and then simply add

@DebugLifeCycle()

above your class definition (the same as how you would use @Component).

Future Work

There’s still lots of improvements that can be made, and I’ve started to list a few of these over on the GitHub repo. If you have any ideas, feel free to raise an issue or submit a PR.