Search Lessons, Code Snippets, and Videos
search by algolia
X
#native_cta# #native_desc# Sponsored by #native_company#

Seven Methods for Debugging Angular Applications

Episode 36 written by Jeff Delaney
full courses and content on fireship.io

Debugging an Angular application can be a major source of frustration, especially when you’re just getting started with the framework. In this lesson, I am going to cover the my top seven methods for Angular debugging, as well a few pointers for Firebase and RxJS specific debugging.

1. Prevent Bugs with TypeScript

The best strategy for debugging is to not introduce bugs in the first place.

TypeScript is your best friend when working with Angular - both for debugging and developer tooling. I prefer to write code with the open-source Atom text editor and its TypeScript plugin. You don’t need to pay for some fancy IDE to take advantage of TypeScript’s enterprise-grade tooling.

When I say take advantage of TypeScript, I am referring mostly to the use of its static typing features. When you do this, you will get instant feedback from your text editor about your code. This will allow you to prevent bugs before they are introduced. Consider the following examples:

Bad: Opting Out of TypeScript


let animal;

createAnimal(name, size) {
return {
species: name,
size: size
}
}

animal = createAnimal('Canine', 500)

In this almost plain JavaScript example, we will not get great feedback from our text editor when something is looking buggy. For example, we could define the animal variable as a string by accident, even though it should always be an animal object

Good: Using TypeScript static typing

export class Animal {
constructor(public name: string, public size: string) {}
}

let animal: Animal;

createAnimal(name: string, size: number): Animal {
return new Animal(name, size)
}

animal = createAnimal('Canine', 500)

the excellent tooling of typescript in Atom text editor

In this example, we define an Animal class and force variables, arguments, and functions to adhere to the proper data structure. If we tried to define the animal variable as a string, our text editor would tell us exactly why we can’t do this.

2. Using Debugger Statements to Stop JavaScript Execution

It is often helpful to stop the execution of your app at various breakpoints. A debugger statement does just that. In this example, we have an items-list component from the FireStarter Demo app. We can throw in a debugger that will pause our Angular app it it reaches this point. It also provides the code of component (in TypeScript) in chrome dev tools.

In this example, we create two breakpoints that will pause our app. The first one will stop at the constructor and second will stop when the subscription to the animals collection is started.

showSpinner = true;

constructor(private db: AngularFireDatabase) { debugger }

ngOnInit() {
this.db.list('/animals')
.subscribe(() => {
debugger
this.showSpinner = false
})
}

Now we can get a play-by-play state of execution of the app for debugging.

using a debugger statement in Angular

3. Inspect Data with the JSON pipe

Sometimes you might retrieve data from an API and know very little about the structure of its response. In these cases, it helps to inspect the data in HTML with the | json pipe. This takes a JavaScript object and converts its it JSON. It can also be used with Firebase observables with the | async pipe, as seen in the example below.

this.animals = this.db.list('/animals')
<div *ngFor="let animal of animals | async">
{{ animal | json }}
</div>

4. Console Debugging

Angular has a built in debugging tool that allows us to (1) probe an element to get a a debugging context for a component. And (2) run a change detection profile on the app. It works by running a change detection loop and recording the total amount of time required to run single change detection operation in the UI.

probe

To probe a component, open developer tools and highlight the component you want to inspect. Then run the following commend in the Chrome console.

ng.probe($0)

profiler

First, You will need to enable debugging tools when the app is bootstrapped. In the main.ts file, add the following lines:

import { ApplicationRef } from "@angular/core";
import { enableDebugTools } from "@angular/platform-browser";

platformBrowserDynamic().bootstrapModule(AppModule).then((module) => {
let applicationRef = module.injector.get(ApplicationRef);
let appComponent = applicationRef.components[0];
enableDebugTools(appComponent);
});

Now you can run the following command from the chrome console.

ng.profiler.timeChangeDetection({record: true})

change detection profile in angular4

5. Augury Chrome Plugin

Angular apps are structured in a way that can visualized as a hierarchy - that’s exactly what the Augury Chrome plugin achieves. I find it especially useful for debugging issues related to NgModules and routing. The plugin is maintained by the good people over at Rangle.io and it’s a must have for any Angular developer.

debugging angular 4 with augury on chrome

6. Angular Logger

As your codebase grows more complex, adding your own custom logger can be a good way to build a custom error . The Angular2-logger allows you to console log messages at different priority levels.

constructor(private logger: Logger) {
}

ngOnInit() {
this.logger.error('This is a priority level 1 error message...');
this.logger.warn('This is a priority level 2 warning message...');
}

7. Debugging RxJS Observables

I saved the best for last becuase debugging RxJS observables can be tough. You can console log them, but that’s not going to get you very far. The most straightforward way to debug RxJS code is to chain the do operator to the observable flow. The do operator allows you to run arbitrary code at any point to to inspect the results, without affecting the underlying observable. Let’s go ahead debug a Firebase object observable.

this.animal = this.db.object('/animals/hippopotamus')

this.animal
.do(val => console.log('before map', val))
.map(val => val.name)
.do(val => console.log('after map', val))

this.animal.subscribe(val => console.log(val))
// before map, Object
// after map, string