Building Awesome Web Apps with Angular 2 @Input and @Output Decorators
This course has been updated! We now recommend you take the Angular 9 Fundamentals course.
Transcript from the "@Input and @Output Decorators" Lesson
>> Lukas: I wanna skip to some code real quick. I feel like I've just dropped a bomb on everybody. Let's see this actually in some code, and let's talk through this. So we have an items list and we have some binding here. So we have property binding and we have event binding.
[00:00:22] But you'll notice that on the items, we have now a custom property on the element that we're binding to, and passing in the items collection. So that is an input, so we're saying, item list, here is your items.
>> Lukas: Then, at the same time, we have two events essentially coming out of this, selected and deleted.
[00:00:51] And from there, we are handling those via a callback, just as if it were a normal DOM event. So does that make sense? And because of this, we have a really clearly defined contract with our component. We know that this component has an input of items and an output of selected and deleted.
[00:01:15] And so, this is, you can think of, is an Angular one isolated scope, was essentially, the API for a directive. This is, in a sense, a contract or an API for your component. And so how this works is we use this, so remember when I said you can decorate a class, but you can also decorate methods and properties?
[00:01:41] This is where this comes in. So using the @Input decorator, we can decorate some value. So in this case, it is literally some value. And then, in the parent template, we can actually bind to that using property binding. And so whatever value is on the parent template, it now gets passed into the child template via input.
[00:02:11] And also, if you need to actually alias that input into something else, you can do that as well. Just something to point out, and so, here,
>> Lukas: We have MyComponent. What is the input on this?
>> Speaker 2: Greeting.
>> Speaker 3: Greeting.
>> Lukas: Greeting, yes, hello, greetings. And so now, you can see here that we have a property of Greeting and we are decorating it to say, this property is an input.
[00:02:50] And then you can see here, in the template we're just binding to that. So we're just treating it like any other property, we're simply saying this is an input. In other words, this is now a property that we can bind to, on from the parent component within the template.
[00:03:12] So here you can see that we're instantiating my-component, in the DOM. We have a property called greeting, and we're binding to it and passing it in. And so now, whatever value is on the parent component then gets passed in to the child component. So really, at the core, it's inputs.
[00:03:35] You're simply defining an avenue or a channel for some data to come into your component. So output is essentially an EventEmitter. So just as you have DOM events, what we're doing under the hood is we are creating our own custom events. And so what we do is we actually expose an EventEmitter, that we can then call.
[00:04:04] So here we have showValue, which is a new EventEmitter. And this is not quite right, let me just adjust this, there we go. And then when we call, show.Value Emit, so what this does is, it actually triggers the event. Then on the parent component, we can now bind to some value as if it were an event.
[00:04:36] So let's see this in code. So what we have here is a greeter output. And so, because of this, greeter is what? It's essentially an output or an event, that we can trigger from within our child component.
>> Lukas: And so from here, we have greeter, it's a new EventEmitter.
[00:05:12] So now it has the ability to emit events. And then within greet, so on click, and somebody actually clicks in the child component, it's calling the greet method which then calls emit on our output.
>> Lukas: And then on the parent component, because we have a greeter output, we can then capture that event when we call Emit from the child component.
[00:05:42] And really essentially, delegate control to the parent component and handle it. So now think about this, within our items feature, we have a list of Items. So when we want to send an items into the items list, well how do we do that? We create an input of items and bind to it.
[00:06:08] Now when somebody selects an item, we want to capture that. How do we do that? Well we create a selected output. Or if somebody actually wants to delete an item, we definitely want to delegate that up to the parent that actually owns the collection. How do we do that?
[00:06:26] Well, we create a deleted output. And so this kind of paves the way for something that looks like this. You have a container component that has all these smaller components, that are simply responsible for presenting data and capturing input. Which leads us to a incredibly important concept of container and presentational components.
[00:06:57] So the idea is that you have a high level container component that's responsible for consuming data, and handling any kind of logic for any kind of an event that is surfaced from the child component. It captures that and then delegates it to the appropriate service. The container components, they essentially know state and how to persist these changes.
[00:07:24] On the other hand, presentational components, they're fully defined by their bindings in so much that they are stateless. They are simply responsible for binding data in and sending data out via their bindings. And the idea is that you want to create as few container components as possible. And you want as many presentational components as possible, yes?
>> Speaker 2: Hans is making a request for you to expand on bindings? Property binding, event binding, and CSS style binding.
>> Lukas: So I tell you what, let me finish this module and then I'll circle back around on that. So here we have a presentational component.
>> Lukas: And what do we have here?
[00:08:17] How many inputs do we have?
>> Speaker 2: One.
>> Lukas: One, which is?
>> Speaker 2: We have items, right?
>> Lukas: Items, how many outputs do we have?
>> Speaker 3: Two.
>> Lukas: Which are?
>> Speaker 2: Selected and deleted.
>> Lukas: Yes now, here's the interesting thing. This is,
>> Lukas: The entirety of this component class, this is it.
[00:08:47] The reason being, because unless Angular is broken, data's gonna come in, go right into its template. And when an event happens, it's going to happen, essentially, right through the output, back to the parent component. So, let me actually pull this up.
>> Lukas: Step in the code here.
>> Lukas: So this is, in fact, a real component.
[00:09:32] And what you have here,
>> Lukas: If we go to items, this is what we're looking at. So obviously, visually it does things, but the component class itself,
>> Lukas: Is completely stateless.
>> Lukas: Yes?
>> Speaker 2: Richard is asking, when I load a component with input and output into router outlet, how do I bind those property event bindings?
>> Lukas: So, I believe that you can prefill that data to do that. So generally, I do not. So container components do not have inputs and outputs. So architecturally for me, is that I load container components via the router outlet. And then from there I load the presentational components within that.
[00:10:27] So I've never needed to actually satisfy inputs and outputs via the router.
>> Lukas: Now I believe you can resolve data into a component from the router, and I think you could do it that way. Cuz there may be a case where you wanna have, kind of a dump component, if you will, loaded to the dump, but possibly loaded into the router.
[00:10:53] But in all honesty, I do container components via the router and then I use my presentational components within that. So, I wish I had a more definitive answer, but architecturally I've never went that way yet, okay? So looking at this ItemsListComponent, here's a really important question. How do we test this?
[00:11:26] Let me ask you a better question. What do you test?
>> Lukas: There's no logic in here. It's stateless, it's completely and simply a conduit for state and events. And so now, essentially, we have a presentational component that doesn't do anything, other than display whatever you put into it and emit any event that happens to it.
[00:12:00] And so for the most part, I'm sure there are probably edge cases, but this is almost an unbreakable component. But you certainly don't have to worry about any internal logic going wrong here. And so the biggest problem with apps is complexity. The biggest cause of complexity is state.
[00:12:21] And so what you wanna do is reduce your state footprint in your application, and this is how we do this. And so this is why we're moving away from MVC more into this component, unidirectional data flow architecture. Because we have a container component, that essentially will grab the data that we need.
[00:12:44] And then from there, we pass it off to these presentational components, that essentially just render whatever you send it. It looks like you have a question for me, Tamara.
>> Speaker 2: I do, it's also being answered in the chat room.
>> Lukas: Okay.
>> Speaker 2: Macy's asking, is this example when we click, we emit selected item?
[00:13:07] But in parent component template, we fire select function with a dollar event argument. Where is the item we clicked?
>> Lukas: So the item you click is, if you go into the template, so notice we have a selected, deleted, and featured output. So this is a good question, where is this happening pray tell, I need to know?
[00:13:35] So now, if you look here,
>> Lukas: When we click this, in my previous example, I actually had click, that called the method, that then internally to the component class. But from here, I'm just grabbing the output and calling emit right on in it. So, what about delete? Same thing, deleted.emit, or feature, for the star if you want to set a feature, Featured.emit.
[00:14:20] When you look here on ngFor, items, well where's that coming from?
>> Lukas: Here. And so we're saying this component has an input of items, and it has three outputs. Or just think of outputs as custom events, that it can emit three custom events that we can then listen to and,
>> Lukas: You can then listen to and capture. So if we go to the parent component, so remember the surface area between a parent component and child component is what? The template, DOM. So we can expect that because we have one input and three outputs, that in the parent component,
>> Lukas: What do we have here?
>> Lukas: One input, so we're passing in items to this component. And we have three outputs or three events, that we can then capture and respond to.
>> Lukas: So let's try another one, this component right here.
>> Lukas: How many inputs do we have?
>> Speaker 2: One.
>> Lukas: One, and what is it?
>> Speaker 2: Item.
>> Lukas: Item. And so essentially we're saying, here, item-detail, set your item to selectedItem. So as selectedItem is changed, so is the item internal to the child component. And that's where the property binding comes in. It's now on the parent component, whenever this changes then we reflect it in the child component, as well as, we have a saved and cancelled output or event.
[00:16:39] And so, let's just dial this into the actual application here. So when I click this, right here, what is going to happen?
>> Speaker 3: Fill out the form on the right.
>> Lukas: Yes, but the first step is we're registering click on the list component. From there I am using this to call the selected event, I'm actually omitting a selected event.
[00:17:11] So here when I do this, internally, I'm saying when I click this, I'm saying selected.emit, here's my item. And then I'm capturing this, and then in the component itself, let's go down here.
>> Lukas: I'm then calling it, I'm calling selectItem and setting that item, whatever it is, to selectedItem.
>> Lukas: And then from there,
>> Lukas: That selectedItem updates and it now re-renders the child component. Yes, exactly right, Hans. So now if we go here,
>> Lukas: And we do this, so just keep in mind these are, this one especially is totally stateless. I click it, it creates a selected event, I capture, I pull the item off of that.
[00:18:17] I've set it to selected item, which then uses that to populate the detail component. And if I go here, new selected event, selected event, selected event, selected event. Or, if I do for instance feature, I can do this and then it does the featured stuff that it does.
[00:18:45] So I actually, I set it to feature and it triggers essentially the child lock for that. Or I can set this back. So the point is here, that this list component is really just, it's there for presentation only. And so what we have is,
>> Lukas: Really just a dumb component, that lives inside of a presentational, or rather a container component.
[00:19:24] Which is, in this case, items.
>> Lukas: And so what we have here is a component that's responsible for getting the items from the service, setting the selected item, and actually managing state for all of the child components.