5

I have an angular 5 website which consumes a restful wcf web service. The web service, in turn, interacts with business objects, that talk to a database. I'm using the @angular/common/http module to consume the web service into an observable object which then renders on the web page. So far so good.

Now, am also updating the database via the web service, which also works fine.

But, it's a tad slow, and I would like to be able to update the Observable directly and simulaneously with the update web service call, so that the web site does not have to wait for the data to go through the web service, through the incredibly slow enterprise resource planning business objects, to the database, and then back down into the Observable objects, updating the screen via the async pipe.

So, I thought to myself, why not just replace the Observables with BehaviorSubjects? Then, I could just use the "next" method on the BehaviorSubject to quickly update the page.

But, when I try that...

public cartons$: BehaviorSubject<ICartonTrackDetail[]>;
this.cartons$ = this.cartonService.getCartonsObservable();

I get this error...

Type 'Observable<ICartonTrackDetail[]>' is not assignable to type 'BehaviorSubject<ICartonTrackDetail[]>'.
  Property '_value' is missing in type 'Observable<ICartonTrackDetail[]>'.```

Now, the error makes sense, but it makes me step back and wonder: What is the best way to accomplish my goal?

Bill Mild
  • 413
  • 1
  • 4
  • 14

1 Answers1

2

you want a subject that will let you inject client-side updates into the stream and you want to merge this subject with the observable that is streaming data from the server.

Something like this:

private clientStream$ : Subject<ICartonTrackDetail[]>;

public cartons$ : Observable<ICartonTrackDetail[]>;

// setup
const serverStream = this.cartonService.getCartonsObservable();
this.clientStream$ = new Subject<ICartonTrackDetail[]>();
this.cartons$ = serverStream.merge(this.clientStream$);

// when you get new data to send to server:
this.clientStream$.next(newData);
.. add code to send newData to server like normal ..

Now any code subscribing to cartons$ will receive updates as you call clientStream$.next(newData)

Brandon
  • 38,310
  • 8
  • 82
  • 87
  • Very helpful, Brandon. Unfortunately for me, doing this is wreaking havoc on my custom validators, and leaving my form in a PENDING state. – Bill Mild May 03 '18 at 16:00
  • The ".merge" seems to nullify the carton$ observable prior to ".next" even though should be data already in the carton$ observable from the server. – Bill Mild May 03 '18 at 17:48
  • It depends on the semantics of `cartonService.getCartonsObservable`. What does that return? A hot observable? Cold? One that ends after single result? The solution code might need modification to maintain the expected semantics – Brandon May 03 '18 at 18:01
  • I believe it would be a hot observable, since it already has data. It does not end after a single result. – Bill Mild May 03 '18 at 18:03
  • try adding `.shareReplay(1)` after the `merge()`. – Brandon May 03 '18 at 18:23
  • Didn't help. Hear is a demo. https://stackblitz.com/edit/example-rxjs-observable-operator-concat-merge-5fmdg7?file=app/app.component.ts. It works as written. But, if I change source2$ from Observable to Subject, then it does not work anymore. I wonder if you could get the demo to work? – Bill Mild May 03 '18 at 18:54
  • Nevermind, I got the sample working. Now, I just have to figure out the bug in my code that is different. – Bill Mild May 04 '18 at 13:31