0

I have a cart-service and 2 components. The product-list component displays the products with a buy button. The top-bar component should display the count but it doesn't work if I getItemCount() directly. What's the issue? I want to get the length/count and not the entire array. Thanks for the help!!!

I also have the code on stackbliz.

@Component({
  selector: 'app-top-bar',
  template: `
    Item Count: {{ itemCount }}<br>
    Items: {{ items | json }}
  `
})
export class TopBarComponent implements OnInit {
  itemCount: number = 0;
  items: string[] = [];

  constructor(private cartService: CartService) { }

  ngOnInit() {
    this.items = this.cartService.getItems();
    this.itemCount = this.cartService.getItemCount();
  }
}
@Component({
  selector: 'app-product-list',
  template: `
    <div>
      <button (click)="addToCart('New Stuff')">Buy</button>
    </div>
  `
})
export class ProductListComponent {
  products: string[];

  constructor(private cartService: CartService) { }

  addToCart(item: string) {
    this.cartService.addItem(item);
  }
}
export class CartService {
  private items: string[] = [ 'One', 'Two'];

  getItems(): string[] {
    return this.items;
  }

  getItemCount(): number {
    return this.items.length;
  }

  addItem(item: string) {
    this.items.push(item);
  }
}
Alessio
  • 3,404
  • 19
  • 35
  • 48
vanessa
  • 3
  • 1
  • Where do you Inject the service? – Timothy Jan 20 '20 at 00:05
  • The problem is that you are calling `cartService.getItemCount` once, and it returns a number which is a value type. `cartService.getItems` returns an `array`, which is a reference type. So you are getting a **reference** to the items, and thus they are being updated. But your **value** of type number is not. Perhaps [this answer can shed some additional light on the subject](https://stackoverflow.com/a/6605700/3718246). – Dean Jan 20 '20 at 00:13
  • @Dean this makes a lot of sense!! So how do I fix the issue? – vanessa Jan 20 '20 at 00:19
  • Supun De Silva already answered. But I will tell you this: read up on Angular state management, rxjs, and reference vs value types. There are a lot of ways to solve this issue. :) – Dean Jan 20 '20 at 00:56

2 Answers2

2

Improving my answer

I believe this is a better approach

Cart Service

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class CartService {
  //  Define behavior Sub
  public valueObservable: BehaviorSubject<number> = new BehaviorSubject<number>(0); 
  private items: string[] = [ 'One', 'Two'];

  //  Init at ctor
  constructor() {
    this.valueObservable.next(this.items.length);
  }

  getItems(): string[] {
    return this.items;
  }

  getItemCount(): number {
    return this.items.length;
  }

 addItem(item: string) {
    this.items.push(item);

    //  Notify on change
    this.valueObservable.next(this.items.length);
  }
}

TopBarComponent

    export class TopBarComponent implements OnInit, OnDestroy {
    itemCount: number = 0;
    items: string[] = [];
    private serviceSubsription ;
    constructor(private cartService: CartService) { }

    ngOnInit() {
        this.items = this.cartService.getItems();

        this.itemCount = this.cartService.getItemCount();

        this.serviceSubsription = this.cartService.valueObservable.subscribe(
        value =>  { this.itemCount = value; console.log('Value : ' + value); }
        );
    }

    ngOnDestroy() {
        this.serviceSubsription.unsubsribe()
    }
}
Supun De Silva
  • 1,437
  • 9
  • 15
  • 1
    Yeah, this is a better solution for sure. And you can further enhance it (if you want to) by not subscribing to the observable in the component and instead using the [async pipe](https://angular.io/api/common/AsyncPipe) against your observable directly, which will unsubscribe for you when the component is destroyed. – Dean Jan 20 '20 at 02:13
  • @Dean: I tried to apply Supun's code and use async pipe so that there's no need to subscribe and unsubscribe but it doesn't work. Can you please tell me where I went wrong? Code is here: https://stackblitz.com/edit/angular-yurkrn – vanessa Jan 20 '20 at 06:03
  • 1
    @vanessa it's because you need to set the value of your itemCount$ to an observable or behavior subject, not a value. [I've updated your StackBlitz code to demonstrate what I'm talking about](https://stackblitz.com/edit/angular-i3daaq). – Dean Jan 20 '20 at 16:24
-1

In the Top Bar Component, Instead of calling the this.cartService.getItemCount() in the onInit

Change the template to look as follows

template: `
Item Count: {{ cartService.getItemCount()}}<br>
Items: {{ items | json }}
`

Becomes an observeable to the DOM

Supun De Silva
  • 1,437
  • 9
  • 15
  • 1
    No, it doesn't become an observable. But during change detection, Angular will call `getItemCount`. So, this does solve the problem; albeit in a less than savory way. – Dean Jan 20 '20 at 00:38