Angular Input Output - Component communication explained
If you have ever struggled with Angular Input and Output then this is the article for you! In a lot of scenarios, the components of your Angular application need to communicate with each other. In Angular this can be done in a couple of different ways like Angular services, accessing instances of components through the template (this is not recommended unless there is a very specific reason), route parameters, resolvers, and the @Input and @Output decorators.
Table of content:
When to use the Angular @Input decorator
The Angular @Input and @Output decorators are used when there is a parent-child relationship between components that have a need to share data. The @Input decorator is used when a child component needs to receive values from its parent component as shown in the image below:
The @Input decorator is declared inside the component that needs to receive values, thus the child component. A component can be considered a child of another component when it is used inside the HTML template of another component.
Let's say you have 2 components, the app.component.ts, and the child.component.ts. When you declare the HTML selector of the child.component.ts inside the HTML template of your app.component it is considered a child of app.component.ts. To use the @Input decorator you need to add it to the child component and pass it a value from the parent component. Let's have a look at what we need to do to add the input decorator to the child component.
The first thing we need to do when using the Angular @Input decorator is to import Input from '@angular/core', next we can start to declare inputs in the component. We declare the decorator by writing @Input followed by a variable name (and if you like a type as we did in the example). If you need your @Input to have a default value even if nothing is passed to it by the parent you can assign it a value like this:
@Input() count: number = 0;
The input property can be used inside the child component like any other component property. It can be used in the TypeScript file or in the HTML template. You can add as many input properties to a component as needed. In our example, the input has a type of 'number', but you can assign any other type to the input property including custom classes and interfaces.
Now let's have a look at the parent component (in our example this will be the app.component.ts) and how we can pass a value to the input of the child component. Inside the HTML template of the parent, we declare the HTML tag of the child component. To pass a value to the input of the child we declare square brackets with the name of the input between them followed by a value assignment.
We named our input in the child component 'count' so we use it by adding [count] to the HTML tag and passing it a value. The value can be a property in your TS file or a method call that returns your value. For our example, we made a property and assigned it a value of 100:
When you declare the HTML tag of an element that has input properties, you are not required to pass values to every input of the component, you can use none of them, all of them, or anything in between.
Listen for new Angular @Input values
In components where you declare Angular @Input decorators, you can listen for new input values with the ngOnChanges lifecycle hook. This is a great way to perform some extra logic when one of your inputs receives a new value. You can declare a SimpleChanges property within the lifecycle hook to receive the changed values. Inside this object, you will find the previous and current value of each input property that has changed. So if you have multiple properties declared and only one receives a new value, only this one property will be included in the SimpleChanges object.
Below is an example of how to handle input changes with the ngOnChanges lifecycle hook:
Using Angular @Input with a setter and getter
Another way to perform some extra logic when an input receives a value is by declaring the input with a setter and getter. The setter is linked to the input and can be used to perform logic when a new value is set, the getter is used to read the value as if you are using a normal input property. This requires a bit more boilerplate but can be useful in some scenarios.
Sending data from child to parent with Angular @Output
In many cases, you do not only need to send data from a parent component to a child component but also be able to receive values in the parent component from the child component. You can do this by using the Angular @output decorator. The @output decorator is used to emit events to the parent and in these events, you can include anything as data, this data can then be used by the parent component.
The @Output decorator is declared on the child component and acts as a doorway to sending data to the parent component through events. In the parent component where you declare the HTML tag of the child component you can listen for output y declaring it on the HTML tag like you would with any other browser function like click, mouseOver, keyPress, and so on.
Let's first have a look at the child component. To use the @Output decorator we need to import it from '@angular/core', we also need to import the EventEmitter also from '@angular/core'. Now we declare the output like this:
@Output() numberOutputEvent = new EventEmitter<number>();
In our example we will have a simple output, that sends a number to the parent component. On the left hand, we have the @Output decorator, and next to it a variable name we use for the output, on the ride side of the equal sign we assign the event emitter and a type we emit with it (in our case number). Just as with the input you are not limited to outputting a number, you can output any type, class, or interface you desire.
After you declare the @Output decorator you can use it to emit values (in our case a number). You can do this by calling the emit method of the EventEmitter like this:
After adding these changes the child component looks something like this:
In the example above we emit an event every time our input receives a new value (this is a bit of a nonsense example, but it illustrates how everything works). We output the received value + 1.
Now let's see what we need to do in the parent component to receive this value. As mentioned before we can listen to Angular @Output events similar to how we would listen to click events. We add the name of the output between round brackets on the HTML tag of the child component and act upon it when it is triggered. Just as with a click event or any other browser event we can receive the event data by declaring a $event parameter.
Now every time the child components emit the numberOutputEvent, the parent can listen to it and act accordingly.
The Angular @Input and @Output decorators are a great way to enable component communication. When there is a parent, child relationship it is the preferred way of communication between the components. An example would be a grid component receiving grid data and sending out events on clicks and changes in the grid, a chart component receiving chart data, and an input component receiving a value and outputting the changes, the options are endless. The input is easy to declare and the output is similar to all other browser events in Angular. For listening to changes in your input properties use the ngOnChanges lifecycle hook.