Through this post I am trying to demonstrate how to write a custom service in AngularJS.
Why custom Service?
- Reusability
Services are used to perform specific operation Or to write piece of code that doesn't fit in Directive or model. Say for example we have to call an API and capture data what we receive from API and perform some operation
on that data.In such cases its a better practice that we take out such piece of code and keep it separately from any directive so that we can encapsulate code and which can be reused when ever required.
- Same data can be shared across application
These services are singletons. Single instance for services will be used across the application.
- Reduce complexity
As we encapsulate code the script files will be easier to understand and manage and enhance whenever required
By default AngularJS provides many inbuilt services to perform many specific operations such as $http, $resource, $q, $anchorScroll, $cacheFactory, $compile, $parse, $locale, $timeout, $exceptionHandler, $filter, $cookieStore, etc..
Instead of using $http from directive lets encapsulate it inside our custom service and lets expose custom service, by doing this user need not to worry what our custom service do under the hood.
To implement this, lets use publicly exposed API “https://api.github.com/users/angular” which gives list of users and lets try to bind this to Index.html page.
Service.js:
Our custom service name would be ‘github’
(function(){
var github = function($http){ //github name of custom service
var getUser = function(userName){
return $http.get("https://api.github.com/users/" + userName)
.then(function(response){
return response.data;
});
};
var getRepos = function(user){
return $http.get(user.repos_url)
.then(function(response){
return response.data;
});
};
var getRepoDetails = function(username,repoName){
return $http.get("https://api.github.com/repos/" + repoName)
.then(function(response){
return response.data;
});
};
return{
getUser : getUser,
getRepos: getRepos,
getRepoDetails : getRepoDetails
};
};
var module = angular.module("githubViewer"); // Loading app
module.factory("github", github); //Registering custom service to application
}());
UserController.Js: Controller used to bind data into view
(function() {
var app = angular.module('githubViewer');
app.controller('UserController',['$scope','github','$routeParams','$location', //Consuming custom service ‘github’
function($scope, github, $routeParams,$location) {
var onUserComplete = function(data)
{
$scope.EnableRepos = true;
$scope.IsError = false;
$scope.User = data;
var repo = $scope.getRepos();
}
var OnReposComplete = function(data){
$scope.Repos = data;
}
var onError = function(error)
{
$scope.IsError = true;
$scope.Error = "API didn't return response";
}
$scope.getRepos = function(){
github.getRepos($scope.User) // Invoking method from custom service
.then(OnReposComplete, onError);
}
$scope.getRepoDetails = function(repo){
$location.path("/RepoDetails/" + $scope.userName + "/" + repo.full_name);
}
$scope.userName = $routeParams.username;
github.getUser($scope.userName).then(onUserComplete,onError); // Invoking method from custom service
}]);
}());
User.html: //Child page which will be included in Index.html
<div>
<div ng-show="User">
<div>
<div>{{User.name}}</div>
<div ng-show="{{IsError}}">{{Error}}</div>
<img ng-src="{{User.avatar_url}}" alt="{{User.name}}" height="100px" width="100px"/>
</div>
<p>
<span>Order By:</span>
<select ng-model="RepoSortOption">
<option value="name" ng-selected="true">Name</option>
<option value="stargazers_count">Stars</option>
<option value="language">Language</option>
</select>
<span>Direction:</span>
<select ng-model="RepoSortDirection">
<option value="+" ng-selected="true">Ascending</option>
<option value="-">Descending</option>
</select>
</p>
<table border="1" id="reposDetails">
<thead>
<tr>
<th>Name</th>
<th>Stars</th>
<th>Language</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="repo in Repos | orderBy:RepoSortDirection + RepoSortOption">
<td><a href="#RepoDetails/User.login/repo.name">{{repo.name}}</a></td>
<td>{{repo.stargazers_count | number}}</td>
<td>{{repo.language}}</td>
</tr>
</tbody>
</table>
</div>
<a href="#/UserSearch">Back to search</a>
</div>
Index.html: User.html will be included in Index.html
<html ng-app="githubViewer">
<head>
<!-- SCROLLS -->
<!-- load bootstrap and fontawesome via CDN -->
<!-- <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css" /> -->
<!-- SPELLS -->
<!-- load angular and angular route via CDN -->
<!-- <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script> -->
<!-- <script src="../../Scripts/lib/angular.min.js"></script> -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
<script src="../../Scripts/Hands-on/app.js"></script>
<script src="../../Scripts/Hands-on/Application.js"></script>
<script src="../../Scripts/Hands-on/UserSearchController.js"></script>
<script src="../../Scripts/Hands-on/UserController.js"></script>
<script src="../../Scripts/Hands-on/RepoDetailsController.js"></script>
<script src="../../Scripts/Hands-on/Services.js"></script>
<script src="../../Scripts/Hands-on/Script.js"></script>
<title>Index</title>
</head>
<body>
<div ng-include="'Header.html'"></div>
<!-- MAIN CONTENT AND INJECTED VIEWS -->
<div id="main">
<!-- angular templating -->
<!-- this is where content will be injected -->
<div ng-hide="true">{{message}}</div>
<div ng-view></div> // Where all child pages will be included
</div>
</body>
</html>
Note: Complete source code is available @ https://onedrive.live.com/redir?resid=5A70375467BBACD0%21116 for reference.
Hope this helps.
Thanks!