Building Awesome Web Apps with Angular 2 Defining a Service
This course has been updated! We now recommend you take the Angular 9 Fundamentals course.
Transcript from the "Defining a Service" Lesson
>> Lukas Ruebbelke: The three big things I want us to walk away with, or rather four. Is how to spin up a project, how to build a component, how to hook it into a template, and then how to abstract our basic business logic into a service. And so I dedicate this module to my friend here, you, this is for you bud.
>> Lukas Ruebbelke: Service man. Okay so a service is just a class, and we understand how classes work. We've built components, properties, methods.
>> Lukas Ruebbelke: And so, this really becomes quite simple. How do we define a service? How do we expose a service? And how do we consume a service?
>> Lukas Ruebbelke: So, defining a service, well, look at this.
[00:01:03] This could, if I had not had, so if this was called items, and I took off the metadata, what do we know if this is a component, or a service, I mean it would be hard to tell.
>> Speaker 2: If you took off what?
>> Lukas Ruebbelke: So if I went like this, let's take off the metadata.
>> Lukas Ruebbelke: I did this.
>> Lukas Ruebbelke: At this point it's just a class, angular has no way that we don't know it can be anything. There's even times where I've had a component that simply had a collection on it that I'm consuming in my templates, I'm like this needs to be a service.
[00:01:55] Copy it, paste it, change the name, change the metadata boom. And so this is, again what I think Angular 2 got very, very right, is we're letting ES6 do all of the heavy lifting in terms of organization, exposing functionality, etcetera. Like, in classes now for inheritance that's the devils work, but for organizational purposes it's amazing like classes in modules are great for organizing stuff.
[00:02:32] So that's go back, that is definitely kind of an interesting thought is once we take this away, and it's just a class, what is it? So the question is are static classes okay in NG 2?
>> Speaker 2: Typescript itself certainly does not.
>> Lukas Ruebbelke: Yeah, so the first person to google that, and get an answer get a high five from me.
>> Lukas Ruebbelke: And so defining a service,
>> Lukas Ruebbelke: It's here I don't feel like I don't feel like really CIDER.
>> Lukas Ruebbelke: How do we expose stuff to the application in the module? So we'll just go down to providers, add it in. And.
>> Speaker 2: Does that assume that there's an import above it there?
>> Lukas Ruebbelke: Yes. So we'll hop into the code and actually walk through this. This is and then to consume a service
>> Lukas Ruebbelke: Well, in the constructor of the class, using constructor assignment.
>> Lukas Ruebbelke: But look at this, you gonna actually have a service that consumes another service, and the mechanism is exactly the same.
[00:04:20] So again talking about the patterns like the dependency injection mechanism just works.
>> Lukas Ruebbelke: So we're starting to kinda see some of these patterns.
>> Lukas Ruebbelke: So there was a question would you name classes specifically like CSS has Bem, and the style guide, the official one that John Papa did, the Angular 2 style guide, they have some specific recommendations.
[00:04:55] I generally would do the model that you're basically talking about, whatever that is, the mechanism, so in this case like items. And then whatever it is like item service, items component, item list component and therefore when I look at it or I'm looking in the code I'm able to find it.
[00:05:13] Now some people have some very different opinions about that for me personally I feel like code should be self documenting, and I like to when I look at something know that this is a service and this is a component, etc.
>> Lukas Ruebbelke: Okay, wow, demonstration time. Time flies.
>> Lukas Ruebbelke: So the temptation here, it's for me to just go ahead and solve the challenge that's coming up, but I don't wanna do that.
[00:05:55] So now I need to make one up off the top of my head. Let's do a new service.
>> Lukas Ruebbelke: New text script file.
>> Lukas Ruebbelke: Let's go users.service.ts
>> Lukas Ruebbelke: Let's do something here real quick. After this im create a, hold on I got this wrong
>> Lukas Ruebbelke: So I'm creating an interface for user.
[00:07:12] So going back to interfaces, what this does is it defines what a user object should look like. So it allows us to actually create typing, typings for specific objects. So, again C sharp, this is any classical language we understand this quite well. So let's go name, actually let's go i.d number, name is string and description.
[00:07:47] Pretty standard, okay. And then what we can do is let's go, selected
>> Lukas Ruebbelke: Again, thank you Intellisent's. So we're saying is when we select set, this selected user, we expect it, to be, This, it will look like this. So we can then, make some inferences. As, well you can see here, that.
>> Lukas Ruebbelke: Of how this is going to, be shape. So if we go, for inferences.
>> Lukas Ruebbelke: Let's see, if this is gonna make me.
>> Lukas Ruebbelke: So, set's selected user. And so now we can say when we set this in here, like we expect this parameter to be of type user.
[00:08:57] And then we'll go, now you can see here, because we have those three properties on the interface, that probably can we make some inferences about what this is going to look like, but the IDE or the IDE as well. Like this, now. I think this is cool, you can also type, collections.
[00:09:26] So, what you can do here, is.
>> Lukas Ruebbelke: They're saying, We are printing a user's property, that is an array, of user's objects. So then if we go here, we'll go ID one. And so right now it's already anchoring, cuz I haven't satisfied the interface. Name, Marcus, description, FEM OG, and now it's happy.
[00:10:10] But look what happens if for instance, oops it doesn't match.
>> Lukas Ruebbelke: So, you go here.
>> Lukas Ruebbelke: I'm like the 'FEM, MINI ME'.
>> Lukas Ruebbelke: Davis, I don't know who this is. Who!? Okay, so far so good? We now have, the user service, we've created interface, and so now, we've done a pretty good job of, Of typing these things out, so we can see, the user interface in action.
[00:11:07] But, now how do we expose this to the rest of our application?
>> Lukas Ruebbelke: Yeah, I think you've got it.
>> Speaker 2: Import it, in your module.
>> Lukas Ruebbelke: Yes. Good, so, we can do this two ways.
>> Speaker 3: [CROSSTALK] We need, a semi colon.
>> Lukas Ruebbelke: What?
>> Speaker 3: After, selected user.
>> Lukas Ruebbelke: Did, I mess something up?
>> Speaker 3: [CROSSTALK] Selected user,
>> Lukas Ruebbelke: Yeah. Thank you, so much. Pair programming, FTW LOL. So, I'm gonna go ahead, and I'm gonna just do a barrel roll, so I'm going to add this to the barrel.
>> Lukas Ruebbelke: Users, service and.
>> Lukas Ruebbelke: we'll go ahead and, just export this as well.
[00:12:05] So we can actually carry this typing, all the way through.
>> Lukas Ruebbelke: the rest, of the application.
>> Lukas Ruebbelke: So, let's go ahead and, let's add this to our at module.
>> Lukas Ruebbelke: Now, again just following up on the barrel roll, is I can import this specifically, with kinda this fully qualified path name.
[00:12:33] So, I can go from here, shared/user service. And like this would, totally work, and I could add this, i could go here, like this. And then I can add this, into the providers. But, once you have four and five or whatever, Classes in the share directory, this starts to get tedious.
[00:13:01] And so, this is where, you know this or this is why this was created, is that we can just import this all in one single import, which is what Angular two is doing in a lot of places as well where import, for instance, component On in it et cetera, okay?
[00:13:25] And so now, that this is available to our application, how would we consume this? Let's, take. Let's go back, to our widgets.
>> Lukas Ruebbelke: Widgets Component, Here, well, first things first. I want a, user's collection, that is of type User.
>> Lukas Ruebbelke: And so now, I'm saying, I'm creating collection locally.
[00:14:04] So, again, this is just like I was doing in the service, same pattern. But now, we need to actually, inject this in here. So, let's go down here. Constructor. Private. userService. And we're not going to do, anything with this here. But, what we wanna do is, let's see if this is gonna give me.
[00:14:44] Nope. So let's go up here, and implement our, A Init. Well, now we have to.
>> Lukas Ruebbelke: So this.userservice, by using the access modifier in the constructor. It's, not a local number. Let's, actually go, this.users equals, users. Rather, so you see even how that even, It didn't like that.
[00:15:32] Okay. Then, if we go over here.
>> Lukas Ruebbelke: Where do, I wanna put this?
>> Lukas Ruebbelke: So, let's just put this at the top for a second. So, pre.
>> Lukas Ruebbelke: Users.
>> Lukas Ruebbelke: There we go.
>> Lukas Ruebbelke: And there we have it.
>> Lukas Ruebbelke: So, this was the magical mystery service tour of how to build a service in Angular 2.
>> Speaker 2: Dale's question.
>> Speaker 2: There's an answer, and then an opinion. I just wanna get your opinion on that.
>> Lukas Ruebbelke: So, you can import everything from Shared. I prefer to be explicit, as well as you may not want everything. And so that is, you may have five things in the barrel roll and you only want two.
[00:16:53] So, I prefer to be very explicit about that. Another example of this is, exactly. So, Andy nailed it. Is by using, really, kind of the dollar, like input dollar from, is you're gonna grab everything. And essentially, you are grabbing things you may not need. A really, really good example of this is Observables.
[00:17:14] I love Observables. They're amazing. But, you have essentially the Observable object, and then you have like 10,000 operators that you can then use. So, at one point, I think they've kind of made it harder to do. But, you could just say it like, import observable from rxjs. And the way you did it it would actually grab everything.
[00:17:34] And it was huge and cumbersome. And what would happen is you would basically import all of rxjs for five operators that you needed. And you didn't need the other 300 operators. And so, the way to do that with Observables is you import just the Observable and then you import the operators as you need.
[00:18:02] So, I think that really being explicit, one, is it is very self-documenting about what you are trying to accomplish, but it keeps you from, basically, importing things that you don't need. Yes?
>> Speaker 2: Okay, this is a different question. Jonas is asking Lukas, could you please explain why we declare services as private?
>> Lukas Ruebbelke: So, the reason why we are doing that in the Constructor is so that TypeScript will essentially assign. So, this is a TypeScript convention. It's called a constructor assignment, where it will essentially take usersService, and because it's private, it will assign essentially this.usersService = usersService, so that we can use it down here.
[00:18:54] So, what this does is it allows you, when you inject something, that it becomes then a local member. Yes?
>> Speaker 2: Is that a singleton UserService across the app? Or, is that new instance for every injected instance of it?
>> Lukas Ruebbelke: So, this is a very, very good question. So, the question is, is this a singleton service across the app?
[00:19:18] Or, is it one per instance? And the answer is neither.
>> Lukas Ruebbelke: But, it's actually somewhere in the middle. So, it's not a singleton in the sense that you can only create one. So, in Angular 1 everything was a singleton. You basically had a service factory that you would say, give me this thing.
[00:19:41] And if it already exists then just return it. And so, what will happen is, essentially, whatever's the last thing in, or rather, I beleive it's the first one, that's what you would get. Well, there was this collision because there was a key value that this key equals this service, and that was it.
[00:19:58] With Angular 2, every component has an injector. So, with Angular 1, it was just one injector for the entire application. With Angular 2, every component has an injector, which may be provided, basically, by its parent or its parent parent parent. The point is at the component level you can manually configure hierarchical dependency injection.
[00:20:26] So, what that means is you can simply say, for this component I want this UserService or this particular version of UserService. But, down here I want to use this particular version. And you can completely override the injector at the component level. And so, this is pretty super advanced stuff.
[00:20:55] Pascal Precht gives a pretty good explanation of this on his blog, Thoughtram. But, it's not a singleton because you can actually have more than one service depending on how you inject it. But, at the same time it's not going to create new one every single time. Instead, it will essentially, it'll be a single instance until you specifically tell it otherwise.
[00:21:22] So, the answer is no, or yes, until it's not.
>> Speaker 2: What was the reference you gave? Somebody's blog?
>> Lukas Ruebbelke: Yeah, so Pascal Precht. I think one of the top content creators around really, Angular 2, is Pascal. And he's another Angular GDE. And his blog is, I think it's blog.thoughtram.io.
[00:21:45] And one the most prolific Angular 2 bloggers. They have a ton of good articles. My two favorite ones are really about hierarchical dependency injection and change detection.
>> Lukas Ruebbelke: In fact, I will even, in my slides, put together some additional resources cuz I've realized, I know Tamara's keeping track of some of the things, but there's definitely some pretty good stuff out there.
[00:22:12] But, definitely check out what Pascal's doing. But, I think hierarchical dependency injection is kind of one of these rocket science edge cases that you're probably doing some pretty advanced stuff. And it's kind of the equivalent of when you go to a workshop. It's like, okay, if you're on Windows, do this.
[00:22:34] If you're on Mac, do this. If you're on Linux, you're on your own, cuz you're smart enough to figure it out yourself. And so I think, if you're doing hierarchical dependency injection, you're probably smart enough to figure out how to actually make that work.
>> Lukas Ruebbelke: Yeah, I agree.
[00:22:54] So, Chris. So, Pascal has a really great talk on change detection. And that's super helpful. Any other questions about services? Yes?
>> Speaker 4: I have one. So, basically, if you have a service in a parent component and you wanted to get data into a child component. So, say, for example, we had a table of data and you select one row.
[00:23:20] And you want that data then to go down into the child component-
>> Lukas Ruebbelke: Mm-hm.
>> Speaker 4: Is there a best practices recommendation on whether you would pass that row of data from parent to child? Or would you, in the child, write code to go back into the service and just get the individual record?
>> Lukas Ruebbelke: So, I spend an entire module tomorrow morning talking about this. But, I will answer that, without going too far into it. What I recommend is, unless you're doing a data mutation. So, in other words, I wanna select data I have a list of things. I want to select it and then I wanna take that and I wanna pass it into like a child component to be rendered.
[00:24:13] So how I would structure that and how I have actually structured it, because the scenario which you have described is this, right here. So at a high-level, what you have is container components and presentational components. So prior to this before the PC police got a hold of it, it was really smart and dumb components that you have a container component that is responsible for fetching data to satisfy the component.
[00:24:50] It then takes that data and splits it up into tiny pieces as needed, and then feeds that into its children components. And tomorrow when we get into inputs and outputs, what happens is you have a component that's for the most part, completely stateless. In other words, you pass it in data.
[00:25:08] It renders it. When something does something, it captures that event instead of handling it in that component, it basically emits it backup to the parent component to be handled. So here, when I select this item, what's happening is this item list is completely done. It's basically getting a list of items and it's saying, render this.
[00:25:32] And then when I select this item, it's capturing that click event and emitting it backup to the main items component. And say like, hey, this thing was selected. And so then what the items component does is say, we have a new selected item. Let's pass it into here.
[00:25:51] And so now, this item details component is now bound to that. And I suppose, let me just show you what this looks like, because I love talking about component driven architecture, but the caveat is I think it's hard to talk about component driven architecture before you talk about components.
[00:26:10] So, you have to understand how a component works before we can talk about how to put them together. With that said, I will show you Kind of a high-level, so you can start thinking about this. So you can see here, what do we have? Property binding. What are passing in?
[00:26:31] An items collection. So within the child component, it's just saying, I've got his collection NG four like let's loop it, let's go. And then here, when you select an item in the child component, it's basically admitting this back up on a selected event. You say, wait a second, selected?
[00:26:52] That's not a DOM event, you guys made that up. Well, maybe this. I'll think it is. Feature's definitely not, but what we're doing is we're now admitting custom events with custom data that we are handling in this top of a little component.
>> Lukas Ruebbelke: So items, selected item. Well, this is actually to look vaguely familiar with kind of what we're doing to widgets.
>> Lukas Ruebbelke: And so we have some interesting things here, because of the router and some kind of functionality, but what we're doing is here, selected item. Now if we go back here.
>> Lukas Ruebbelke: Actually, let's go to selectItem right here. When this selected event gets fired, well, it's calling this method and passing it here.
[00:27:54] So, the list is emitting a selected item. And so basically, this is just an extension of what we did in the previous exercise. And then so notice now, item. So, the details were binding item to selected item. So the minute this is selected over here, it's omitting the selected item.
[00:28:19] And so it's saying, here's the selected item, do what you want with it. And this becomes even more important like for deleted, because I wanna remove myself, but you wanna allow an item to remove itself from a collection. You want to actually defer that up to the parent.
[00:28:34] So it's saying like hey, like this item selected to mark for deletion like here's this information. And then the parent component or the container component say, okay, I know what to do here. I'll take this item and then I'll actually pass it on to the item service to be deleted from the actual server, but selected items.
[00:28:57] Once it's selected, we're basically sending that up via the event and we're setting it which then now sets it here. And if we make some change and we click Save. Well, it's saying, okay, save this item and here's the new item or basically here's the new value. So we'll talk more about this tomorrow, but that's the pattern.
[00:29:25] So container component, presentational or DOM components. We just stack them in all day long. Yes?
>> Speaker 5: Thomas has a question, you may have touched on it and I missed it, but when do you use the injectable on service?
>> Lukas Ruebbelke: Always. So you can get away with it sometimes, but the minute you are service has a dependencies that being injected in, then this will break.
[00:29:53] So you basically have to have correct metadata generated for things, components, services, whatever for Angular to know what to do with it. And so really, the best practice rule of thumb is anytime you create a service, just annotate it with injectable. You'll never regret it. If you don't, you may regret it.
>> Speaker 5: Next question from Richard, how do you do component binding across router outlet?
>> Lukas Ruebbelke: Component binding across router outlet.
>> Lukas Ruebbelke: So, I'm trying to think what that means generally. So going back to the container component, presentational component. I treat really like router outlet is being, like a fine-grained slot for your feature. And so, I would not try to do binding on the router outlet directive or rather component is I think that would be precarious.
[00:31:11] I think you only want to pass in fully formed components into the router outlet. And so, I've never actually even heard of any kind of like component binding done on the router outlet. Well, not only that, you don't actually have access to the internals of the router outlet.
[00:31:28] So, I'm not sure how much mileage you would actually get.
>> Lukas Ruebbelke: But if somebody knows of a good use case, that sounds like some mad scientist, rather I can. I probably shouldn't, but I'm going to kind of scenario there.