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.

Subscribe to our mailing list