A Guide To Debugging Angular Applications
Debugging is key to building any application. One cannot build an application bug-free on the first go. Although there are a few things we could do to avoid creating bugs from a developers point of view, like defensive programming or Test Driven Development, in any non-trivial application, bugs are unavoidable. Knowing how to debug an application is a key skill for developers, and speeds up the process of eliminating bugs and delivering software.
If you are using RxJs operators, you might find that debugging the operator chain can be a bit tricky and having a .tap() between the operators makes it easier to inspect the chain. It gives us the ability to watch the chain without actually modifying it.
tap does not transform the values here but is used to perform some side effects.
This is a quirky command that we can use on the console to see what the component’s state is. Simply select the component you want to inspect from the Elements tab and execute ng.probe($0).componentInstance on the console. $0 is a global variable the Chrome DevTools make available, whose value is the most recently selected element. You can use this for $1, $2, $3 & $4 to inspect the previous four DOM elements.
You can also inspect various other information like the parent of the selected component, list of listeners by just executing ng.probe($0) instead.
This will only give us the necessary information when the debug mode is enabled and will not work on Production where debug mode is usually disabled.
Angular has a built-in profiler which can be used to profile your Angular application. At the moment, it offers only one kind of profiling — timeChangeDetection. timeChangeDetection performs change detection on the application and prints how long a round of change detection takes for the current state of UI. The recommended value for this is to be less than 3 milliseconds.
You can also record this profiling for further analysis by passing in an argument.
Augury is a Chrome extension that provides a visual representation of the Angular components on a page, their dependencies, router information, whether change detection is used for the component, etc.
Augury makes it is easy to inspect the state of a component from its Component Tree tab under Properties tab. If your component has an Output() event emitter, you can also emit an event from the Properties tab.
Using View Source link, you can navigate to the source code for the component.
Injector Graph tab under Component Tree tab shows the visual hierarchy of the selected component and the dependency injections for it.
Router Tree tab shows all the defined routes that are currently loaded in the application. If a component is lazy loaded, it is indicated by [Lazy] next to the component. This information can be used to detect which routes are not lazy loaded and decide whether or not to lazy load them to improve the performance of the application.
For more information on how to use Augury, please check the docs.
If you are using Redux to manage your application state, there is a Chrome extension called Redux DevTools which lets us see all the actions dispatched, state of the application after each action and the difference in states.
It also gives us the ability to rewind the actions and see the corresponding changes on the page.
Redux DevTools needs a bit of setup to be used in Dev environments but once setup, it is well worth the effort. Check out this blog to find out about how to use it in Production. It is recommended that the full-featured version be used for development and the restricted one for production.
json and async pipes
If you have a JSON object that you would like to print the value on the page for debugging purposes, you can use the json pipe with the JSON object.
If you have the JSON object as an observable, you can chain the json pipe along with the async pipe — | async | json.
More information on pipes is available here.
Chrome dev tools shows the current Call Stack, which gives us information about the stack of function calls indicating what triggered the execution of the function that is being executed. Quite a few of these would be from Angular libraries but you can identify the ones that relate to your code. This will also show the HTML part of the application that triggered the function call. You can also right-click a particular frame and restart it and it will restart the execution from that frame.
There are issues which could be caused due to slow networks, especially if there are async operations happening in the application. Sometimes our end users are on slow networks, and if development and testing is done on faster networks some bugs are hard to find. This could be annoying especially when you do not know that slow network is the cause of it. In these cases, we could add a custom speed for the network with low kb/s and try to reproduce them. The same technique can be used to throttle your CPU, simulating the experience your members will have when using slow / older phones.
You can also use breakpoints on Chrome Dev Tools instead of debugger statement and avoid waiting for the application to compile and refresh for the debugger statement to apply. It is also easy to enable and disable the breakpoints individually.
You can add a conditional breakpoint by right clicking on the line number in the source file. This will only get triggered when the specified condition is satisfied.
You could add a conditional breakpoint with a consoe.log() statement. Since the console.log() statement returns undefined, it will never actually stop the debugger at that point, but will still give you console output for debugging.
If you want to find a source file in the Sources tab in Chrome dev tools, you can hit Cmd (Ctrl) + P and type in the file name.
If you are using local storage to store the state of your application, you could use the Application tab to inspect the state of local storage.
You can clear the data in local storage for your application by right-clicking on the app under Local storage and choosing to clear.
You can set, get or remove an item from local storage by doing localStorage.setItem(‘<key>’), localStorage.getItem(‘<key>’) & localStorage.removeItem(‘<key>’) respectively on the console window.
You can also edit or delete the values in local storage by double-clicking a key or value.
Like debugging any other application, console objects can be used in Angular applications too. There are different ways of using console with different levels of information.
This come in handy when you are debugging a bug that only happens in test environments.
Pro tip: console.warn() and console.error() will also print the call stack long with the warning/error.
debugger; statement, like console objects, can be used in Angular applications as well. Having a debugger statement when the chrome dev tools is enabled, causes the application to break at a point where you placed the debugger statement.