In this article I will discuss a better programming practice for Angular.js than was demonstrated in the previous articles within this series. I have mentioned before, part of the purpose this blog is very much a “learning in progress” for me. Without going through the previous articles I would not have been able to get to this point and write “better code”. Hopefully with that understanding, those of you who have been along for the ride will appreciate this and grow with me 🙂
Services within Angular
Within Angular there are these programmatic entities called services – and if you read around Angular programming practices (Follow @ToddMotto – http://toddmotto.com/opinionated-angular-js-styleguide-for-teams/) you will find (as I have) that adding application logic within Controllers is generally frowned upon.
“In AngularJS world, the services are singleton objects or functions that carry out specific tasks. It holds some business logic. Separation of concern is at the heart while designing an AngularJS application. Your controller must be responsible for binding model data to views using $scope. It does not contain logic to fetch the data or manipulating it.”
http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
From a maintainability perspective, as well as logical code layout perspective, having functional logic within the controller is akin to writing a 1000 line LotusScript agent – not optimal !!
I used this article by Dan Whalin to better understand how to create a Service using a factory
So what changed?
We I changed the controller around so that code in the controller that once looked like this:
$scope.createPerson = function(event) {
$http({
url: '//copper.xomino.com/xomino/ainx.nsf/api/data/documents?form=fUserName',
data: $scope.person,
withCredentials: true,
method: "POST",
headers: {
"Content-Type": "application/json"
}
})
.success(function(data) {
location.href = "#/people";
})
.error(function(data) {
console.log('Error: ' + data);
});
event.preventDefault();
return false;
};
now looks like this:
$scope.createPerson = function(event) {
dataFactory.createPerson($scope.person, event)
.success(function(data) {
event.preventDefault();
location.href = "#/people";
})
.error(function(data) {
console.log('Error: ' + data);
});
};
and the service looks like this:
dataFactory.createPerson = function (person, event) {
console.log(person)
return $http({
url: urlBase+'documents?form=fUserName',
data: person,
withCredentials: true,
method: "POST",
headers: {
"Content-Type": "application/json"
}
})
};
Now I know you are looking at this and saying to yourself – hang on you just copied and pasted all the code and actually made it longer and more complicated – and that would be very perceptive of you – but (and I had to make this leap to believe it myself), it is better.
My example application only really has one purpose – manage people. In a more complex application a separation of logic into like components would make sense. By using the service I have separated all my “do stuff with people” out into a people Service. When I built the next part of the application – to do with buildings for example I would create a buildings Service and manage all my building logic there.
Let the controller do it’s job and bind the data to the View and keep your logic out of the controller and into Services.
The final Code
My service (service.js)
personApp.factory('dataFactory', ['$http', '$timeout', function($http, $timeout) {
var urlBase = '//copper.xomino.com/xomino/ainx.nsf/api/data/';
var dataFactory = {};
dataFactory.getPeople = function () {
return $http.get(urlBase+'collections/name/byFirstName5Col?open&count=3').success(function(data) {
$scope.people = data;
});
};
dataFactory.getPerson = function (id) {
console.log('get person')
return $http.get(urlBase+'documents/unid/' + id)
};
dataFactory.createPerson = function (person, event) {
console.log(person)
return $http({
url: urlBase+'documents?form=fUserName',
data: person,
withCredentials: true,
method: "POST",
headers: {
"Content-Type": "application/json"
}
})
};
dataFactory.savePerson = function (person, event, id) {
console.log(id);
return $http({
url: urlBase+'documents/unid/' + id,
data: person,
withCredentials: true,
method: "PATCH",
headers: {
"Content-Type": "application/json"
}
})
};
dataFactory.deletePerson = function (id) {
var temp = confirm('Are you sure you want to delete?')
if (temp){
return $http.delete(urlBase+'documents/unid/' + id)
} else {
location.href = "#/people"
}
};
return dataFactory;
}]);
My controller
peopleControllers.controller('PersonDetailCtrl', ['$scope', '$routeParams', '$http', 'action', '$timeout', 'dataFactory',
function($scope, $routeParams, $http, action, $timeout, dataFactory) {
$scope.create = (action=='new' || action=="delete");
if (action=="get") {
dataFactory.getPerson($routeParams.docId)
.success(function(data) {
$scope.person = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
}
$scope.home = function(){
location.href='#/people'
};
$scope.createPerson = function(event) {
dataFactory.createPerson($scope.person, event)
.success(function(data) {
event.preventDefault();
location.href = "#/people";
})
.error(function(data) {
console.log('Error: ' + data);
});
};
$scope.savePerson = function(event) {
dataFactory.savePerson($scope.person, event, $routeParams.docId)
.success(function(data) {
event.preventDefault();
location.href = "#/people"
})
.error(function(data, status, headers, config) {
console.log('ErrorData: ' + data);
});
};
if (action=="delete") {
dataFactory.getPerson($routeParams.docId)
.success(function(data) {
$scope.person = data;
$timeout(function(){
dataFactory.deletePerson($routeParams.docId)
.success(function(data) {
$scope.person = {};
location.href = "#/people"
})
.error(function(data, status, headers, config) {
console.log('ErrorData: ' + data);
});
}, 300)
})
.error(function(data) {
console.log('Error: ' + data);
});
};
}]);