Before we begin with the Part V of this series of articles which shows how to make a Project Tracking Website using AngularJS and Web API, let us quickly recap what we have covered so far. So far we have introduced AngularJS, created a REST based service using ASP.NET Web API, have dived into AngularJS Controllers which fetched data using ASP.NET Web APIs and Entity Framework Database Modelling First and have also seen how to design views using Angular JS directives to design interactive web pages. We have explored ng-repeat, ng-show, ng-model and ng-submit directives. We have also seen how to use filters with AngularJS directives.
This is the fifth article in this series. In this article, we will see how AngularJS Services plays an important role in our development. We will separate the HTTP service call into AngularJS service and use it by injecting it into our Controllers.
Services play a vital role in AngularJS. It is a component in AngularJS for specific jobs like –
- Performing a reusable logic which can communicate over HTTP
- Performing the computation on an algorithm
- To implement validation checks against the data
- To invoke the local store stored in a browser or manipulate cookies.
When the logic of your application does not fit in Model/View/Directive, you can make use of AngularJS services to implement application logic. You can make use of these services in a controller, a directive or even in another service.
Types of AngularJS Services
AngularJS provides different ways to define services. The different types of services vary depending on the way to write them and their usage. Services are singletons (Only one instance of the service is created for the entire application). Following are the flavors with a brief description about them:
- service: A service is defined as a type and all members to be added to the service object have to be added to the type instance using the this keyword.
- factory: A factory is defined using JavaScript module syntax. The members to be exposed from a factory have to be returned from the module function.
- providers: Providers are lower level services. Every factory and service is internally registered as a provider. Every provider should expose a member named $get. The contents of $get block are exposed as a factory and they are accessible in run blocks, controllers and other services. The members that are directly added to providers are accessible only inside the config block and inside other providers.
- value: A value is a simple object. We can’t inject any dependencies inside a value.
- constant: A constant is similar to value; except it is accessible inside providers and config blocks.
Built-in Services
AngularJS provides couple of services out-of-the-box. For example –
- $timeout – is an out-of-the-box service which is similar to SetTimeout() in JavaScript.
- $interval – is an out-of-the-box service which is similar to SetInterval() in JavaScript.
When you implement your application logic in services, it very easy to unit test these services. You can also build your own custom services to implement reusable logic which you can use later in your applications.
We have already seen the $http service which can make a request to server side APIs to pull data from or push data into a server. The server in turn may save this data inside a data storage like database.
Let’s see some out-of-the-box services which we can make use of in our Project Tracking website. To start with, we will look at the $log service which provides different functions for logging purpose. The functions are –
- log
- info
- error
- warn
- debug
To use the $log service, we will first open EmployeesController.js and inject the $log service to our controller as shown in the following code –
var EmployeesController = function ($scope,$http,$log) {
Now we will change the SearchEmployees function by adding $log.info function as shown here:
$scope.searchEmployees = function (employeeName) {
$http.get("http://localhost:2464/api/ptemployees/" + employeeName)
.then(employees, errorDetails);
$log.info('Found Employee which contains - ' + employeeName);
};
Now run the EmployeeDetails.html page and open Chrome developer tool (or dev tools of any browser of your choice) to test this code. The output is shown here:
Similarly, there are a number of services which AngularJS provides like $window, $browser, $animate, $anchorscroll etc. that address common problems.
Building Custom AngularJS Services
We will now build a custom service to move some logic out of our controller. We will separate the HTTP calls into a separate service and then use the same into our controller. This way testing of our logic will become much easier and it will open a wider range of scenarios to use this service, into various controllers and directives.
We will first add a JavaScript file EmployeesService.js into our Services folder. Once you add the JavaScript file, write the following code as shown here:
(function () {
var employeeService = function ($http) {
var employees = function () {
return $http.get("http://localhost:2464/api/ptemployees")
.then(function (serviceResp) {
return serviceResp.data;
});
};
var searchEmployees = function (employeeName) {
return $http.get("http://localhost:2464/api/ptemployees/" + employeeName)
.then(function (serviceResp) {
return serviceResp.data;
});
};
return {
employees: employees,
searchEmployees: searchEmployees
};
};
var module = angular.module("ProjectTrackingModule");
module.factory("employeeService", [‘$http’, employeeService]);
}());
In the above JavaScript code, we are creating a service which makes a call to our ASP.NET Web API using $http, the built-in service of AngularJS. This service returns an object containing two functions: employees() and searchEmployees(). These functions in turn call the Web API services that we created in first post of this series.
The employee function makes a call to our ASP.NET Web API using get method of the $http service. This service will return a promise. We are using the then() method of the promise and returning the raw data. The same implementation is there in our SearchEmployees method; which takes an EmployeeName as a parameter. Then we are registering the service with our AngularJS module and making the service available to all the controllers and directives at the end.
The service factory function generates a single object or function that represents the service to the rest of the application.
Note: Please include EmployeesService.js file into your view EmployeesDetails.html after the app.js file.This is because in app.js, we have our AngularJS Module “ProjectTrackingModule” defined and the service is registered to this module.
Now let’s modify the above defined EmployeesController.js to use our custom service. In the controller code, instead of calling $http service directly, we will now inject our service and call its object into our controller. The code is shown below –
(function () {
var EmployeesController = function ($scope,employeeService,$log) {
var employees = function (data) {
$scope.Employees = data;
};
$scope.SearchEmployees = function (employeeName) {
employeeService.SearchEmployees(employeeName)
.then(employees, errorDetails);
$log.info('Found Employee which contains - ' + employeeName);
};
var errorDetails = function (serviceResp) {
$scope.Error="Something went wrong ??";
};
employeeService.employees().then(employees,errorDetails);
$scope.Title = "Employee Details Page";
$scope.EmployeeName = null;
};
app.controller("EmployeesController", ["$scope", "employeeService", "$log", EmployeesController]);
}());
In the above code, the $http service call has been replaced by our custom service employeeService. The employeeService then gives a call to its functions and returns raw HTTP data. This data is being consumed in our models. Run EmployeeDetails.html page and see the output which is shown in the following figure:
Refactoring All Controllers and Modifying Views
Now add the different services into our Services folder. Write the $http service call logic into our services and make use of them in our respective controllers. Below are the services which we will add into our Project Tracking website –
- ProjectsService.js
- UserStoriesService.js
- ProjectTasksService.js
- ManagerCommentsService.js
Controllers to be modified are listed below –
- ProjectsController.js
- UserStoriesController.js
- ProjectTasksController.js
- ManagerCommentsController.js
We will also have to modify the views to display all the records of Projects, UserStories, ProjectTasks and ManagerComments tables data. So we will make use of ng-repeat directive to iterate through the data and display the same in our Views table respectively.
Let’s start with ProjectsService.js. The code for this service is shown below –
(function () {
var projectService = function ($http) {
var projects = function () {
return $http.get("http://localhost:2464/api/ptprojects")
.then(function (serviceResp) {
return serviceResp.data;
});
};
return {
projects: projects
};
};
var module = angular.module("ProjectTrackingModule");
module.factory("projectService", ["$http", projectService]);
}());
Don’t forget to include the reference of ProjectsService.js into ProjectDetails.html view after app.js reference. The ProjectsController.js code is shown here:
(function () {
var ProjectsController = function ($scope, projectService) {
var projects = function (data) {
$scope.Projects = data;
};
var errorDetails = function (serviceResp) {
$scope.Error = "Something went wrong ??";
};
projectService.projects().then(projects, errorDetails);
$scope.Title = "Project Details Page";
};
app.controller("ProjectsController", ["$scope", "projectService", ProjectsController]);
}());
The table tag of ProjectDetails.html is modified as shown here:
<table class="table table-striped table-hover">
<thead>
<tr class="info">
<td>
Project ID
</td>
<td>
Project Name
</td>
<td>
Start Date
</td>
<td>
End Date
</td>
<td>
Client Name
</td>
</tr>
</thead>
<tbody>
<tr class="success" ng-repeat="Project in Projects">
<td>
{{Project.projectID}}
</td>
<td>
{{Project.projectName}}
</td>
<td>
{{Project.startDate}}
</td>
<td>
{{Project.endDate}}
</td>
<td>
{{Project.clientName}}
</td>
</tr>
</tbody>
</table>
Add a UserStoriesService.js file into Services folder and write the following code:
(function () {
var userStoriesService = function ($http) {
var userStories = function () {
return $http.get("http://localhost:2464/api/ptuserstories")
.then(function (serviceResp) {
return serviceResp.data;
});
};
return {
userStories: userStories
};
};
var module = angular.module("ProjectTrackingModule");
module.factory("userStoriesService", ["$http", userStoriesService]);
}());
Don’t forget to include a reference to UserStoriesService.js into ProjectDetails.html view after app.js reference. Now modify the UserStoriesController.js file. Write the following code:
(function () {
var UserStoriesController = function ($scope, userStoriesService) {
var userStories = function (data) {
$scope.Stories = data;
};
var errorDetails = function (serviceResp) {
$scope.Error = "Something went wrong ??";
};
userStoriesService.userStories().then(userStories, errorDetails);
$scope.Title = "User Stories Page";
};
app.controller("UserStoriesController", ["$scope", "userStoriesService", UserStoriesController]);
}());
The table tag of UserStoryDetails.html page is as shown below –
<table class="table table-striped table-hover">
<thead>
<tr class="info">
<td>
Project Task ID
</td>
<td>
Start Date
</td>
<td>
End Date
</td>
<td>
Task Completion (%)
</td>
</tr>
</thead>
<tbody>
<tr class="success" ng-repeat="Task in Tasks">
<td>
{{Task.projectTaskID}}
</td>
<td>
{{Task.taskStartDate}}
</td>
<td>
{{Task.taskEndDate}}
</td>
<td>
{{Task.taskCompletion}} %
</td>
</tr>
</tbody>
We will now add ProjectTasksService.js file into our Services folder and write the following code:
(function () {
var projectTasksService = function ($http) {
var projectTasks = function () {
return $http.get("http://localhost:2464/api/ptprojecttasks")
.then(function (serviceResp) {
return serviceResp.data;
});
};
return {
projectTasks: projectTasks
};
};
var module = angular.module("ProjectTrackingModule");
module.factory("projectTasksService", ["$http", projectTasksService]);
}());
Don’t forget to include the reference of ProjectTasksService.js into ProjectDetails.html view after app.js reference. Modify the ProjectTasksController.js file as shown here:
(function () {
var ProjectTasksController = function ($scope, projectTasksService) {
var projectTasks = function (data) {
$scope.Tasks = data;
};
var errorDetails = function (serviceResp) {
$scope.Error = "Something went wrong ??";
};
projectTasksService.projectTasks().then(projectTasks, errorDetails);
$scope.Title = "Project Tasks Page";
};
app.controller("ProjectTasksController",["$scope", "projectTasksService", ProjectTasksController]);
}());
The table tag of ProjectTaskDetails.html page is as shown here:
<table class="table table-striped table-hover">
<thead>
<tr class="info">
<td>
Project Task ID
</td>
<td>
Start Date
</td>
<td>
End Date
</td>
<td>
Task Completion (%)
</td>
</tr>
</thead>
<tbody>
<tr class="success" ng-repeat="Task in Tasks">
<td>
{{Task.projectTaskID}}
</td>
<td>
{{Task.taskStartDate}}
</td>
<td>
{{Task.taskEndDate}}
</td>
<td>
{{Task.taskCompletion}} %
</td>
</tr>
</tbody>
Finally we will add ManagerCommentsService.js into our Services folder and write the code as follows:
(function () {
var managerCommentsService = function ($http) {
var managerComments = function () {
return $http.get("http://localhost:2464/api/ptmanagercomments")
.then(function (serviceResp) {
return serviceResp.data;
});
};
return {
managerComments: managerComments
};
};
var module = angular.module("ProjectTrackingModule");
module.factory("managerCommentsService", ["$http", managerCommentsService]
);
}());
Don’t forget to include the reference of ManagerCommentsService.js into ProjectDetails.html view after app.js reference. Now modify the ManagerCommentsController.js file as shown in the following code –
(function () {
var ManagerCommentsController = function ($scope, managerCommentsService) {
var managerComments = function (data) {
$scope.Comments = data;
};
var errorDetails = function (serviceResp) {
$scope.Error = "Something went wrong ??";
};
managerCommentsService.managerComments().then(managerComments, errorDetails);
$scope.Title = "Manager comments Page";
};
app.controller("ManagerCommentsController", ["$scope", "managerCommentsService", ManagerCommentsController]);
}());
The table tag of ManagerCommentDetails.html page is as shown here:
<table class="table table-striped table-hover">
<thead>
<tr class="info">
<td>
Comment ID
</td>
<td>
Comments
</td>
</tr>
</thead>
<tbody>
<tr class="success" ng-repeat="Comment in Comments">
<td>
{{Comment.managerCommentID}}
</td>
<td>
{{Comment.comments}}
</td>
</tr>
</tbody>
</table>
Now you have seen how easy it is to build a custom AngularJS services and use them in your application. We will continue to invest our time to modify these services when we will start implementing CRUD operations on our database tables using ASP.NET Web APIs.
In the next article, we will take a closer look at how to use routing in AngularJS, the role of routing and configuring routes in our Project Tracking application.
Summary
In this article, we have seen how AngularJS services which is available out-of-the-box, can help you build your applications. We have seen $log and $http services so far. We have also seen how to build Custom Services in AngularJS and use it in our Project Tracking website.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Pravinkumar, works as a freelance trainer and consultant on Microsoft Technologies. He is having over 10 years of experience in IT and is also a Microsoft Certified Trainer(MCT). He has conducted various corporate trainings on all versions of .NET Technologies including .NET, SharePoint Server, Microsoft SQL Server, Silverlight, ASP.NET, Microsoft PerformancePoint Server 2007 (Monitoring). He is passionate about learning new technologies from Microsoft. You can contact Pravinkumar at dabade[dot]pravinkumar [attherate] gmail[dot]com