Introduction to Services
Published on Oct 14, 2015
Services were introduced in Ember 1.10, replacing and improving upon the common pattern of service controllers. They’re incredibly useful for connecting to third-party APIs, connecting disparate parts of your application, and storing state that should survive route changes but not be saved to the server.
In this video we create a Service, then share that Service and its state between a Controller, a Model, and a Component by injecting it into all three.
Links
Code
Create the service
//services/current-team.js
export default Ember.Service.extend({
monsters: [],
add(monster){
if(this.get("fullTeam")){
alert('Team is full. Remove a monster to add another.')
} else {
this.get('monsters').pushObject(monster)
}
},
remove(monster) {
this.get('monsters').removeObject(monster);
},
includes(monster){
return this.get('monsters').includes(monster)
},
fullTeam: Ember.computed.gte('monsters.length', 6)
});
Inject it in several different places
//controllers/monsters/monster.js
export default Ember.Controller.extend({
currentTeam: Ember.inject.service(),
actions: {
add(){
this.get("currentTeam").add(this.get("model"))
},
remove(){
this.get("currentTeam").remove(this.get("model"))
}
}
})
//models/monster.js
//...
currentTeam: Ember.inject.service(),
onTeam: Ember.computed('currentTeam.monsters.[]', function(){
return this.get('currentTeam').includes(this);
})
//components/team-lineup.js
export default Ember.Component.extend({
currentTeam: Ember.inject.service(),
teamMembers: Ember.computed.alias('currentTeam.monsters')
});
Transcript
Services were introduced to Ember in 1.10, and they’ve become very, very important since then. So a service is an Ember object that sticks around for the life of the application, and it can store state and you can access it in almost any part of the application easily, just by injecting it.
Here are some of the things that the core team thinks that you might use services for. These all look like great episode ideas, so if you want to see one of them in particular, go ahead and put it down in the comments.
In this episode we’ll be talking about the basics, and then in future episodes we will definitely be going over user and session authentication.
So today we’ll be working with a Monster Card collection app, where we have a bunch of monster cards and we want to assemble a team out of some of them.
So we’ll be going through multiple URLs, multiple routes, but we’ll want to keep the same state throughout all of that, and so that makes this a good use for a service.
We’ll go ahead and generate a service called the current-team
.
This is what was generated for us. Notice it extends of Ember.Service
.
So for our team of monsters we’ll need a list of monsters. It’ll start off by default as just an empty array. Then we’ll need methods that add and remove the monster. So we’ll just pushObject
to the array and removeObject
from the array. Later we’ll need methods that check whether the monster is already included in the array and whether the team is full, full being having six monsters already. So we’ll go ahead and add those.
We’ll also add an if
check to see if the team is full, and if it is, we’ll give an alert instead of adding that monster. So there’s our first service.
Notice it’s laid out just like any other Ember.Object
, but the important thing isn’t understanding what this particular service does, it’s how we’re going to use it in the rest of the app.
So first, we’re going to want to display the monsters team here,
so we’ll want to access it in the team-lineup
component. How we do that is we’re going to create the currentTeam
variable, do Ember.inject.service
, and so what that’s going to do is that’s going to find the service called currentTeam
and it’ll know to take that camelcase and turn it into dash case, and it will inject that there so we can access that service from this component.
Notice if we want to call it something else like monsterTeam
, we can still do that. We’d have to give the name of the service as an argument,
but we’ll go ahead and stick with currentTeam
for now. So we got the currentTeam
service injected into the team-lineup
component. Let’s go ahead and use that in our template. So we’ll loop through that. So we get the currentTeam
and then grab the list of monsters off of there and loop through them. Then for now we’ll just display it whole and put them each in a list item, since they’re part of a list.
And we’ll go ahead and give the currentTeam
some seed data. Once again these will soon be full objects, but we’re just showing that it works.
And there we go.
So we have our service being injected into the component and displaying its data. Next we’ll make this ‘Add Team’ button work so it will actually add stuff to the current team.
To prepare for that, we’ll go ahead and remove this seed data from the monsters hash since we’ll be putting in real objects.
And then we’ll go ahead and put in a much more complete styling here, a much more complete template.
So now the current-team
service
and the team-lineup
component
are ready for the ‘Add Team Member’ button. To make that work we need to go to our controllers
, monster
, monster
controller, and then we have to inject the service.
Notice this is just like how we injected the service on the team-lineup
component. So now, just like in the team-lineup
component, the currentTeam
service is available for use. Now we just have to add the add
action to this controller, and it’ll grab the currentTeam
service and use that add
action,
and then we’ll put the add
action in this controllers template.
Now we can start building our team.
There are a couple of problems though. First, you can keep on adding the same monster to your team, and you don’t want that. You only want one of each monster per team.
We’ll solve this problem by going to the monster
model, then we’ll inject the service here. Yes, you can inject service onto models as well, and we’ll use that to create the onTeam
property, which tells you if this particular monster is already on your team. It does that by watching the monsters hash and then checking whether it includes the current monster.
So here’s how we’ll start off using this. We’ll go to the template of the monsters controller, and we see that we have the button showing at all times now. But here we can go and create an if
statement, so if
it’s onTeam
, it’ll show something else.
Now when we add a Zombird, you can only add it once.
So these are the basics of having this service, but there are two quick little features that’ll make it even nicer. One is to reuse the onTeam
property and work to style the card differently when it’s already on the team. We’ll do that both in the monster controller
as in the monsters plural controller.
Then we’ll go to our browser and we’ll see that it’s changing how it’s styled, so we can tell which monsters are on our team so we don’t accidentally click on them again.
The second addition is a remove button, so we’ll add a remove
action on the monsters controller, which calls the remove
action, the remove
method on currentTeam
.
And then in the monster template, we’ll create a button where you can call the remove
action.
So then when we add it, we can remove it again.
So in order to make this feature, we created a service. This service was a currentTeam
service, and we did it by extending the Ember.Service
object.
Then in three different places we injected this service. We did it in the model,
we did it in the monster
controller,
and then in the team.lineup
component.
In each of them, we used Ember.inject.service
. So each of these was accessing the same service, and that same service had the same data. It was a singleton.
So in the monster
controller, you could add and remove stuff from there,
and then in the team.lineup
you could loop through every monster in the list.
And then in the monster
model, you’re checking whether that monster was on the list or not.
Without services this would have been pretty difficult to coordinate, because you can’t necessarily have all these things talking with each other easily. So we have a service that stores all this data and then everything else is accessing it.
In this week’s pro episode, we’ll be seeing another use case of this with a current user and a session service. I’ll see you then.