Skip to content

Communication

Using @Input and @Output

In this approach, the child component, which is embedded in the parent component, has both @Input and @Output properties which can be referenced in the html selector.

  • Type into the parent input

    • Value is updated in the child component
  • Type into the child input

    • Value is emitted to the parent when submitted

<fieldset>
  <legend>
    <h1>Parent Component</h1>
  </legend>

  <p>Parent Input</p>
  <input type="text" [(ngModel)]="inputText" />

  <p>Received message from child: {{ receivedMessage }}</p>

  <app-child
    [parentInput]="inputText"
    (childOutput)="onMessageReceived($event)"
  ></app-child>
</fieldset>

export class ParentComponent implements OnInit {
  // Text that will be sent to child, and binded with []
  inputText!: string;

  // Message set from @Output method `onMessageReceived`
  receivedMessage: string = '';

  constructor() {}

  ngOnInit() {}

  /**
   * Method invoked by `@Output` from child
   */
  onMessageReceived(message: string) {
    this.receivedMessage = message;
  }
}

<fieldset>

  <legend>
    <h1>Child Component</h1>
  </legend>

  <p>Parent input: {{ parentInput }}</p>
  <input type="text" [(ngModel)]="parentInput" />

  <p>Child Message to send to parent: {{ message }}</p>

  <input type="text" [(ngModel)]="message" (keyup.enter)="sendMessage()" />

  <button (click)="sendMessage()">Send to Parent</button>

</fieldset>

export class ChildComponent implements OnInit {

  @Input() parentInput: string = '';  
  @Output() childOutput = new EventEmitter<string>();

  message: string = '';

  constructor() {}

  ngOnInit() {}

  sendMessage() {
    this.childOutput.emit(this.message);
  }
}

View the full demo on StackBlitz


Using a Service

Components can communicate with any number of other components with the use of a Service that can be injected into each component. In the following example we have a parent component with a son and daughter component. The son or daughter component can emit values that all other components listen to and print.

<fieldset>
  <legend>
    <h1>Parent Component</h1>
  </legend>

  <div *ngIf="brotherMessage$ | async as brotherMessage">
    <p>Son said: {{ brotherMessage }}</p>
  </div>
  <div *ngIf="sisterMessage$ | async as sisterMessage">
    <p>Daughter said: {{ sisterMessage }}</p>
  </div>

  <app-brother></app-brother>
  <app-sister></app-sister>
</fieldset>

export class ParentComponent{
  inputText!: string;

  brotherMessage$: Observable<string>;
  sisterMessage$: Observable<string>;

  constructor(private uiService: UiService) {
    this.brotherMessage$ = this.uiService.brotherMessage$;
    this.sisterMessage$ = this.uiService.sisterMessage$;
  }
}

<fieldset>
  <legend>
    <h1>Brother Component</h1>
  </legend>
  <p>Message to send to anybody</p>
  <input type="text" [(ngModel)]="inputText" (keyup.enter)="onClick()" />
  <button (click)="onClick()">Submit</button>

  <div *ngIf="sisterMessage$ | async as sisterMessage">
    <p>Sister said: {{ sisterMessage }}</p>
  </div>
</fieldset>

export class BrotherComponent {

  inputText!: string;

  sisterMessage$?: Observable<string>;

  constructor(private uiService: UiService) {
    this.sisterMessage$ = this.uiService.sisterMessage$;
  }

  onClick(): void {
    this.uiService.brotherSpeak(this.inputText);
  }
}

@Injectable()
export class UiService {
  private brotherMessage = new Subject<string>();
  public brotherMessage$ = this.brotherMessage.asObservable();

  private sisterMessage = new Subject<string>();
  public sisterMessage$ = this.sisterMessage.asObservable();

  constructor() {}

  brotherSpeak(message: string): void {
    this.brotherMessage.next(message);
  }

  sisterSpeak(message: string): void {
    this.sisterMessage.next(message);
  }
}

View the full demo on StackBlitz


Template selector reference

The instance of the child selector can be used throughout the parent template.

  • Parent component displays the message property of the child component

<fieldset>

  <legend>
    <h1>Parent Component</h1>
  </legend>

  <p>Received message from child: {{ child1.message }}</p>

  <app-child #child1></app-child>

</fieldset>

View the full demo on StackBlitz


Using @ViewChild

By injecting the child component programatically, all of it's public methods can be accessed within the parent component.

1
2
3
4
5
6
7
8
9
<fieldset>
  <legend>
    <h1>Parent Component</h1>
  </legend>

  <p>Received message from child: {{ receivedMessage }}</p>

  <app-child #child1></app-child>
</fieldset>

export class ParentComponent{
  receivedMessage: string = '';

  // @ViewChild(ChildComponent) childComponent!: ChildComponent; 
  @ViewChild('child1') childComponent!: ChildComponent;

  constructor() {}

  ngAfterViewInit() {
    this.childComponent.message$.subscribe((message) => {
      this.receivedMessage = message;
    });
  }
}

<fieldset>

  <legend>
    <h1>Child Component</h1>
  </legend>

  <p>Child Message to send to parent: {{ message }}</p>

  <input type="text" [(ngModel)]="message" (keyup.enter)="sendMessage()" />

  <button (click)="sendMessage()">Send to Parent</button>

</fieldset>

export class ChildComponent{

  @Input() parentInput: string = '';

  message: string = '';

  private messageSubject = new Subject<string>();

  message$ = this.messageSubject.asObservable().pipe(
    debounceTime(300), // Optional: Debounce to emit only after 300ms of no changes
    distinctUntilChanged() // Optional: Only emit if the value actually changes
  );

  constructor() {}

  sendMessage() {
    this.messageSubject.next(this.message);
  }
}

View the full demo on StackBlitz

Comments