1

I am trying to pass data from a parent to its child component in Angular 17. The data is retrieved from the back-end through an API that gets an array of items.

In my parent component, items$ is a subject:

items$ = Subject<Array<Item>> = new Subject();

I subscribe it to the method of my service that retrieves all items from the backend:

ngOnInit(){
    this.itemService.getItems().subscribe(this.items$);
}

In my parent.html template, this code does display that there are items:

@if(items$ | async){
"There are items!"
}@else{
"There aren't any items"
}

So I then call my child component:

@if(items$ | async){
<app-child [items$]=items$>
}

This is what my child.component.ts looks like:

export class ChildComponent implements OnInit{
@Input() items$: Observable<Array<Item>>;

ngOnInit(){
this.items$.subscribe();
}
}

An child.component.html:

@if(items$ | async){
"There are items in the child component!"
}@else{
"There aren't any items in the child component"
}

Well, it displays that there aren't any items in the child component.

I know I don't need to subscribe in the onInit() method of my child component, but I really just wanted to make sure that I am indeed subscribed to my multicast observable.

Also, interestingly, when I take my child component out of the @if condition in parent.html, the items are displaying!

Any idea why? And how can I condition the display of my child component to the presence of items?

3
  • 1
    this.itemService.getItems().subscribe(this.items$) this piece of code looks really strange, what are you trying to achieve with this? Commented Nov 27, 2024 at 10:21
  • 1
    I think I now understand this pattern. When using .subscribe(), instead of a callback function, you can also pass an Observer object instead which has optional properties for next, error and complete. So when passing a Subject, it will call the next function of that subject. Never seen this, but pretty neat. Commented Nov 27, 2024 at 10:29
  • This is it @JSONDerulo, and I had indeed forgotten to call next() on my subject. It works now :) Commented Nov 27, 2024 at 10:35

1 Answer 1

2

You can convert the Subject to a BehaviorSubject, so that it maintains state (the array). Prefer subject when you want to trigger emissions for events (Event Bus) not holding state.

Also you should write a callback inside the subscribe that calls the next method of the BehaviorSubject with the data from the API.

itemService = inject(ItemService);
items$: BehaviorSubject<Array<any> | null> =
  new BehaviorSubject<Array<any> | null>(null);

ngOnInit() {
  this.itemService.getItems().subscribe((data: any) => {
    this.items$.next(data);
  });
}

Full Code:

import { Component, inject, Injectable, Input, OnInit } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { Observable, of, Subject, BehaviorSubject } from 'rxjs';
import { CommonModule } from '@angular/common';

@Injectable({ providedIn: 'root' })
export class ItemService {
  getItems() {
    return of([{ test: 1 }]);
  }
}

@Component({
  selector: 'app-child',
  imports: [CommonModule],
  template: `
    @if(items$ | async) {
      "There are items in the child component!"
    } @else {
      "There aren't any items in the child component"
    }
  `,
})
export class ChildComponent implements OnInit {
  @Input() items$!: Observable<Array<any> | null>;

  ngOnInit() {
    this.items$.subscribe();
  }
}

@Component({
  selector: 'app-root',
  imports: [CommonModule, ChildComponent],
  template: `
    @if(items$ | async){
      "There are items!"
    }@else{
      "There aren't any items"
    }
    <hr/>
    @if(items$ | async) {
      <app-child [items$]="items$"/>
    }
  `,
})
export class App {
  itemService = inject(ItemService);
  items$: BehaviorSubject<Array<any> | null> =
    new BehaviorSubject<Array<any> | null>(null);

  ngOnInit() {
    this.itemService.getItems().subscribe((data: any) => {
      this.items$.next(data);
    });
  }
}

bootstrapApplication(App);

Stackblitz Demo

Sign up to request clarification or add additional context in comments.

3 Comments

That solved it! Thank you so much!
That being said, I don't understand why my iems would be showing in the parent component? I take that they weren't showing in the child because my Subject had not been populated with any data, am I correct?
@Ems I just pasted in both, you can modify it to suit your needs, it has no significance

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.