2

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.

2
  • I don't see an emit anywhere. You give the observable an initial value - but never emit a new one. The Observable does not know if a local variable somewhere was changed. Commented Sep 18, 2019 at 12:09
  • Try - <p>{{product | async}}</p> Commented Sep 18, 2019 at 12:12

3 Answers 3

2

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.

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

Comments

1

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>

4 Comments

This should work - personally I normally prefer to use the BehaviourSubject (because it holds a value, but that's a matter of taste)
It's working, but I have a question, how can I know when it changed from the ProductDetailsComponent? Because I need to change something from the TS side :/.
You can subscribe to it this.service.product$.subscribe(value => { // do something }).
Should I add the subscribe in the constructor or ProductDetailsComponent? Or where exactly? Sorry, as I said I'm quite new in this topic :\.
1

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

Comments

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.