Angular Parent Child Component Communication With Input and Output Decorator
Angular Parent Child Component Communication With Input and Output Decorator

In this video you will learn how Angular parent and child components can communicate with each other. We are using input decorator and output decorator to describe Angular component communication. On the real example you will see how we can pass data from parent component to our child component and how thorough events child component can notify parent about some changes.

Until now we had only app.component and usersList component. But let's imagine the big project with at least 20-30 components on the page. Maybe we have a feed and top menu, some elements inside feed, popular tags, pagination, tag list in each post and much more. All components inside Angular a looking as a tree. Everything is started with app.component and then we have child inside child inside child. The question is now how can we pass data between them? Because at some point you want to notify your parent component that user clicked somewhere or you want to pass data from parent component inside child component.

For now we have users inside our usersList component and we have all logic there. It's not bad but just imagine that we want to reuse usersList in different places with different data. At first place it should be the list of new users, in second the list of followed users and so on. So first of all we want to make out usersList configurable. So that we can give users data from outside and it just manage the logic of rendering data inside.

First let's move users outside of usersList and in it's parent - app.component.

export class AppComponent {
  users = [
    {
      id: '1',
      name: 'User 1',
      age: 25,
    },
    {
      id: '2',
      name: 'User 2',
      age: 27,
    },
    {
      id: '3',
      name: 'User 3',
      age: 30,
    },
  ]
}

Now we need to create an @Input. This is a special angular syntax to pass variables to the component.

export class UsersListComponent {
  @Input() users
  ...
}

So here we said that we are waiting to get users property from outside.

Now we just need to pass users to out usersList component.

<app-users-list [users]="users"></app-users-list>

As you can see we used square bracket to pass users property. The value inside square brackets is exactly the input name as it is in component. The property on the right can be anything because it's just a variable from app.component.

Now in browser everything is working as before. But we don't have users array inside usersList component which means that it's now much more flexible. We can pass any users array from outside and this component will render it.

But there is a problem now with our usersList component. As you can see we still have create and remove logic inside our component. Why it is bad? First of all in can be that remove is not just a remove from array but an API request to delete this element in database. Second thing is that we modify users array from parent inside child. It means that it will be much difficult to debug this code because if you pass it in child then in child of child several times and then modifies you will have hard time of guessing where did this change happened.

This is why would be nice to move remove and add outside of the usersList. Because we have no clue how exactly our parent component want to implement this logic. So what we want to do is just send an event to the parent that user did something. And parent must decide what to do.

Let's start with remove first. We want to tell parent that user clicked remove on an item with the id of the user.

export class UsersListComponent {
  @Output() removeUser = new EventEmitter()
}

So here we added an output with name removeUserEvent and assigned there new EventEmitter. It's a special thing from Angular which helps us to send events to the parent.

Now let's move removeUser logic to app.component. Because parent should know what it wants to do with data.

removeUser(id: string): void {
  this.users = this.users.filter((user) => user.id !== id)
}

Now we need to bind our output to the function removeUser in parent

<app-users-list
  [users]="users"
  (removeUser)="removeUser($event)"
></app-users-list>

So here we have round brackets because it's an event and we also have this $event from Angular. We need it here to get an argument that we passed in our child.

Now we only need to call our output inside child.

<span (click)="removeUser.emit(user.id)">Remove</span>

As you can see we are calling removeUser in child differently because it's not just a function but an eventEmitter. So this is why here is .emit. And we passed user.id inside so parent knows what we want to delete.

As you can see in browser everything is working as previously but now our usersList doesn't know anything about specific remove implementation and just emits the value for parent.

The same logic we can do with addUser. We simply give the name outside and parent has it's own logic of creating a user. Maybe it will be just push but it can be also a API call which saves the data to database.

usersList.component.ts

addUser(): void {
  this.addUserEvent.emit(this.newUserName)
  this.newUserName = ''
}
<app-users-list
  [users]="users"
  (removeUser)="removeUser($event)"
  (addUserEvent)="addUser($event)"
></app-users-list>

app.component.ts

addUser(userName: string): void {
  const uniqueId = Math.random().toString(16)
  const newUser = {
    id: uniqueId,
    name: userName,
    age: 30,
  }
  this.users.push(newUser)
}

As you can see in browser, everything is working as previously. But now our usersList component is completely reusable.

In this video you learned on the real example how parent and child components can communicate in Angular.

If angular for beginners is too easy for you or you want more advanced stuff check my 14 hours Angular course where we create a real project from beginning to the end. Link is also in the description.

📚 Source code of what we've done