Check out a free preview of the full Ember Octane Fundamentals course
The "Services" Lesson is part of the full, Ember Octane Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
Mike creates a service for user authentication and configures the service to hold onto the ID of the logged in user by utilizing local storage in the browser.
Transcript from the "Services" Lesson
[00:00:00]
>> Mike North: This is a pretty silly kind of authentication that we're using right now. There is basically like you get to select any user and you have this local user ID state and nothing is done with it from that point on. So we're gonna create some state that can be shared horizontally across different concerns in our app.
[00:00:21]
And we're going to do this by way of something called a service. You can think of a service as an object or a class where an instance of that class can connect various unrelated things together. For example, the currently logged in user. We probably gonna have to use that here on the side bar in order to display who's logged in.
[00:00:49]
We probably wanna use it to make sure that only logged in users can get to this UI that we're looking at and logged out users should be kinda kicked out to the log in page. If we were making authenticated API calls, which we are not, you'd probably have some tokens some access token that's authentication related.
[00:01:09]
So the alternative like to using a service would be, it would involve a lot of threading things through and passing a lot of orgs around to all of these different concerns that need to know about it. And it would greatly complicate our component tree. A service allows us to avoid that and if you've worked with other frameworks this is analogous to the react context API and to angular services, right?
[00:01:41]
It's a way of sharing things that does not involve passing it all the way through the component tree explicitly. So we're going to begin by using ember CLI to create a service. ember generate service, and we're gonna call it auth.
>> Mike North: And as you probably expect by now, we got two files outta this.
[00:02:08]
The service itself and a passing unit test. So let's look at the service. It's an ES6 class, these are very simple. All it does is it entrust that it is a singleton scoped to the application. That doesn't- that means that like in your acceptance testing things, you still will tear that singleton down and recreate it.
[00:02:31]
But while your app is running, you can be assured that this thing will have very long life cycle, right. And you can store data here, and even as the user navigates to different routes, and components are created and destroyed, this thing will live a long time. But there's not much to it, you can kinda think of it as a very generic object.
[00:02:54]
So, what we want on this is to have a currentUserID and we can also have this service handle the login and logout related transitions, right? The navigation things that happen and what that lets us do is kind of encapsulate all of the author related stuff into this one object, right?
[00:03:23]
So it knows what it means to login and what it means to log out, what it means to check whether users authenticated or not. And everyone else gets the benefit of operating on a central definition of all those things. So let's get started. So we want a currentUserID and I want this to survive a reload.
[00:03:42]
So I'm gonna use local storage for this. I'm gonna have a property that is based on the local storage value. And just for consistency's sake, I'm gonna define a key up here.
>> Mike North: Just so that when we do reads and writes to local storage, I cannot make a typo and read from one key and write to another.
[00:04:07]
So get currentUserID and we're gonna have. Window.localStorage.getItem(AUTH_KEY),
>> Mike North: And we have to return it. We can have a function here called LoginwithUserId, which our log in form will take advantage of user ID and what it's gonna do is window, local storage, set item,
>> Mike North: userId. So effectively, like call in login with userId, it will save it in local storage, getting the current userId will read it from local storage, seems good.
[00:04:54]
In addition to writing this thing to local storage we want to trigger a change in URL, right. Logging in involves taking you into the log in experience and that means using link to as far as we know at this point, but there's another way we can programmatically do the same thing that clicking on a link does.
[00:05:15]
And that is through using the router which also happens to be a service. So we get to consume a service now and here's how that works. We're gonna import a function called inject or we're gonna within this file alias it at service.
>> Mike North: So we have imported this thing, if we can call service and here's how we get access to the router.
[00:05:51]
>> Mike North: That's it, actually I'm gonna keep, wow we'll do that in a second. So we have access to the router and we can just do this.router and we have a hold of that. For autocomplete goodness, I'm gonna import. Yeah, that'll do. Import the router symbol. Right, this is from your router JS file and I'm just gonna use that in a comment.
[00:06:23]
>> Mike North: Used as type here. My mistake, looks like I have to import the base class.
>> Mike North: Which makes sense because there are no types for this code that we're writing here. So we've got router we're just using it in the JS dot thing, JS.comment, but that let's us down here do this router transition two and we can name the route we wish to send the user to.
[00:06:52]
So when they log in, they should be sent to teams.
>> Mike North: So they log in, all we need is just pass the userId, it'll get saved and then the user is sent off to the authenticated experience. That should be it. Now let's go hook it up to our login form.
[00:07:15]
So go to login form JS, and instead of calling this login with userId, you can actually get rid of that down here. Well we need to inject the service, but down here's where we'll use that function, log in with userId that we just made. So we'll import that, inject, inject as service,
[00:07:44]
>> Mike North: From ember service and then up here will say service auth, right, it is just the name of the service itself. We can use our JS dot thing again.
>> Mike North: I’m just trying to use autocomplete here to grab that import, type authservice, so now we'll get auto complete for our local code, and this .auth.login with userId.
[00:08:30]
Let's test it out.
>> Mike North: So I'm just gonna check my depth tools to make sure I have no local stored currently I don’t know if that happened just now or in testing earlier. But it can reload and we see there’s no local storage here, nothing so I’ll select a user, it says logging in with userId of two, sign in.
[00:09:05]
This is a debugger that we left here. We should probably remove that.
>> Mike North: Let's look at local storage again.
>> Mike North: There it is, so I've successfully saved the user ID and I did successfully end up on this URL. This is really nice cuz imagine if there were a couple different ways to login like maybe when a new user is registered and you get their information back.
[00:09:33]
You can automatically treat them login right after sign up. This gives you one central place where anyone can take advantage of this stuff and to demonstrate that. Well we won't worry about that yet. But I could show the userId in a component as well. We will certainly get there.
[00:09:56]
It's just not a value. A lot of value in doing that at this moment. So let's check our tests.
>> Mike North: Looks like.
>> Mike North: Looks like everything is okay at this point. So we are good to go. So just a recap, we created a service called auth, it uses another service called router.
[00:10:24]
The router's job is to handle transitions to different URLs sort of like programmatic clicking on links. The auth service holds the currently login users ID. It handles all the particulars of dealing with local storage, no other part of the app needs to know that local storage is what we happened to be using.
[00:10:45]
So it's a nice encapsulation tool, and a nice way of sharing things across many unrelated concerns otherwise unrelated concerns in your app. All you need to remember is inject it like that. So import this and use that, and this is just the name of the service, the file name auth.js.
[00:11:06]
So I will make a commit, we have just completed step ten.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops