logo
Published on

Understanding Component Communication in Angular 17 using the “props” analogy

web-development
Authors

In this short discussion, I talk about the component communication mechanism in Angular by thinking from a React Props perspective.

First, What are Props in React?

In React, props (short for properties) are used to pass data from parent components to child components. Here’s a simple example:

// REACT parent to child communication example
function ChildComponent(props) {
  return <h1>Hello, {props.name}</h1>;
}

function ParentComponent() {
  return <ChildComponent name="Angular" />;
}
  • In this example, the ParentComponent passes the prop name to the ChildComponent.
  • For most use cases, this is usually considered a good practice since only the parent is allowed to fetch or mutate data.

Hint: This one-way data flow helps maintain predictability and consistency in the application. However, it may not suffice for all use cases, especially when the child component needs to communicate back to the parent or needs to share data with sibling components.

Parent to child communication

Angular’s Input Decorator

Angular uses a similar concept to React Props called @Input decorator to pass data from parent to child components. Let’s see how we can implement the above example in Angular:

// ANGULA parent to child communication example
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<h1>Hello, {{name}}</h1>`,
})
export class ChildComponent {
  @Input() name: string;
}

@Component({
  selector: 'app-parent',
  template: `<app-child [name]="'Angular'"></app-child>`,
})
export class ParentComponent {}

  • In this Angular example, the ParentComponent passes the name to the ChildComponent using the @Input decorator.

Child to Parent communication

Angular’s Output Decorator

Angular provides and @output decorator to allow child components to relay back data and events to their parents. Why do we need this you ask?

  • Say, you are building a reusable Table Component which contains a list of action buttons for each row. Each row is implemented as a custom card component to ensure consistent style and behavior.
  • Each time a button is clicked, you would want to know which button is clicked and take the appropriate action e.g. Send an API request to update and item
  • If you are using a one-way communication scheme, this action can only be fulfilled by the parent component, hence you need a way for the Row component to send information to the Table Component
// ANGULA child to parent communication example
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-action',
  template: `<button (click)="onButtonClick()">Update me!</button>`,
})
export class ActionComponent {
  @Output() buttonClick = new EventEmitter();

  onButtonClick() {
    this.buttonClick.emit('Button was clicked!');
  }
}

@Component({
  selector: 'app-table',
  template: `
    <table>
      <tr>
        <th>Cloud Provider</th>
        <th>Owner</th>
        <th>Actions</th>
      </tr>
      <tr>
        <td>Azure</td>
        <td>John Doe</td>
        <td><app-action (buttonClick)="onCardButtonClick($event)"></app-action></td>
      </tr>
      <!-- More rows... -->
    </table>
  `,
})
export class TableComponent {
  onCardButtonClick(event: string) {
    console.log(`Busy processing event: {event}`)
    // Send an API request to update an item
  }
}
  • In this example, the Action Component emits an event whenever its button is clicked. The Table Component listens for this event and reacts accordingly.
  • Similarly, in react, you use callback functions to allow the child components to communicate events back to their parents. The parent must provide a handler function as a prop when implementing the component, for example:
// REACT child to parent communication example
function ChildComponent(props) {
  return <button onClick={props.onButtonClick}>Update me!</button>;
}

function ParentComponent() {
  function onButtonClick() {
    console.log('Button was clicked!');
    // Send an API request to update an item
  }

  return <ChildComponent onButtonClick={onButtonClick} />;
}

Should you always use One-Way Data Flows?

  • One-way data flows ensure that the state within a component always remains consistent and predictable. However, it can lead to prop drilling where you have to pass down props through multiple layers of components which can become harder to maintain or even cause inefficiencies by re-rendering intermediate components unnecessarily.

  • On the other hand, allowing child components to fetch their data can make components more reusable and easier to refactor. However, it can lead to an inconsistent state if not managed properly.

  • At it is usually the case, the best approach will depend on the unique requirements of your app. If you are unsure, here are some generally recommended practices:

    • Start with one-way data flows and refactor to state-management libraries like Redux if prop-drilling starts becoming a problem
    • If reusability and ease of refactoring are more important, allowing child components to fetch their data could be beneficial.
    • Refactor as Needed: As your application grows and changes, don’t be afraid to refactor your components and their data flow. What worked well for your application in the early stages might not be the best fit as it scales.

Closing thoughts

As we can see, both React and Angular provide mechanisms to pass data from parent components to child components. While the syntax and implementation details differ, the underlying concept is the same.


Which syntax do you prefer? Let us know in the comments below!