2

I'm working on a feature for a web portal, for a user to "preview" some changes they have made to the data before saving it to the database. I've made a service and have two components, one that emits change events and the other that (is supposed to) listen and then update itself with the preview.

I want the preview to open in a new tab.

My service is

@Injectable()
export class PreviewService {

   private hub = new Subject<Hub>();

   hubUpdated = this.hub.asObservable();

   updateHubPreview(hub: Hub) {
     this.hub.next(hub);
   }

   constructor() {
   }
}

My emitting component emits events by

emitChange(): void {
   this.previewService.updateHubPreview(this.hub);
   window.open(`hubs/preview/mock/${this.hub.id}`);
   // I also tried [routerLink=...] target="_blank" in the template
}

And my preview component is

constructor(private hubService: HubService, private route: ActivatedRoute, 
            private previewService: PreviewService) {

   this.hub = new Hub();
   previewService.hubUpdated.subscribe(value => {
      console.log("hello!");
      this.hub = value;
   });
 }

In the newly opened window, nothing happens.

shammelburg
  • 6,974
  • 7
  • 26
  • 34
lewtur
  • 603
  • 1
  • 6
  • 16

3 Answers3

3

This would be caused by that Subject is only subscribing when the emit happens, so you are emitting before opening the new window, so when trying to subscribe the next has already been executed before and therefore subscribe isn't firing. You can use BehaviorSubject that always subscribes, instead of Subject:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

private hub = new BehaviorSubject<Hub>(null);

See this: Angular 2 - Behavior Subject vs Observable?

Excerpts:

  • Behavior subject needs a initial value as it must always return a value on subscription even if it hasn't received a next()

and

  • A regular observable only triggers when it receives a onnext
Community
  • 1
  • 1
AT82
  • 71,416
  • 24
  • 140
  • 167
  • Thanks! With this addition I'm able to get some sort of message when the new tab opens with the component, i.e. the service call is being read bu the component. However any further updates, i.e. further calls of hub.next(newValue) do not update the new window, so i'm stuck with the default null value – lewtur May 15 '17 at 14:28
  • Where do you have set the service as provider? it's provided in your ngModule only, right? – AT82 May 15 '17 at 14:31
  • ah, wait... this is a new tab. Then it won't work, you need to somehow sync these, but I can't unfortunately help you there :( – AT82 May 15 '17 at 14:42
  • Yeah the service is only provided in my app.module. And no worries, i'll tick your answer anyway because it did help – lewtur May 15 '17 at 14:44
  • Thanks :) Yeah, I'm sorry I can't help you further. Tried googling a bit... I think that this question could help you though perhaps: http://stackoverflow.com/questions/9554896/sharing-websocket-across-browser-tabs – AT82 May 15 '17 at 14:50
  • 1
    I ended up solving this rather primitively using cookies. Whenever my publisher component makes a change, writes a cookie, then the publisher just reads a cookie every few seconds and sets its property to the value received. Not really clean but does the job for now – lewtur May 16 '17 at 08:57
0

You are subscribing after the vale was emited. That won't work for a subject.

Change private hub = new Subject<Hub>(); to this private hub = new ReplaySubject<Hub>(1);

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
0

I managed to solve this using HTML5 local storage. I have a class:

export class StorageHelper {

    static write(key: string, item: any) {
       localStorage.setItem(key, JSON.stringify(item));
    }

   static read<T>(key: string): T {
      return <T>JSON.parse(localStorage.getItem(key));
   }
}

The new tab just requests some data from this class when it opens, which the parent tab has stored. Not a perfect solution but it does the job.

lewtur
  • 603
  • 1
  • 6
  • 16