<!DOCTYPE html>
<html lang="en">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.5.0.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="scripts/errorDemoApp.module.js"></script>
<script src="scripts/services/demo.service.js"></script>
<script src="scripts/services/exception-handler.service.js"></script>
<script src="scripts/services/custom-exception-handler.service.js"></script>
<script src="scripts/components/retriever/retriever.component.js"></script>
<script src="scripts/components/popup/popup.component.js"></script>
</head>
<body>
<div ng-app="errorDemoApp">
<retriever></retriever>
</div>
</body>
</html>
<div>
<label>Get Data</label>
<br/>
<button ng-click="ctrl.getUsers()">Get users</button>
<br>
<button ng-click="ctrl.getToDos()">Get todos</button>
<br />
<button ng-click="ctrl.getComments()">Get comments</button>
<br />
<hr />
<br />
<pre>{{ctrl.result}}</pre>
</div>
errorDemoApp.component('retriever', {
templateUrl: 'scripts/components/retriever/retriever.component.html',
controller: ('retrieverController', retrieverController),
controllerAs: 'ctrl',
});
function retrieverController(demoService, customExceptionHandler) {
let self = this;
self.result = '';
self.getUsers = getUsers;
self.getToDos = getToDos;
self.getComments = getComments;
//exceptions will be caught in the component for two scenarios
//1. when we want to show a specific error message to a user
//2. when we want to log some specific/targetted data about the exception
function getUsers() {
//scenario 1
//we need to show a specific error message to the user in this function
demoService.getUsers()
.then((response) => {
self.result = response;
})
.catch((error) => {
customExceptionHandler.showErrorMessage("Failed to retreive users");
//now this error will be handled by $exceptionHandler override
throw error;
});
}
function getToDos() {
//scenario 2
//we will handle the error in this method it self and add some targeted information
demoService.getToDos()
.then((response) => {
self.result = response;
})
.catch((error) => {
//we log the error by calling the api that persists data (e.g. kibana)
console.error(`an error happned in dataRetrieverController.getUsers: ${JSON.stringify(error)}`);
});
}
function getComments() {
//we dont need to always handle exceptions in file
//if an error gets thrown here it will be handled by $exceptionHandler override
demoService.getComments()
.then((response) => {
self.result = response;
});
}
}
<div class="modal-header">
<h3 class="modal-title">An error occured</h3>
</div>
<div class="modal-body">
{{ctrl.errorMessage}}
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ctrl.close()">Ok</button>
</div>
errorDemoApp.component('popup',{
templateUrl:'scripts/components/popup/popup.component.html',
controller:('popupController', popupController),
controllerAs:'ctrl',
bindings:{
modalInstance: '<',
resolve:'<',
}
});
function popupController(){
let self=this;
self.errorMessage='';
self.$onInit=$onInit;
self.close=close;
function $onInit(){
self.errorMessage=self.resolve.message;
}
function close(){
self.modalInstance.close(self.editedName);
}
}
errorDemoApp.service('demoService',demoService);
//by not handling exceptions here, we can avoid cluttering the http layer
function demoService($http){
let service={
getUsers:getUsers,
getToDos: getToDos,
getComments: getComments
}
return service;
function getUsers(){
return $http.get("https://jsonplaceholder.typicode.com/users")
.then(function (response) {
// return response.data;
return null.data;
});
}
function getToDos() {
return $http.get("https://jsonplaceholder.typicode.com/todos")
.then(function (response) {
// return response.data;
return null.data;
});
}
function getComments() {
return $http.get("https://jsonplaceholder.typicode.com/comments")
.then(function (response) {
// return response.data;
return null.data;
});
}
}
errorDemoApp.service('customExceptionHandler', customExceptionHandler);
function customExceptionHandler($uibModal) {
let service = {
showErrorMessage: showErrorMessage,
};
return service;
function showErrorMessage(errorMessage){
let popupSettings = {
backdrop : 'static',
keyboard : true,
component: 'popup',
resolve :{
message: function(){
return errorMessage;
}
}
}
let modalInstance=$uibModal.open(popupSettings);
return modalInstance;
}
}
//we are overriding the default angularJS $exceptionHandler here
errorDemoApp.factory('$exceptionHandler', exceptionHandler);
function exceptionHandler() {
return function (exception, cause) {
//we log the error by calling the api that persists data (e.g. kibana)
console.error('following error occured:');
console.error(`exception: ${exception}`);
console.error(`cause: ${cause}`);
};
}
//according to exceptionHandler documentaiton
//code executed in event-listeners (e.g. event handlers and async functions (like setTimeout(), setInterval(), callbacks) does not delegate exceptions to $exceptionHandler
//To really catch these errors it is necessary to add a global error handler in the browser, by setting the onerror property of the window object
//https://www.bennadel.com/blog/2855-piping-global-errors-into-the-exceptionhandler-service-in-angularjs.htm
errorDemoApp.run(function addGlobalErrorHandler($window, $exceptionHandler) {
$window.onerror = function handleGlobalError(message, fileName, lineNumber, columnNumber, error) {
// If this browser does not pass-in the original error object, let's
// create a new error object based on what we know.
if (!error) {
error = new Error(message);
// NOTE: These values are not standard, according to MDN.
error.fileName = fileName;
error.lineNumber = lineNumber;
error.columnNumber = columnNumber || 0;
}
// Pass the error off to our core error handler.
$exceptionHandler(error);
};
});
var errorDemoApp = angular.module("errorDemoApp", ['ngAnimate', 'ui.bootstrap']);