Until now, we have dealt with data-binding in AngularJS. We have seen how to take data from our controllers and get it into the UI, and ensure that whenever the user interacts with or types in any data, we get it back into our controllers. We used and worked with some common directives, and dealt with forms and error handling. In this blog, we dive into AngularJS services.
By the end of the next few blogs, we will have a thorough understanding of AngularJS services and get some hands-on experience in using core built-in AngularJS services. After that, we will learn why and when we should create AngularJS services, and actually create a simple service ourselves.
AngularJS services are functions or objects that can hold behavior or state across our application. Each AngularJS service is instantiated only once, so each part of our application gets access to the same instance of the AngularJS service. Repeated behavior, shared state, caches, factories, etc. are all functionality that can be implemented using AngularJS services.
Let’s first take a look at why we need them.
Why Do We Need AngularJS Services?
So far we have only created AngularJS controllers, which create state and functions that our HTML then uses for a variety of tasks. AngularJS controllers are great for tasks that relate to the following:
- Which model and data fields to fetch and show in the HTML
- User interaction, as in what needs to happen when a user clicks something
- Presentation logic, such as how a particular UI element should be styled, or whether it should be hidden
Controllers are stateful, but ephemeral. That is, they can be destroyed and re-created multiple times throughout the course of navigating across a Single Page Application. Let’s take a look at an example to clarify this:
In this example, we introduced two controllers for the first time: a MainCtrl and a SubCtrl. The MainCtrl controls the overall page, and the SubCtrl controls a subsection of the page and holds the data we want to display.
We also have two tabs, which are shown and hidden depending on which button the user clicks. This is accomplished using a new directive, ng-switch. ng-switch acts like a switch statement in the HTML. It takes a variable (using the on attribute, which in this case is MainCtrl’s tab), and then, depending on the state, hides and shows elements (using the ng-switch-when attribute, used as children of the ng-switch). The ngswitch-when takes the value that the variable should take. Finally, the SubCtrl has a function to add more items to the array, which is triggered by a button in the UI.
Now, moving on to our HTML, the body is controlled by the MainCtrl, and holds state on which tab in the HTML is shown. We then have two buttons that allow us to change which tab is currently shown. Notice again that this is done by changing the model and letting AngularJS update the UI automatically.
Finally, we have a div element on which we have the ng-switch. Both tabs (each one has the ng-switch-when) are exactly the same except for the header. They show the list of items, and add items when the button in that tab is clicked. With this out of the way, let’s take a look at some key behaviors:
- Both of the tabs, First and Second, are using the same controller, SubCtrl. But each one has its own instance of the list variable. Adding items in one tab does not add them to the other, and vice versa.
- If we add items to the first tab and then switch to the second tab, we will see the items the controller starts with. But then if we navigate back to the first tab, we will see that those items disappear from the first controller as well.
We could still achieve the functionality we were aiming for by having a parent-level controller, and moving our list variable into the parent controller (such as MainCtrl). Each SubCtrl would then have to access the variable through the top controller explicitly. This solves our problem but adds global, implicit state, which is never ideal for a large, maintainable application.
When we use controllers, they are instances that get created and destroyed as we navigate across our application. This is especially true when we start working on routing and multiple URLs in a Single Page Application. Also, one controller cannot directly communicate with another controller to share state or behavior.
Services Versus Controllers
In the applications we develop, we will end up using both controllers as well as services. Now, when we say “services” in AngularJS, we include factories,
services, and providers. We’ll see the difference between the three in “The Difference Between Factory, Service, and Provider” on a later blog post. That said, both controllers and services fill a certain need in our application, and attempting to do too much or do in one what ideally belongs in the other can lead to bloated, unmaintainable, and untestable code. Below Table gives a quick overview of the types of responsibilites and needs for which we would use controllers versus services.
|Presentation logic||Business logic|
|Directly linked to a view||Independent of views|
|Drives the UI||Drives the application|
|One-off, specific Reusable Responsible for decisions like what data to fetch, what data to show, how to handle user interactions, and styling and display of UI||Responsible for making server calls, common validation logic, application-level stores, and reusable business logic|
We’ll dive into AngularJS services in next blog post, including how to use existing services and create our own. After we finish that, we’ll come back to some examples of what belongs in services.