Check out a free preview of the full Angular 17+ Fundamentals course
The "Dependency Injection (DI)" Lesson is part of the full, Angular 17+ Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
Mark explains the concept of dependency injection in Angular and how Angular has implemented it on the frontend. The instructor demonstrates how to make a service injectable using the `@Injectable` decorator and how to provide the service at the root level using the `providedIn` property. They also show how to inject the service into a component using the `inject` function and how to use the injected service to fetch data and display it in the component's template. The instructor also mentions that if you don't want to provide the service at the root level, you can provide it at the component level instead.
Transcript from the "Dependency Injection (DI)" Lesson
[00:00:00]
>> One of the things that's really special about Angular is dependency injection. Now, here's dependency injection. DI is really a design pattern, all right? We didn't create dependency injection. This is not an Angular thing, this is a design pattern in computer science, and we've just implemented it on the front end using our tools.
[00:00:19]
So it's a mechanism for creating and delivering. Creating, so getting instances of things, and then delivering them, right, parts of the application to other parts. So earlier there were questions about, well, how do you get data around your application if you can't send it through the inputs, right?
[00:00:37]
If you don't want me to prop drill, what else can I do? This is a part of that story. But I like to take this up to say, creating and delivering. Create instances, make them available, create, deliver. Or you could also say create and share, right? I'm sharing this information some other place.
[00:00:55]
Those are my two ways to kind of think about dependency injection. All right, to make something injectable we have to use our decorators once again. Angular relies on decorators, right? Because remember, without our decorator, this thing is just a regular TypeScript class. Okay, so here's what we got, friends.
[00:01:15]
Friends at home, check this out. You got Injectable as your decorator and then you have this thing called providedIn: 'root'. What does that even mean? Okay, here's what providedIn: 'root' means. providedIn: 'root' means that it's available at the top of your application because with Angular's dependency injection, you can actually provide different places.
[00:01:35]
So meaning at the highest level is not the only place to provide something. A component could provide resources to its children and services to its children, and the scope could be only that. So we're saying this thing is providedIn: 'root', okay, at the root of the application. But you could also provide across application boundaries, meaning if you have multiple elements on the screen, when I say element, I mean an Angular element, which is kinda like a web component, you might say well, this one can provide to the other web components.
[00:02:07]
So you could do a lot, this thing is powerful, you have a lot of flexibility. Okay, so root means available to the entire application, all right? And then you can export class and it's a service, and in that service is business as usual. When I say business as usual, I mean making your API calls, fetching data, everything you normally would do.
[00:02:30]
Business as usual, okay? Here's how you make it available, though. So we just defined our car service, but how do I make it available in my application to other parts? Well, we have the inject function. Okay, inject means just inject it, bring it in, it creates an injection context.
[00:02:49]
And then that's a place where you can then ask Angular's dependency injection system for things, okay? And inject allows us to do that. But let's say that if you want to use it, you can also combine things like inject and then do your constructor too. And you can inject in the constructor.
[00:03:09]
So there are two ways to do this. Use the inject function. You can also use some other syntax for constructor injection. But let's jump into an example, because too much theory about dependency injection is just not as helpful as actually doing it, okay? So let's jump right into an example here.
[00:03:28]
So we got 11-dependency-injection, here we go. Close up. It's closing time. That's not Semisonic, is it?
>> It is.
>> Yeah, Semisonic. Yeah, I remember when that song came out is right around time that one and The Freshmen, by The Verve Pipe came out. And I remember both those songs, I was in high school when that stuff happened.
[00:03:59]
And I just remember singing both of those songs and it being super weird for people, cuz I can't sing. Okay, but what I can do is ng serve, so let's ng serve this one. Is it spelled properly? Still spelled properly, still a professional. We go to our user listing, here's where things are gonna get really fun here.
[00:04:19]
Again, check this out. First, we're gonna make that class injectable. How do we do that, how do we make it injectable? We gotta use the @Injectable decorator and do providedIn: 'root', so let's make that happen. All right, I'm not gonna go that way, I'll go this way, cuz it'll open up the wrong one, app and then app.component, user.service.
[00:04:46]
Pardon me, pardon my interruption here. Okay, make it @Injectable, import inject. Okay, and then the property we want, providedIn. Where do we want to provide it? Root, yeah, that's right. Whoever said that, that was so right, providedIn: 'root', which means it's available to our whole application. Now that it's available to our entire application here's what we can do now.
[00:05:24]
We can inject the service, so we use the inject function, we can inject the service, and then we can get the data. So let's do all of that. Let's do all that together. So we're in app.component. Okay, let's see, app.component, yes. First, we need to get inject. So we'll get that from @angular/core.
[00:05:56]
Okay, excellent. Then down here, we can call it userService. And so in our component body, we call it userService. Not @Injectable there, just the word inject, okay? And what do we want to inject? The user service. Let's go. So now we have an instance. Remember I said, creating and sharing, right?
[00:06:23]
So, we're asking Angular for an instance of our service. Here's a question that I'm sure some of you are thinking. When it creates that instance, is it like a singleton instance or is it a copy of the instance? In this case, it's actually a singleton, right? Meaning there's one instantiation of it and we shared that in the application, which is helpful.
[00:06:45]
But if you want to make copies of a service, you can provide them at the component level to the children and then it can also make a copy instead of doing the same shared instance. Okay, so you have that flexibility, friends. Awesome, so now that we have that, we're really almost there, we did this.
[00:07:03]
Shit, import userData. Sure, okay, so we got to add our userData property. Okay, let me if I need to slow down, I'm gonna go userData. This is gonna be an array of type User. But make sure you pull the right one from the same project can be an array of type User.
[00:07:26]
And it's gonna equal empty array for now. Okay, you got an empty array. And now in our constructor, Let's have some fun here. Let's do this, we'll say this.userService, and then we say getUserData. But guess what? UserData returns a promise, actually, returns a promise. So we use promises.then, okay, and then we can start to just get busy.
[00:08:06]
Okay, so when I gave you this code, I'm typing it out so that way we can have it together, right? But we can just say data, so I already gave this code so you can copy it from the readme. Okay, so at home friends, I'm typing this but you can copy the readme version, it still works.
[00:08:20]
So we got this,userData = data. Okay, so now we got that going. So,this is a promise because I wanted to simulate what would it be like to do an HTTP request cuz those usually return promises except for if you use fetch, you got to do the promise and then you got to do resolve.json.
[00:08:42]
So I didn't wanna do that extra step, Cuz I don't like it. All right, let's keep moving. So now, since we locked that in Let's just iterate over, take a user of userData, all right, track, I think user has a user.id, I think this one does, let's see.
[00:09:14]
What did I say, @for, sorry, there you go, yeah, I'm pretty sure there's a user.id on this one. Okay, is there a component for this? A user-info component, there sure is. So then we're just gonna say, that's rad. All right, and then we'll do a user-info component, so let's import a user-info component.
[00:09:41]
Yeah, from the same local directory, so ./user dash-info.component. This is the one earlier today when we were doing a user, this is a component that kept showing up and it was like what is this random data? This is where it came from, all right? And before we continue to reference it in our template, we do our imports, okay, and get UserInfoComponent.
[00:10:05]
Look at that, looking good. So app-user-info, what do you want? There has to be some type of property on here. Can you tell me? You're not gonna tell me? Okay, let's see, let's go in there. Is there an input?
>> Directions say it's called user.
>> Well, that's the data, we gotta set up our input then, Right?
[00:10:33]
So if we're gonna use it we got to set up the input, so let's do it. So we'll bring in our input and the only thing that's gonna change is we do input. And now we have default data. No, go ahead, ask your question, cuz somebody else probably has it.
[00:10:53]
Okay, so now we have a user there, but so we have some default data, right? That way it just doesn't matter. This is just default data, which is why it's a -1. So we have our input and we provided some default data there. Okay, excellent, so now we set up our user, we're gonna buy into it and then we'll take each user, From right here and then get each user.
[00:11:20]
Okay, let me just slow down for a second and let's all get on the same page. So let's go back to UserInfoComponent. Only thing I did here was I tagged the property with the input, I decorated it with the input decorator. Didn't change any other data in there, I just decorated it with the input decorator.
[00:11:39]
All right, rad. Okay, I'm going to go back. And then in here, I referenced the selector and then I had my property binding. And then I sent in my property of user, but user came from our user data list. Okay, what questions can I answer, friends? Yes, please.
[00:12:01]
>> Can we use async await for getUserData?
>> No, okay, so the question is can use async await for getUserData? You can't make a constructor async, you see? That's the problem, the constructor cannot be async, which makes sense because if you think about what asynchronous means, it means that something will happen at some point in the future.
[00:12:26]
You don't know when it's gonna be done finishing. When you instantiate an object, that construct is gonna be called. And whenever you do an object, this is just programming in general, you don't want your constructor to be too heavy, because you could be creating millions of these and you don't want it to be really slow.
[00:12:42]
So if you wanted to deal with async await, there's another way. Would you all like to see that? Give me a thumbs up. See async await version of this? Yeah, okay, here's another way to do it, we can implement, I think it's called OnInit, which is a lifecycle method for Angular, so let's see if we can get that from onInit.
[00:13:06]
Yeah, so this is one of our lifecycle methods that you can tap into. So what we can do instead, so let me first take that off, okay? Implements, okay, so that works. So now it's complaining that we have not implemented it, so let's make ngOnInit. So we do ngOnInit, I can make this async.
[00:13:28]
You see, this can become async. All right, what are you complaining about? No, I don't mean that. Let me finish the work and then we'll fix that error, cuz I don't really mean what they're asking me to do. So we could take this function and then we can say const data = await instead of the then, right, so we got that and we can take this right out, okay?
[00:14:01]
So I don't really want to return a promise void, but I think you just want me to, which is fine, I just won't fight you. Okay, fine, yeah, all right, sounds good. You don't need to do both. Okay, so this is the alternative way. So what is ngOnInit?
[00:14:18]
ngOnInit is a lifecycle hook and it has an order from the creation of a component when this lifecycle hook will run. Not the same hooks that you think about with React, right? React has state hooks and things like that, these are not the same. These are places in the lifecycle of the component from when it's first created until it dies or until it's destroyed.
[00:14:42]
It has a list of things you can tap into. One of the first ones that happens after the constructor is ngOnInit. So the component is being initialized. So you can make API calls there. That's actually the safest place to make API calls that are not really fast, right?
[00:15:00]
Fast, meaning if I had to make 50 of these components, we don't want the component to be hung up on instantiation with the constructor, right? This works asynchronously cuz the constructor is synchronous. So you can't block waiting, but you can asynchronously wait. So ngOnInit is where you can put your data, okay?
[00:15:19]
Good question. I like this update, we can keep it that way, totally works. So I think we're done with this example. Let's look at the screen, [SOUND] it's there. I mean, this one is not beautiful. Not beautiful, but, okay, yeah?
>> Where does root come from in providedIn, is it from app-root?
[00:15:44]
What would we put if we did not want the whole app?
>> Okay, good question, where does root come into play, that word in providedIn: 'root'? It's a really great question. Let's go back to, Our service. This right here, where does that come from? What would you do?
[00:16:02]
What would you do if you didn't want root? Okay, so what would you do here if you didn't want root to be a part of the story? You would provide this service at your component level, right? At the component level that you wanted it to be available from, you could provide it there.
[00:16:19]
Because the options at this level where you say providedIn is only platform and route. So meaning make it for the whole application or also make it for the cross application boundaries if you have multiple on the same page. But if you wanna control it where it doesn't show up to every component, then you provide it at the component level cuz components have more properties here.
[00:16:43]
So you can do, let me show you some stuff. You see, you have a lot of stuff, animations, imports, inputs, outputs, preserveWhitespaces, providers. Okay, so you can get involved, query schemas, styles. I mean, we have a lot of things. Okay, those are not properties, but you see kinda where we are that you can get even more involved here.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops