# Component Communication

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Core Angular Concepts
Source: https://www.devtutorials.ai/learn/angular/core-concepts/component-communication

> How parent and child components exchange data: @Input() for data going down, @Output() with EventEmitter for events going up, two-way binding between components, and best practices.

## What Is Component Communication?

**Component communication** is how components that are placed inside each other exchange data and events.

Data flows in two directions:

- **Parent to child.** The parent passes data down to the child through an **input**.
- **Child to parent.** The child sends a notification up to the parent through an **output** event.

The parent owns the data. The child displays it and reports user actions, and the parent decides how to change the data.

### Real-World Example

In a product catalog, the `ProductList` parent loads products from the API and passes each one to a `ProductCard` child through an input.

When the user clicks **Add to cart** on a card, the child emits an `addToCart` event with the product id. The parent receives it and updates the cart, which the header then displays.

<figure className="ngc-fig">
<svg viewBox="0 0 640 240" role="img" aria-label="The parent ProductList passes product data down to the child ProductCard through an input, and the child sends an addToCart event up through an output.">
<defs><marker id="ngc-e2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-node ngc-solid c-red"><rect x="200" y="14" width="240" height="58" rx="9" /><text x="320.0" y="41" textAnchor="middle" style={{fontSize:"16px"}}>ProductList</text><text className="ngc-sub" x="320.0" y="59" textAnchor="middle">parent: owns the data</text></g><g className="ngc-node ngc-soft c-blue"><rect x="200" y="170" width="240" height="58" rx="9" /><text x="320.0" y="197" textAnchor="middle" style={{fontSize:"16px"}}>ProductCard</text><text className="ngc-sub" x="320.0" y="215" textAnchor="middle">child: displays and reports</text></g><path className="ngc-link ngc-flow c-green" d="M260 74 C 230 110, 230 130, 260 168" markerEnd="url(#ngc-e2)" style={{fill:"none"}} /><path className="ngc-link ngc-flow c-orange" d="M380 168 C 410 130, 410 110, 380 74" markerEnd="url(#ngc-e2)" style={{fill:"none"}} /><text className="ngc-title c-green" x="120" y="118" textAnchor="middle" style={{fontSize:"13px"}}>[product]="p"</text><text className="ngc-note" x="120" y="138" textAnchor="middle">@Input(): data goes down</text><text className="ngc-title c-orange" x="520" y="118" textAnchor="middle" style={{fontSize:"13px"}}>(addToCart)="add($event)"</text><text className="ngc-note" x="520" y="138" textAnchor="middle">@Output(): events go up</text>
</svg>
<figcaption>Inputs go down. Events go up.</figcaption>
</figure>

## Parent-to-Child Communication

**Parent-to-child communication** sends data from a parent component into a child component.

<figure className="ngc-fig">
<svg viewBox="0 0 640 214" role="img" aria-label="Parent passes a value with property binding; the child receives it in an @Input property.">
<defs><marker id="ngc-c1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="200" y="10" width="240" height="58" rx="9" /><text x="320.0" y="36" textAnchor="middle" style={{fontSize:"14px"}}>Parent (ProductList)</text><text className="ngc-sub" x="320.0" y="54" textAnchor="middle">[product]="selected"</text></g></g><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-purple"><rect x="200" y="140" width="240" height="58" rx="9" /><text x="320.0" y="166" textAnchor="middle" style={{fontSize:"14px"}}>Child (ProductCard)</text><text className="ngc-sub" x="320.0" y="184" textAnchor="middle">@Input() product</text></g></g><path className="ngc-link ngc-flow c-blue" d="M320 72 L320 136" style={{fill:"none"}} /><circle className="ngc-packet c-blue" r="5"><animateMotion dur="1.6s" repeatCount="indefinite" path="M320 72 L320 136" /></circle><text className="ngc-note" x="340" y="110">data flows down</text>
</svg>
<figcaption>Parent to child: a property binding sends data into an @Input().</figcaption>
</figure>

The child declares which values it accepts. The parent sets those values in its template with property binding, the same `[ ]` syntax you use on HTML elements.

```angular runnable title="The parent passes the signed-in user to a badge"
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-user-badge',
  template: `<span class="badge">👤 {{ name }}</span>`,
  styles: `.badge { padding: 3px 10px; border-radius: 12px; background: #e0e7ff; }`,
})
export class UserBadge {
  @Input() name = '';
}

@Component({
  selector: 'app-root',
  imports: [UserBadge],
  template: `
    <p>Signed in as <app-user-badge [name]="currentUser" /></p>
    <button (click)="currentUser = 'Ravi'">Switch user</button>
  `,
})
export class App {
  currentUser = 'Asha';
}
```

Click **Switch user**. The parent changes `currentUser`, and Angular passes the new value to the child, which updates its view.

## `@Input()`

`@Input()` is a decorator that marks a property of the child class as an **input**. Only input properties can receive values from a parent's template.

```ts
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-user-badge',
  template: `<span>👤 {{ name }}</span>`,
})
export class UserBadge {
  @Input() name = '';
}
```

You can configure an input:

- `@Input({ required: true })` makes Angular report a build error when the parent forgets to set it.
- `@Input({ alias: 'userName' })` lets the parent use a different attribute name than the property name.
- A default value, such as `= ''`, is used until the parent sets one.

Newer Angular code can also declare inputs with the `input()` function, which returns a signal. The practice editors on this site run Angular without a build step, so they use the `@Input()` decorator. Both forms work the same way from the parent's point of view.

## Passing Data with `@Input()`

An input can receive any value: text, numbers, booleans, arrays or objects. Passing a whole object keeps the parent template short when the child needs several fields.

```angular runnable title="The parent passes product objects to a reusable card"
import { Component, Input } from '@angular/core';

interface Product {
  name: string;
  price: number;
  inStock: boolean;
}

@Component({
  selector: 'app-product-card',
  template: `
    <div class="card">
      <strong>{{ product.name }}</strong>
      <span>₹{{ product.price }}</span>
      <span>{{ product.inStock ? 'In stock' : 'Sold out' }}</span>
      @if (featured) { <em>Featured</em> }
    </div>
  `,
  styles: `.card { display: flex; gap: 12px; padding: 6px 10px; margin: 4px 0; border: 1px solid #d1d5db; border-radius: 8px; }`,
})
export class ProductCard {
  @Input({ required: true }) product!: Product;
  @Input() featured = false;
}

@Component({
  selector: 'app-root',
  imports: [ProductCard],
  template: `
    @for (p of products; track p.name; let first = $first) {
      <app-product-card [product]="p" [featured]="first" />
    }
  `,
})
export class App {
  products: Product[] = [
    { name: 'Mechanical keyboard', price: 4999, inStock: true },
    { name: 'USB-C hub', price: 1899, inStock: false },
    { name: '27" monitor', price: 18499, inStock: true },
  ];
}
```

The first card receives `featured = true` from the loop variable `$first`. The other cards keep the default `false`.

## Child-to-Parent Communication

**Child-to-parent communication** sends a notification from a child component up to its parent, usually because the user did something inside the child.

<figure className="ngc-fig">
<svg viewBox="0 0 640 214" role="img" aria-label="The child emits an event with @Output; the parent listens with event binding and runs a method.">
<defs><marker id="ngc-c2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="200" y="10" width="240" height="58" rx="9" /><text x="320.0" y="36" textAnchor="middle" style={{fontSize:"14px"}}>Parent (Cart)</text><text className="ngc-sub" x="320.0" y="54" textAnchor="middle">(added)="addToCart($event)"</text></g></g><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-purple"><rect x="200" y="140" width="240" height="58" rx="9" /><text x="320.0" y="166" textAnchor="middle" style={{fontSize:"14px"}}>Child (ProductCard)</text><text className="ngc-sub" x="320.0" y="184" textAnchor="middle">@Output() added.emit(product)</text></g></g><path className="ngc-link ngc-flow c-green" d="M320 136 L320 72" style={{fill:"none"}} /><circle className="ngc-packet c-green" r="5"><animateMotion dur="1.6s" repeatCount="indefinite" path="M320 136 L320 72" /></circle><text className="ngc-note" x="340" y="110">events flow up</text>
</svg>
<figcaption>Child to parent: the child emits an event, and the parent listens to it.</figcaption>
</figure>

The child does not change the parent's data directly. It **emits an event**, optionally with a value. The parent listens with event binding, the same `( )` syntax you use for DOM events:

```html
<!-- parent template -->
<app-rating (rated)="saveRating($event)" />
```

`$event` holds the value the child emitted.

## `@Output()`

`@Output()` is a decorator that marks a child property as an **output**: an event that the parent can listen to.

The property must hold an `EventEmitter`. The child calls its `emit()` method to send the event.

```angular runnable title="The child reports a click, and the parent counts it"
import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-like-button',
  template: `<button (click)="liked.emit()">👍 Like</button>`,
})
export class LikeButton {
  @Output() liked = new EventEmitter<void>();
}

@Component({
  selector: 'app-root',
  imports: [LikeButton],
  template: `
    <app-like-button (liked)="likes = likes + 1" />
    <p>{{ likes }} people like this post</p>
  `,
})
export class App {
  likes = 12;
}
```

Do not give an output the name of a native DOM event, such as `click` or `change`. The parent would receive both your event and the browser's event under the same name. Use a descriptive name, such as `liked`.

## `EventEmitter`

`EventEmitter` is the Angular class that sends output events. Its type parameter describes the value the event carries:

- `EventEmitter<void>` sends an event without a value.
- `EventEmitter<number>` sends a number, for example a rating.
- `EventEmitter<Product>` sends an object.

`emit(value)` sends the event. Every parent handler bound to the output receives `value` as `$event`.

```angular runnable title="The child emits ratings, and the parent stores them"
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-star-rating',
  template: `
    <span>{{ label }}: </span>
    @for (star of [1, 2, 3, 4, 5]; track star) {
      <button (click)="rated.emit(star)">{{ star }}★</button>
    }
  `,
})
export class StarRating {
  @Input() label = 'Rate';
  @Output() rated = new EventEmitter<number>();
}

@Component({
  selector: 'app-root',
  imports: [StarRating],
  template: `
    <app-star-rating label="Delivery" (rated)="onRated('Delivery', $event)" />
    <app-star-rating label="Product" (rated)="onRated('Product', $event)" />
    <p>{{ summary }}</p>
  `,
})
export class App {
  summary = 'No ratings yet';

  onRated(area: string, stars: number) {
    this.summary = area + ' rated ' + stars + ' out of 5';
  }
}
```

The same `StarRating` component is used twice. The parent knows which one was clicked because each binding passes its own label.

| Point | `@Input()` | `@Output()` |
|---|---|---|
| Direction | Parent → child | Child → parent |
| Parent syntax | `[name]="value"` | `(name)="handler($event)"` |
| Child property holds | A value | An `EventEmitter` |
| Child sends with | Nothing. It only receives | `this.name.emit(value)` |
| Typical use | Data to display | User actions inside the child |

## Two-Way Binding Between Components

When a child has an input `x` and an output named `xChange`, the parent can use two-way binding: `[(x)]="value"`. This is the same pattern `ngModel` uses.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="Banana-in-a-box syntax combines an input named size and an output named sizeChange.">
<defs><marker id="ngc-c3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>[(size)]="fontSize"</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">in the parent</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-c3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>[size]</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">value goes down</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-c3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>(sizeChange)</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">new value comes up</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-c3)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>fontSize updated</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">both stay in sync</text></g></g><text className="ngc-note" x="320" y="128" textAnchor="middle">[( )] is a short form for one @Input() and one @Output() named ...Change.</text>
</svg>
<figcaption>Two-way binding = property binding + event binding.</figcaption>
</figure>

```angular runnable title="An input and an xChange output enable [(x)]"
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="change(-1)">−</button>
    <strong> {{ count }} </strong>
    <button (click)="change(1)">+</button>
  `,
})
export class Counter {
  @Input() count = 0;
  @Output() countChange = new EventEmitter<number>();

  change(step: number) {
    this.countChange.emit(this.count + step);
  }
}

@Component({
  selector: 'app-root',
  imports: [Counter],
  template: `
    <p>Tickets: <app-counter [(count)]="tickets" /></p>
    <p>Total: ₹{{ tickets * 250 }}</p>
  `,
})
export class App {
  tickets = 2;
}
```

## Component Communication Best Practices

- **Keep data ownership in one place.** The parent owns and changes the data. The child receives it through inputs and reports changes through outputs.
- **Do not change input objects inside the child.** Emit an event and let the parent create the updated object.
- **Name outputs after what happened**, such as `rated`, `deleted` or `addToCart`. Do not name them after a handler, such as `onRated`.
- **Mark essential inputs as required** with `@Input({ required: true })`, so a missing binding fails at build time.
- **Keep the chain short.** Passing data through more than two or three levels of components becomes hard to follow. Use a shared service instead, which is covered in the Services lesson.
- **Emit only what the parent needs**, such as an id or a value, instead of the whole child component.

Inputs go down, events go up. A child never changes its parent's data directly. It asks the parent to change it by emitting an event.

## Common Mistakes

<div className="ngc-mistakes">

| Mistake | What you see | Fix |
|---|---|---|
| Binding to a property without `@Input()` | `NG0303: Can't bind to 'product' since it isn't a known property` | Add `@Input()` to the child property |
| Text without quotes: `[label]="Delivery"` | The child receives `undefined` | Use `[label]="'Delivery'"` or `label="Delivery"` |
| Output not initialised: `@Output() saved;` | `Cannot read properties of undefined (reading 'emit')` | Assign `new EventEmitter()` |
| Forgetting `$event` in the parent handler | The handler receives no value | Pass it: `(rated)="onRated($event)"` |
| Changing an input object inside the child | The parent's data changes unexpectedly | Emit an event and let the parent update the data |
| Output named `countchange` for `[(count)]` | Two-way binding does not update the parent | Name it exactly `countChange` |

</div>

## Check Yourself

## Exercises

## What to Remember

- A parent passes data down with `[input]="value"` to a child property marked `@Input()`.
- A child sends events up with an `@Output()` `EventEmitter`, and the parent listens with `(output)="handler($event)"`.
- The parent owns the data. Inputs go down, events go up.

Next: **Component Lifecycle Hook**, the hooks Angular calls as a component is created, updated and destroyed.
