How To Debug an Angular 1 Application via DevTools

The browser provides powerful tools for debugging web applications, including those built with Angular 1. I’ll list a few important concepts, but for an in-depth tutorial on debugging JavaScript in the console I highly recommend reading through the DevTools documentation on developers.google.com.

Commonly, you might set breakpoints to explore the code and check the value of variables mid-execution. You can set breakpoints for certain types of event handlers, on particular lines, or for the browser to pause execution for any exception. Once paused, you can inspect the value of variables visible from that line in the function’s scope, step over/into function calls. You can review stack traces and even auto-format poorly formatted or minified code.

While paused at a breakpoint you can execute any valid JavaScript expression from that context. DevTools also allows you to write and save commonly used snippets if you have long or complex debugging scripts that you find yourself copying and pasting regularly to the console.

Despite its power, there are times when the debugger is too low-level a tool for debugging an Angular application. From a breakpoint, you only have visibility into local, closed over, and global variables which are visible from the currently executing line of code. You can gain a wider view by traversing the call stack or stepping into functions, but it can take a lot of time and exploration to find information relevant to the bug you are fixing.

Additionally this gets even more difficult, when code is minified and source maps aren’t available, or when debugging someone else’s application whose code you’re not familiar with.

Thankfully, Angular exposes some helpful interfaces to inspect the state of the application. With the three tricks below, you can potentially save yourself hours of time debugging from the browser console.

List all of the providers and controllers of an Angular module

Recently, a friend asked me to help debug their Angular 1 application but I didn’t have immediate access to the source code and the deployed code was minified without source maps. Exploration via breakpoints was almost impossible, so to get quick snapshot of the application, I ran the command below which listed all of the providers registered on the main module.

// from http://stackoverflow.com/a/27533937
angular.module('MyApp')['_invokeQueue'].forEach(function(value){ 
    console.log(value[1] + ": " + value[2][0]);
});

For illustration, here is the same statement executed against the Angular 1 TodoMVC app to list its controllers and providers:

>  angular.module('todomvc')['_invokeQueue'].forEach(function(value){ 
     console.log(value[1] + ": " + value[2][0]);
   });

   register: TodoCtrl
   factory: todoStorage
   factory: api
   factory: localStorage
   directive: todoFocus
   directive: todoEscape
<- undefined

This snippet has come in handy many times. Have you ever seen the error Error: Unknown provider: myApiProvider <- myApi, and you could swear that yes, you do have a provider called myApi? Typos happen and they can be maddening to track down. Using the snippet above to list the registered providers has saved me hours diagnosing these kinds of mistakes.

Reveal the public and private methods of a service or factory

Let’s say we want to inspect the TodoMVC api factory from the console to validate that it is working as intended. One option would be to set a breakpoint on a line where a reference to api is in scope and then log it to console. Alternatively, we can get a reference to $injector from the console and ask it to give us a reference to api, or any other provider for that matter.

var service = angular.element(document.body).injector().get('serviceName')

Once we’ve captured a reference to a provider, we can inspect its public methods and properties:

>  var todoApiService = angular.element(document.body).injector().get('api')
<- undefined
>  todoApiService
<- Object
    api: Resource(value)
    clearCompleted: ()
    delete: (todo)
    get: ()
    insert: (todo)
    put: (todo)
    todos: Array[0]
    __proto__: Object

Now we can actually directly call the api service’s public methods to see if it’s working as intended. For example, we could call todoApiService.clearCompleted() and see how that action propagates through the application.

Get a reference to a controller to inspect its public methods and properties

Unlike providers, controllers are not registered on the application’s $injector instance. Providers are instantiated one time and then cached by the $injector . The cached instances are injected as needed into functions. Because service instances are shared across every function that injects that service they are ideal for storing shared state. This application design pattern is very common and known as the singleton pattern.

Controllers are not singletons. Angular instantiates a new controller every time a controller is bound a scope. You can also directly instantiate a controller and it’s very common to do so in unit tests. So, when debugging a page you don’t want just any controller, you want a particular instance of a controller which is bound to a particular scope in the DOM.

There are two (or more) ways to inspect a particular controller. The first is via a helpful hook provided by DevTools. If you type $0 into the browser console, it will return a reference to the DOM node currently selected DOM element in the “Elements” tab. To begin, select an element whose controller you want to inspect:

Then via the console execute this line:

// from http://stackoverflow.com/a/22799606
angular.element($0).scope()

Alternatively, you can use document.querySelector to target an element and use the same code snippet:

>  angular.element(document.querySelector('ng-view')).scope()
<- Scope {$$childTail: ChildScope, $$childHead: ChildScope, $$nextSibling: null, $$watchers: Array[13], $$listeners: Object…}

Once you’ve captured a reference to the controller for a particular element, you can inspect the scope and any properties attached to the controller itself and even explore up and down the scope tree to look for shadowed properties and explore child scopes.

Hopefully these tricks help you to explore and debug Angular 1.X applications more effectively. For advanced debugging and performance tuning, try out AngularJS Inspector, ng-stats and other libraries designed specifically for debugging Angular applications.


2 thoughts on “How To Debug an Angular 1 Application via DevTools”

Leave a Reply

Your email address will not be published. Required fields are marked *