Federico Navarrete
Federico Navarrete

Reputation: 3274

Why an Observable variable is not updating in real-time in Angular?

I'm new in Angular and I'm trying to transfer the data between 2 siblings in real-time:

This is my service:

message-transfer-service.ts

import { Injectable } from '@angular/core';
import { Products } from './products';
import { Observable } from "rxjs/Observable";
import "rxjs/add/observable/of";

@Injectable({
  providedIn: 'root'
})


export class MessageTransferService {

  private products = new Products();

  getProducts() : Observable<Products> {
    if (this.products)
      return Observable.of(this.products);
  }

  setProducts(product: Products) {
    this.products = product;
  }

  constructor() { }
}

This is my first sibling:

products-list.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { MessageTransferService } from '../message-transfer.service';
import { Products } from '../products';

@Component({
  selector: 'app-products-list',
  templateUrl: './products-list.component.html',
  styleUrls: ['./products-list.component.css']
})
export class ProductsListComponent implements OnInit {

  constructor(private service: MessageTransferService){ }

  product: any;

  ngOnInit() {
    this.product = { "id": 1, "name": "Cat", "age": 30 };
    this.setProducts();
  }

  setNewProduct() {
    this.product = { "id": 2, "name": "Dog", "age": 30 };
    this.setProducts();    
  }

  setProducts() {
    let d : Products = JSON.parse(JSON.stringify(this.product));
    this.service.setProducts(d);
  }
}

product-list.component.html

<p>products-list works!</p>
<button (click)="setNewProduct()">Set new book</button>

This is my second sibling:

product-details.component.ts

import { Component, OnInit } from '@angular/core';
import { MessageTransferService } from '../message-transfer.service';
import { Products } from '../products';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-product-details',
  templateUrl: './product-details.component.html',
  styleUrls: ['./product-details.component.css']
})
export class ProductDetailsComponent implements OnInit {

  product: Observable<Products>;

  constructor(private service: MessageTransferService){

   }

  ngOnInit() {
    this.getProducts();
  }

  getProducts() {
    this.product = this.service.getProducts();
    console.log(this.product);
  }
}

product-details.component.html

<p>product-details works!</p>

<button (click)="getProducts()">Click</button>

<p>{{product}}</p>

If I click on the button, the data is refreshed:

example

What I'm looking forward to is when I do click on the Set new book, its content should be automatically updated since the values are updated in the service, but until I do click on the button Click it's reflected in the console. Do you have any idea, what should I change? Probably is something in the constructor of product-details, but I'm not sure since I'm quite new in this area. Thanks for any hint.

I'm working with Angular 8 and the latest version of RXJS.

Upvotes: 2

Views: 1168

Answers (3)

Quentin
Quentin

Reputation: 1933

enter image description here

Stackblitz

You can save your list like this with BehaviorSubject in your service:

export class MessageTransferService {

  private productsSubject = new BehaviorSubject<Products[]>([]);
  public products$ = this.productsSubject.asObservable();

  setProducts(product: Products) {
    const products = this.productsSubject.getValue();
    products.push(product);
    this.productsSubject.next(products);
  }

}

And recover it by subscribing :

export class ProductDetailsComponent implements OnInit {

  products: Products[];

  constructor(private service: MessageTransferService){ }

  ngOnInit() {
    this.service.products$.subscribe(products => this.products = products)
  }
}

setProducts(product) of your service thus retrieves the existing list and adds the new product to the list.

Upvotes: 2

Hsuan Lee
Hsuan Lee

Reputation: 2330

Use BehaviorSubject(Thanks @fredrik).


export class MessageTransferService {

  private products = new Products();
  products$ = new BehaviorSubject<Products>(this.products)

  setProducts(product: Products) {
    this.products$.next(product)
  }

  constructor() { }
}
export class ProductDetailsComponent implements OnInit {

  product$: this.service.product$;

  constructor(private service: MessageTransferService){

   }

  ngOnInit() {

  }

}
<p>{{product$ | async}}</p>

Upvotes: 1

Damian Pioś
Damian Pioś

Reputation: 483

You are returning Observable and not subscribing it

export class MessageTransferService {

  private products = new Products();
  private productSubject = new BehaviorSubject<Product>(this.products);

  getProducts() : Observable<Products> {
    if (this.products)
        return this.productSubject.asObservable();
  }

  setProducts(product: Products) {
     this.products = product;
     this.productSubject.next(this.products);
  }

  constructor() { }
}

and subscribe it in component

Upvotes: 1

Related Questions