Как генерировать изменения, используя Rx JS и angular? - PullRequest
2 голосов
/ 02 апреля 2020

У меня есть служба для извлечения и отправки товаров с именем BasketService.

export class BasketService {
apiUrl = 'http://0.0.0.0:8000';
basket: Observable<Basket>;
constructor(private httpClient: HttpClient) {}
getBasket(): Observable<Basket> {
    this.basket = this.httpClient.get<Basket>(`${this.apiUrl}/api-auth/basket/`);
    return this.basket;
}
addToBasket(sku: Sku, count: number): Observable<Basket> {
   const oBasket = this.httpClient.post<Basket>(`${this.apiUrl}/api-auth/basket/`, {items: 
   [{sku: sku.pk, quantity: count}]});
   return oBasket;
   }
}

Я подписываюсь на изменение корзины в моем компоненте BasketService.getBasket (). Subscribe (), после этого я пу sh некоторые товары в корзину, вызывая функцию addToBasket. Я хочу обновить подписчика, который подписывается на getBasket, но, как и ожидалось, этого не происходит.

export class BasketComponent implements OnInit {
  basketIcon = faShoppingBasket;
  sum = 0;
  sumVerbose = 'корзина пуста';
  basket: Basket;

  constructor(private router: Router, private basketService: BasketService) {
  }

  openBasket() {
    this.router.navigate(['basket']);
  }

  ngOnInit(): void {
    this.basketService.getBasket().subscribe(basket => {this.basket = basket; console.log(basket); });
    //console.log(this.basket);
  }

}
export class PromoSkuComponent implements OnInit {
  basketIcon = faShoppingBasket;
  @Input() title: string;
  @Input() pk: number;
  @Input() price: number;
  @Input() image: string;

  constructor(private router: Router, private basketService: BasketService) {
  }

  ngOnInit(): void {
  }

  addToBasket() {
    this.basketService.addToBasket({pk: this.pk} as Sku, 1).subscribe(basket => basket);
  }

1 Ответ

1 голос
/ 02 апреля 2020

Наблюдаемое, возвращаемое из http.get, является наблюдаемым одноразового использования.

Для создания будущих значений вам потребуется создать тему прокси.

В приведенном ниже примере я объявляю личное Subject<Basket>. И getBasket, и addToBasket создают субъекта, если он не существует. Это означает, что у вас не будет ни темы, ни одного и того же экземпляра субъекта, возвращенного всеми функциями.

После получения ответов http отправьте ответ через тему. Все подписчики получат новое значение.

export class BasketService {
  constructor(private httpClient: HttpClient) {}

  private apiUrl = 'http://0.0.0.0:8000';
  private basket$: Subject<Basket>;

  getBasket(): Observable<Basket> {
    // Basket has already been loaded. Return the subject
    if (this.basket$) {
      return this.basket$.asObservable();
    }

    // Create a replay subject to ensure late subscribers receive an initial value
    this.basket$ = new ReplaySubject<Basket>(1);

    // Subscribe to the http client and emit the response
    const url = `${this.apiUrl}/api-auth/basket/`;
    this.httpClient.get<Basket>(url)
      .subscribe(basket => this.basket$.next(basket));

    // Return the subject
    return this.basket$.asObservable();
  }

  addToBasket(sku: Sku, count: number): Observable<Basket> {
    const url = `${this.apiUrl}/api-auth/basket/`;
    const body = {items: [{sku: sku.pk, quantity: count}]};

    // Create subject if it doesn't exist.
    // It may be unlikely that you can addToBasket if you haven't loaded the basket, this is here for safety
    if (!this.basket$) {
      this.basket$ = new ReplaySubject<Basket>(1);
    }

    // Update the basket, emit the response to all current and future subscribers
    this.httpClient.post<Basket>(url, body)
      .subscribe(basket => this.basket$.next(basket));

    return this.basket$.asObservable();
  }
}

Это предполагает, что вы хотите выполнить GET только при первом вызове getBasket. Если вы хотите всегда возвращать последнее значение корзины, вы можете создать тему в точке объявления и всегда запускать http GET.

private basket$: Subject<Basket> = new ReplaySubject<Basket>(1);

getBasket(): Observable<Basket> {
  const url = `${this.apiUrl}/api-auth/basket/`;
  this.http.get(url).subscribe(basket => this.basket$.next());
  return this.basket$.asObservable();
}

addToBasket(sku: Sku, count: number): Observable<Basket> {
  const url = `${this.apiUrl}/api-auth/basket/`;
  const body = {items: [{sku: sku.pk, quantity: count}]};
  this.httpClient.post<Basket>(url, body)
    .subscribe(basket => this.basket$.next(basket));

  return this.basket$.asObservable();
}

Если вы постоянно вызываете addToBasket из одного и того же компонента вы можете предпочесть не возвращать объект из addToBasket, но использовать следующий шаблон в своем компоненте:

basket: Basket;

ngOnInit() {
  this.basketService.getBasket().subscribe(basket => {
    this.basket = basket;
  });
}

addToBasket(product) {
  this.basketService.addToBasket(product);
}

, где addToBasket просто выполняет HTTP POST и не возвращает наблюдаемое. Подписка getBasket здесь просто получит обновленное значение.

...