The framework allows the creation of a service component in different ways. The most usual way is to create it using a factory. Therefore, we need to register the service in the application module that passes two parameters: the name of the service and the factory function.
A factory function is a pattern used to create objects. It is a simple function that returns a new object. It brings more concepts such as the Revealing Module Pattern.
To understand this pattern, let's start by declaring an object literal called car:
var car = {
plate: "6MBV006",
color: "Blue",
entrance: "2013-12-09T23:46:15.186Z"
};
The JavaScript language does not provide any kind of visibility modifier; therefore, there is no way to encapsulate any property of this object, making it possible to access everything directly:
> console.log(car.plate);
6MB006
> console.log(car.color);
Blue
> console.log(car.entrance);
2013-12-09T23:46:15.186Z
In order to promote encapsulation, we need to use a function instead of an object literal, as follows:
snippet
var car = function () {
var plate = "6MBV006";
var color = "Blue";
var entrance = "2013-12-09T23:46:15.186Z ";
};
Now, it's no longer possible to access any property of the object.
> console.log(car.plate);
undefined
> console.log(car.color);
undefined
> console.log(car.entrance);
undefined
This happens because the function isolates its internal scope, and based on this principle, we are going to introduce the concept of the Revealing Module Pattern. This pattern, beyond taking care of the namespace, provides encapsulation. It allows the implementation of public and private methods, reducing the coupling within the components. It returns an object literal from the function, revealing only the desired properties.
snippet
var car = function() {
var plate = "6MBV006";
var color = "Blue";
var entrance = "2013-12-09T23:46:15.186Z ";
return {
plate: plate,
color: color
};
};
Also, we need to invoke the function immediately; otherwise, the variable car will receive the entire function. This is a very common pattern and is called IIFE, which is also known as Immediately-Invoked Function Expression.
snippet
var car = function() {
var plate = "6MBV006";
var color = "Blue";
var entrance = "2013-12-09T23:46:15.186Z ";
return {
plate: plate,
color: color
};
}();
Now, we are able to access the color but not the entrance of the car:
> console.log(car.plate);
6MB006
> console.log(car.color);
Blue
> console.log(car.entrance);
undefined
Beyond that, we can apply another convention by prefixing the private members with _, making the code much easier to understand:
var car = function() {
var _plate = "6MBV006";
var _color = "Blue";
var _entrance = "2013-12-09T23:46:15.186Z ";
return {
plate: _plate,
color: _color
};
}();
This is much better than the old-school fashion implementation of the first example, don't you think? This approach could be used to declare any kind of AngularJS component, such as services, controllers, filters, and directives.
In the following code, we have created our parkingService using a factory function and the Revealing Module Pattern:
services.js
snippet
parking.factory("parkingService", function() {
var _calculateTicket = function(car) {
var departHour = new Date().getHours();
var entranceHour = car.entrance.getHours();
var parkingPeriod = departHour– entranceHour;
var parkingPrice = parkingPeriod * 10;
return {
period: parkingPeriod,
price: parkingPrice
};
};
return {
calculateTicket: _calculateTicket
};
});
In our first service, we started to create some parking business rules. From now, the entrance hour is subtracted from the departure hour and multiplied by $10.00 to get the parking rate per hour.
However, these rules were created by means of hard coded information inside the service and might bring maintenance problems in the future.
To figure out this kind of a situation, we can create constants. It's used to store configurations that might be required by any application component. We can store any kind of JavaScript data type such as a string, number, Boolean, array, object, function, null, and undefined.
To create a constant, we need to register it in the application module. In the following code, there is an example of the steps required to create a constant.
constants.js
parking.constant("parkingConfig", {
parkingRate: 10
});
Next, we refactored the _calculateTicket method in order to use the settings from the parkingConfig constant, instead of the hard coded values. In the following code, we are injecting the constant inside the parkingService method and replacing the hard coded parking rate.
services.js
snippet
parking.factory("parkingService", function(parkingConfig) {
var _calculateTicket = function(car) {
var departHour = new Date().getHours();
var entranceHour = car.entrance.getHours();
var parkingPeriod = departHour– entranceHour;
var parkingPrice = parkingPeriod * parkingConfig.parkingRate;
return {
period: parkingPeriod,
price: parkingPrice
};
};
return {
calculateTicket: _calculateTicket
};
});
The framework also provides another kind of service called value. It's pretty similar to the constants; however, it can be changed or decorated.