最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular 2 - single service provider across multiple browser windows - Stack Overflow

programmeradmin3浏览0评论

I am trying to supply an alert once a task is complete - the user may be in any of multiple pages at the time. The alert should display to all pages.

I am using a service implementing BehaviorSubject The provider for which is in my appponent.ts page - single instance

In my appponent.html I have the two components, one the alert, the other that fires the alert.

<alert></alert>
<submit-service></submit-service>

The service emits to the alert component which renders the alert. This works fine, but only ever on the page that submits the service (not to any other page) - submission function is also in the alert component.

submit-service utilises public emit: BehaviorSubject<model> = new BehaviorSubject(new model());

Once the event is completed it then fires off this.emit.next(_model);

In the alert component I subscribe to the event

ngOnInit(): void {
    this.service.emit.subscribe(data=> {
           this.fireAlert(data);
        }
    });
}

so I suppose the main question is, how do I have a single service subscribed across multiple instances, across multiple pages?

EDIT 1

Apologies for the ambiguity, by page I mean separate browser window or tab i.e. window.open

I am trying to supply an alert once a task is complete - the user may be in any of multiple pages at the time. The alert should display to all pages.

I am using a service implementing BehaviorSubject The provider for which is in my app.component.ts page - single instance

In my app.component.html I have the two components, one the alert, the other that fires the alert.

<alert></alert>
<submit-service></submit-service>

The service emits to the alert component which renders the alert. This works fine, but only ever on the page that submits the service (not to any other page) - submission function is also in the alert component.

submit-service utilises public emit: BehaviorSubject<model> = new BehaviorSubject(new model());

Once the event is completed it then fires off this.emit.next(_model);

In the alert component I subscribe to the event

ngOnInit(): void {
    this.service.emit.subscribe(data=> {
           this.fireAlert(data);
        }
    });
}

so I suppose the main question is, how do I have a single service subscribed across multiple instances, across multiple pages?

EDIT 1

Apologies for the ambiguity, by page I mean separate browser window or tab i.e. window.open

Share Improve this question edited Aug 11, 2017 at 15:47 Googs asked Jul 28, 2017 at 21:37 GoogsGoogs 8217 silver badges14 bronze badges 6
  • To my knowledge it's not really possible. You have to subscribe to the observable in each component that is "reacting" to new data, otherwise they wouldn't know that a change occured. So (if I am reading this right), you are doing it correctly. Can you clarify what it's doing that is not desireable? I can't quite discern that from your original post. – joshrathke Commented Jul 28, 2017 at 21:42
  • I guess the part I don't understand is "multiple pages". In an Angular context that's a little confusing. Do you mean multiple components deep within the application? – joshrathke Commented Jul 28, 2017 at 21:44
  • Sure, basically I am wanting to somehow extend the service and emission across multiple pages, not just multiple components within the same page. Even though I create a single instance of the provider, that single instance is only for the child components within that page. Each page then has its own instance of the parent (and therefore it's won subscribers). So wondering how I would go about creating a global instance across multiple pages, and if not possible, what kind of workarounds are possible or people may have done. – Googs Commented Jul 28, 2017 at 21:47
  • No, multiple physical pages, not child components – Googs Commented Jul 28, 2017 at 21:47
  • K, gotcha, answer coming.... – joshrathke Commented Jul 28, 2017 at 21:49
 |  Show 1 more comment

2 Answers 2

Reset to default 20

Just in case others are having this same issue - there is in fact an answer. Using global storage events allows the traversing of information across all browser tabs/windows.

In the service instead of using BehaviourSubject, the service updates or 'emits' the data to a local storage item, event listener utilising a HostListener() decorator can then listen for these udpates - which listens across all windows and tabs.

Example:

    @HostListener('window:storage', ['$event'])
    onStorageChange(ev: StorageEvent) {
       console.log(ev.key);
       console.log(ev.newValue);        
    }

See here for further information: Storage events

So there's a couple things at play here. The first is the service that let's your application know that it's time to display the alert. It sounds like you already have that, but for simplicity sake I would make sure you are declaring that in a forRoot() context. I won't go into a crazy amount of detail regarding this topic, but essentially you need to make sure that your service is running in the root context. If you start lazy loading modules, and then subscribing to your service from within the lazy loaded module, it will create it's own Dependency Injection context and you'll start pounding your head against the table wondering why your service isn't updating. (been there :)

The next thing to look at is where you want to render your alert. You'll likely want to use the ComponentFactoryResolver to render your alert in the highest level component you can think of that makes sense. Basically (if I understand your need correctly), you need this to be within the same component, or higher as all of the pages you want to have the alert rendered to. For example I am working on an application that has a dashboard where we have a ComponentFactoryResolver that renders any and all modals we might need throughout the application. This allows us to call modals from anywhere within the dashboard using, like you, a behavior subject that activates the modals. Here's a great article on using the ComponentFactoryResolver.

Update 1 So after realizing that "page" was actually a new browser window this method won't necessarily work. Using BehaviorSubjects will only update within the application context, so opening a new window creates a new application context, i.e. killing the BehaviorSubject of being a viable candidate to make this work. You'll need to have a service that is not instance specific. Web sockets as you mentioned would be a good alternative.

It is worth noting though that if it's possible to refactor the code to open modals instead of new windows, you could maintain the integrity of your Dependency Injection tree, and then use BehaviorSubjects to achieve this. Otherwise you'll need something outside of the application that is maintaining state.

发布评论

评论列表(0)

  1. 暂无评论