If there is an AngularJs app running in the page, it is often hard to
debug or see the current model (stored in the $scope
variables).
There are browser extensions ng-inspector
and Batarang,
but what if you just want to see values, maybe change something
quickly? Here is how to do this from the browser's JavaScript console.
I made the following example that you can try this on. You can enter the commands in the middle column (that acts like browser console). We have the following HTML and JavaScript on the page
1 |
|
We have variable name
attached to the scope in the div app
. Let us change the variable
to something else, like Mary and see the change on the page.
Grab the element and scope
We first start by getting the reference to the scope
.
1 | var el = document.getElementById('app'); |
If we include full jQuery library before including angular, we can get the wrapped element
in a single request var ngEl = angular.element('#app');
Even without full jQuery library, inside Chrome DevTools we can select an element using $(<selector>)
syntax.
For example to grab the isolate scope from a directive 'MyWidget' that creates <my-widget>
node
1 | angular.element($('my-widget')).isolateScope(); |
Change the model / DOM
Let us change the name to a new value
1 | scope.name = 'Mary'; |
Notice, that the page has not changed yet. We have only changed the model.
The Angular engine has no idea that it needs to update any HTML nodes bound to the model.
To update them, we can execute $apply
function that acts on a scope
1 | scope.$apply(); |
If our update is simple (as above), we can directly change the model and call apply function
1 | scope.$apply('name = "Mary";'); |
We can also call any functions attached to the scope using the expression
1 | // app code |
We can also do many steps if we pass a function to the $apply
function, which will be
executed in the scope's context BUT we need to inject the scope we want to use.
1 | scope.$apply(function ($scope) { |
Inner elements
You do not even need to know the exact id of the element to get the outer scope. For example if we have an HTML element inside the element with angular controller, we can still get the correct scope
1 | <div id="app" ng-controller="MyApp"> |
From the console use id inner
1 | var el = document.getElementById('inner'); // inner is inside app |
Broadcast an event
If you want to send an event to all listening scopes, you can quickly do this using $rootScope
1 | // somewhere in the application |
If the angular application wraps body
element instead of the document, you can still use it easily
<body ng-app="...">
angular.element(document.body)
...
Get the root application node
If you do not know the node where the application has been boostrapped, you can find the element after bootstrapping using the attribute selector
var app = angular.element(document.querySelector('[ng-app]'))
Chrome DevTools shortcut
Sometimes getting the exact element selector is difficult. Instead you can use the following method
- Open the Chrome DevTools elements panel
- Select the looking glass tool
- Pick the element on the page
The selected element will be highlighted in the DOM
The Chrome DevTools has the shortcut $0
to refer the currently selected element. You can use this
shortcut to quickly get the Angular scope.
1 | angular.element($0).scope(); |
Quick, isn't it?
Inject a service
You can get to any service / constant / etc. from the console using an injector instance. Just grab the root element and ask for its injector instance.
1 | var el = document.getElementById('app'); |
Inject something
Usually the document.body
has angular application around it, so if you need to grab something
angular.element(document.body).injector().get('some name');
Cause an exception
Sometimes I want to test if the application's exception handler really catches errors. To cause the exception inside the app we can execute this in the browser console
angular.element(document.body).injector().get('$rootScope').$apply(
function bad() { 'use strict'; bad = 'bad'; }
);
The 'use strict'
is necessary to make sure bad
variable is a true error, and does not create
new property on the window
object.
Stop inside a called function
Imagine we have a button with a click handler <button ng-click="doSomething()">Do it</button>
. We
do not know where the function doSomething
is defined, but we can still easily pause the
execution inside of it.
Select the button in the "Elements" tab. This makes the button element available under $0
variable.
Tell the Chrome browser to stop once the function doSomething
starts
debug(angular.element($0).scope().doSomething)
That's it. If you want to prevent the stopping (remove the breakpoint), use undebug
function.
undebug(angular.element($0).scope().doSomething)
Pretty cool.