No matter how well-designed your AngularJS application is, when components start interacting with the real world–and each other–unforeseen issues can arise. Being able to dive into the app's components, inspecting individual slices of behavior and state, is critical to efficiently resolving those issues. This article walks through some techniques we use at Made By Munsters to debug Angular apps with the help of our favorite browser's dev consoles.


Controllers

You can access controllers through the dev console by using the angular.element(element).controller(name) function. This function looks for a controller associated with the target directive name; if no name is provided then it defaults to ngController. The search begins at the specified element and walks up the element's ancestors until it's found a matching controller or hit the root of the document.

Because .controller() defaults to ngController, if you want to locate a directive's controller, you'll need to specify that directive's registered name–either it's camel case name ('myCustomDirective') or the snake case name (my-custom-directive) will work. Note that this is not the name of the directive's controller, if you've specified one through the directive's controllerAs attribute.

There's a shortcut for accessing the element when using Chrome: select the element (either via right-clicking and Inspect Element on from the browser, or selecting the element from the Elements tab of the Dev Tools) will cause $0 to point to the element when referenced in the Dev Tools console. You can then use a statement like this to retrieve the controller:

// "Regular" controller
angular.element($0).controller();

// Directive controller
angular.element($0).controller('myCustomDirective');

Note that this search walks up the DOM tree ancestry, so you'll need to click on the controller or directive element itself, or one of its descendants.


Scopes

While we've moved away from writing controllers and directives that place their properties directly onto the scope, we still occasionally need to access the scope when debugging. Angular provides access to controller and directive scopes through a pair of methods on the decorated elements it returns from angular.element:

  • scope() - Retrieves the scope of the element. If the element doesn't have it's own scope, angular will walk up the DOM tree to find the nearest ancestor element with a scope.
  • isolateScope() - Retrieves the isolate scope of the element. Because the scope is isolated, this only works on the element declaring the isolate scope. If you're looking at elements in the DOM tree of your browser tools, you can identify these isolate scope elements by looking for the class ng-isolate-scope.

The element can be either a normal DOM element, or a jQuery element. Both of these options provide the same results:

// Normal DOM element, accessed via document.querySelector
angular.element(document.querySelector('your-directive-name')).scope();

// jQuery element, accessed... via jQuery
angular.element($('your-directive-name')).scope();

Again, often the easiest way of finding the element in Chrome is using the Inspect Element trick. Once you've started inspection of the targeted element, you can then get to your target scope with angular.element($0).scope().

Note that after making any data changes, you may need to call $scope.$apply() or $scope.$digest() in order to view the changes. For example:

angular.element($0).scope().message = 'Hello World';
angular.element($0).scope().$digest();


Services

You can retrieve any service through the dev console by using this construct:

angular.element(document).injector().get('service.name');

As we move more and more logic into services, this technique is quickly becoming my favorite way to debug. Because it works with the document reference and retrieves globally-accessible services, it can be quickly loaded from the browser console without having to identify a particular element context. Note that because this can access any service, it works equally well for custom services as it does for stock Angular services like $http.


Wrapping Up

The best debugging solution is to find bugs early, during unit testing of individual components. Test-driven development, or at least a handful of tests to cover common scenarios, is the preferred method of building Angular components. Trying to fix errors in the context of a live app will always be more difficult. Nonetheless, integration issues can be difficult to anticipate and there are always going to be new errors uncovered when large numbers of components begin interacting. In those cases, these console debugging techniques are essential for inspecting the app and quickly identifying the source of the issue. We hope these serve you as well as they've served us.

More posts
  • Paperclip Supporting Binary Images and SVG

    Made By Munsters projects typically rely on Rails backends, and that means that we often enlist Paperclip to help us handle file uploads. Typically our image upload support handles binary image files. If we want to also support SVG images, we need to modify how Paperclip handles the image attachment.

    Read More
  • Recognized as One of Indianapolis' Top Design Firms

    Whan an unexpected surprise, but one we don't take lightly. At Made by Munsters, we've worked extremely hard in building quality products with our partners and scaling our world-class team of designers and developers with the best talent.

    Read More
  • A Few Tips To Help Manage Git Branches

    Managing git branches when branching off multiple feature branches can be a pain. What branches have been merged and can any be deleted?

    Read More