How to handle uncaught exceptions in AngularJS?

Loading

Uncaught exceptions in AngularJS can cause unexpected application crashes, leading to a poor user experience. To prevent this, you need a global exception handling mechanism that captures errors, logs them, and ensures your application remains stable.


1. Default Behavior of Uncaught Exceptions

By default, if an exception occurs inside an AngularJS controller, service, or directive, AngularJS logs it to the console but does not handle it in a structured way.

Example: Unhandled Exception in a Controller

angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.triggerError = function() {
throw new Error("Something went wrong!");
};
});

If you call $scope.triggerError(), the browser console will show:

Error: Something went wrong!

This method does not allow centralized logging, remote tracking, or user notifications.


2. Using $exceptionHandler for Global Exception Handling

AngularJS provides the $exceptionHandler service to catch all uncaught exceptions globally.

Customizing $exceptionHandler

Modify $exceptionHandler inside a .config() block using $provide.decorator:

angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$exceptionHandler', function($delegate, $log) {
return function(exception, cause) {
// Log error to the console
$log.error("Global Exception:", exception);

// Send error to a remote server
logErrorToServer(exception, cause);

// Call the default exception handler
$delegate(exception, cause);
};
});
});

// Function to send error details to the server
function logErrorToServer(exception, cause) {
console.log("Logging error to server:", exception.message, "Cause:", cause);
}

How It Works

Logs the error using $log.error()
Sends error details to a remote server for tracking
Preserves AngularJS’s default behavior by calling $delegate(exception, cause)


3. Handling Uncaught Async Errors

AngularJS does not automatically catch errors inside asynchronous operations, such as $http requests, $timeout, or promises.

Example: Handling $http Errors

angular.module('myApp', [])
.controller('myController', function($scope, $http, $exceptionHandler) {
$scope.loadData = function() {
$http.get('invalid_url')
.then(function(response) {
$scope.data = response.data;
})
.catch(function(error) {
$exceptionHandler(error, "HTTP Request Error");
});
};
});

Without .catch(), errors won’t trigger $exceptionHandler, so always use .catch(error => $exceptionHandler(error, "Message")).


4. Using $q Exception Handling for Promises

When using promises ($q), you can catch unhandled exceptions globally.

Example: Handling $q Errors

angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$q', function($delegate) {
let originalReject = $delegate.reject;

$delegate.reject = function(reason) {
console.error("Unhandled Promise Rejection:", reason);
return originalReject(reason);
};

return $delegate;
});
});

Intercepts rejected promises
Logs promise-related errors


5. Handling Uncaught Errors with $window.onerror

If an error occurs outside AngularJS (e.g., inside a third-party library), $exceptionHandler will not catch it. You need to use $window.onerror.

Example: Capturing Global Errors

angular.module('myApp', [])
.run(function($window, $exceptionHandler) {
$window.onerror = function(message, source, lineno, colno, error) {
$exceptionHandler(error || message, "Global JavaScript Error");
};
});

Captures errors from non-Angular code
Logs errors caused by external scripts


6. Tracking Errors with User Details

For better debugging, log errors along with user session details.

Example: Logging Errors with User Info

angular.module('myApp', [])
.config(function($provide) {
$provide.decorator('$exceptionHandler', function($delegate, $log, UserService) {
return function(exception, cause) {
let user = UserService.getCurrentUser();
let errorDetails = {
message: exception.message,
stack: exception.stack,
cause: cause,
user: user
};
$log.error("User Error:", errorDetails);
logErrorToServer(errorDetails);
$delegate(exception, cause);
};
});
});

// Simulated UserService
angular.module('myApp').service('UserService', function() {
this.getCurrentUser = function() {
return { id: 101, name: "John Doe" };
};
});

Includes user context
Helps debug user-specific issues


7. Preventing Errors in Production

To disable detailed error logs in production, use $logProvider.debugEnabled(false).

Example: Disable Debugging Logs in Production

angular.module('myApp', [])
.config(function($logProvider) {
$logProvider.debugEnabled(false);
});

Prevents sensitive error details from being logged
Hides stack traces in production

Leave a Reply

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