Parent and Child: Inputs and Outputs
PlantList holds all the plants. Each PlantCard shows one and has a Water button. The card needs its plant from the list, and the list needs to hear when the button is pressed. Angular gives each direction its own tool.
The rule
Data flows down through inputs; events flow up through outputs.
The child declares both ends:
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-plant-card',
template: `
<h3>{{ plant().name }}</h3>
<button (click)="water()">Water</button>
`,
})
export class PlantCard {
plant = input.required<Plant>(); // data coming down
compact = input(false); // optional, with a default
watered = output<number>(); // events going up
water() {
this.watered.emit(this.plant().id);
}
}input()creates a read-only signal the parent fills.input.required<T>()means the parent must supply it;input(false)has a default.output<T>()creates an event the child fires withemit(value).
The parent binds both on the tag:
@for (p of plants(); track p.id) {
<app-plant-card [plant]="p" (watered)="markWatered($event)" />
}[plant]="p" sends data down. (watered)="..." listens, and $event is whatever the child emitted, here the plant's id.
An input signal has no set method. The child cannot overwrite what it was given. That is the point: the component that owns the data is the only one that changes it, so you always know where to look.
Older code uses decorators for the same job, @Input() plant!: Plant; and @Output() watered = new EventEmitter<number>();. The idea is identical.
The shortcut
Before wiring anything, draw an arrow. Does the value travel from parent to child? Square brackets and input(). Does news travel from child to parent? Round brackets and output(). Then ask who owns the data: the owner updates it after hearing the event.
Draw the arrows
The plant goes down (list to card). "I was watered" goes up (card to list).
Drills
Answer fast — trust the rule.
How does PlantList pass a plant into PlantCard?
Inside PlantCard, what fires the watered event?
In (watered)="markWatered($event)", what is $event?
Why can't PlantCard call this.plant.set(newPlant)?
Remember
- Data down with
[x]andinput(); events up with(y)andoutput(). - Shortcut: draw the arrow, then let the owner of the data change it.
- Trap: trying to change an input inside the child instead of emitting.
