0cnLaroche
0cnLaroche

Reputation: 277

Item not being pushed to array in Angular class

I made an Angular Service that keeps a list of items that other components can add items to and can retrieve. For some reason nothing gets added to the list. It does only within the add() function of my service but the variable doesn't get changed outside of it.. For example, at the end of the add() function, the console returns the item that was added. But when I check the list again from the getItems() function list is empty. Why would list doesn't get updated outside of the function after push?

    export class BasketService {
      private list: Item[] = [];
      constructor() {}

      add(product: Product, units: number): void {
        let item: Item = new Item();
        item.product = product;
        item.units = units;
        this.list.push(item);
        console.log(this.list[(this.list.length - 1)]);
        // Log shows item object
      }
      getItems(): Observable<Item[]> {
        console.log(this.list);
        // Log shows empty array
        return of(this.list);
      }
    }

EDIT

getItems() does have a subscriber however the issue doesn't seem to come from there since it receives an empty array of Item, the same empty list that is printed by the console in getItems (in the basketService).

export class BasketComponent implements OnInit {
  items: Item[];

  constructor(private basketService: BasketService) {}

  ngOnInit() {
    this.getItems();
    console.log(this.items.length);
  }
  getItems(): void {
    this.basketService.getItems()
      .subscribe(items => {
        this.items = items; });
  }

Upvotes: 0

Views: 1009

Answers (2)

jo_va
jo_va

Reputation: 13983

Your getItems() method returns an Observable holding your list of items. You need to subscribe to that Observable to get the items.

Also, you could use a BehaviorSubject to push the list in the Observable every time the list is modified.

That way, your service consumers will get notified through the observable when there is an item added.

@Injectable()
export class BasketService {
  private list: Item[] = [];
  private subject = new BehaviorSubject<Item[]>([]);
  private list$ = this.subject.asObservable();

  add(product: Product, units: number): void {
    let item: Item = new Item();
    item.product = product;
    item.units = units;
    this.list.push(item);
    this.subject.next(this.list); // push the modified list to notify observers
  }
  getItems(): Observable<Item[]> {
    return this.list$;
  }
}

Use it like this:

export class MyComponent implements OnInit {
  constructor(private service: BasketService) { }

  ngOnInit() {
    this.service.getItems().subscribe(items => {
      console.log('list modified', items); // this is executed after each list.push()
    });
  }
}

You could also use scan with a Subject and get rid of the list variable. The list will be created by the scan operator by appending to the previous values:

@Injectable()
export class BasketService {
  private subject = new Subject<Item>();
  private list$ = this.subject.pipe(
    scan((list, item) => [...list, item], [] as Item[]),
  );

  add(product: Product, units: number): void {
    let item: Item = new Item();
    item.product = product;
    item.units = units;
    this.subject.next(item);
  }
  getItems(): Observable<Item[]> {
    return this.list$;
  }
}

Here is a short Stackblitz demo using the scan operator.

Upvotes: 1

Milos Kovacevic
Milos Kovacevic

Reputation: 891

Make sure that your service is decorated with @Injectable and provided properly so all your components that use the service have the same instance of a service.

Upvotes: 0

Related Questions