简体   繁体   English

使用 Angular Observable 订阅会话存储密钥

[英]Using Angular Observable to subscribe to Session Storage key

I'm trying to accomplish what I had hoped would be a simple "look at the key in Session Storage and update it when it changes" scenario, but Observables really trip me up.我正在尝试实现我所希望的一个简单的“查看会话存储中的键并在它发生变化时更新它”的场景,但 Observables 真的让我失望了。

message$ = new Observable(observer => {
    observer.next(window.sessionStorage.getItem('message'));
});
    
ngOnInit() {
    this.message$.subscribe();
}

And the message$ is bound to the HTML, pretty straightforward:并且 message$ 绑定到 HTML,非常简单:

<p id="message">{{message$ | async}}</p>

The Observable will output the text to the HTML if there's already value stored in 'message' but if there's no message on init and then it gets added, or the message value does exist and is updated, nothing happens.如果 'message' 中已经存储了值,则Observable会将文本输出到HTML ,但是如果 init 上没有消息然后它被添加,或者消息值确实存在并被更新,则不会发生任何事情。 I'm obviously doing something wrong, and I would appreciate the insight of someone knowledgeable with Observables .我显然做错了什么,我会感谢了解Observables的人的洞察力。

I think what you're looking for is a StorageEvent我认为您正在寻找的是StorageEvent

You can create your observable from this event using fromEvent , like this:您可以使用fromEvent从此事件创建可观察对象,如下所示:

const message$ = fromEvent<StorageEvent>(window, "storage").pipe(
  filter(event => event.storageArea === sessionStorage),
  filter(event => event.key === "message"),
  map(event => event.newValue)
);

Notice that I added filters to specifically look for updates of sessionStorage and I specified the key === "message", but you can also use localStorage and any key you want请注意,我添加了过滤器来专门查找 sessionStorage 的更新,并指定了键 === "message",但您也可以使用 localStorage 和您想要的任何键

Also, as Damian C pointed out , you don't need to subscribe to this observable if you're planning to only use it in template.此外,正如Damian C指出的那样,如果您打算仅在模板中使用它,则无需订阅此 observable。

EDITED: solution above only working when you have multiple tabs opened and want to synchronize changes to storage.已编辑:上述解决方案仅在您打开多个选项卡并希望同步对存储的更改时才有效。 Check this answer检查这个答案

I think you have to change a place where you set your value to sessionStorage.我认为您必须更改将值设置为 sessionStorage 的地方。 My suggestion is to use your own StorageService ( my example ) where you will set your value to storage and keep an observable, which should emit when you are setting key/value.我的建议是使用您自己的 StorageService( 我的示例),您将在其中将值设置为 storage 并保留一个可观察的对象,该可观察对象应该在您设置键/值时发出。

I did an example on stackblitz在stackblitz上做了一个例子

https://stackblitz.com/edit/angular-3szmzd https://stackblitz.com/edit/angular-3szmzd

This thread is a bit old, but I just had the same problem.这个线程有点旧,但我遇到了同样的问题。

As indicated by @quake, the storageEvent is not raised for changes within the same window.正如@quake 所指示的那样,不会针对同一窗口内的更改引发 storageEvent。

To achieve the desired "settings interception" you have to patch the storage functions of the localSorage or sessionStorage.要实现所需的“设置拦截”,您必须修补 localSorage 或 sessionStorage 的存储功能。 For example:例如:

   // somewhere in a Class...

  private setStorageItemFunc: (key: string, value: string) => void;
  private getStorageItemFunc: (key: string) => string;
  private subject$: Subject<string>;

  applySettingsInterception() {
    // define a Subject
    this.subject$ = new Subject();

    // just for demonstation purposes. Don't subscribe to the subject, use async pipe instead
    this.subject$.subscribe(e => {
      console.log('value changed', e);
    });

    // save original functions
    this.setStorageItemFunc = window.localStorage.setItem;
    this.getStorageItemFunc = window.localStorage.getItem;

    // set storage functions to the newly created interceptor functions
    // bind to this to get access to the current object and it's values in interceptor function
    window.localStorage.setItem = this.setStorageItem.bind(this);
    window.localStorage.getItem = this.getStorageItem.bind(this);
  }

  private setStorageItem(key: string, value: string): void {
    console.log('setStorageItem', key, value);

    this.subject$.next(`${key}: ${value}`);

    this.setStorageItemFunc.call(localStorage, key, value);
  }

  private getStorageItem(key: string): string {
    const value = this.getStorageItemFunc.call(localStorage, key);

    console.log('getStorageItem', key, value);

    return value;
  }

As you can see, within the intercepted functions, you can call the next() function of an Subject as mentioned by @damian-c.如您所见,在拦截的函数中,您可以调用@damian-c 提到的主题的next()函数。

Since you are using the async pipe, you don't need to subscribe in your component, you can take that out.由于您使用的是异步管道,因此您无需订阅您的组件,您可以将其删除。 AsyncPipe will handle the subscribe / unsubscribe behind the scenes. AsyncPipe 将在后台处理订阅/取消订阅。

Try using a subject instead.尝试改用主题。

// This is what will help you broadcast your message and push updates to subscribers.
message$ = new Subject();

// Anytime the "message" changes, you'll want to call this.
updateTheSubject() {
    this.message$.next(window.sessionStorage.getItem('message'));
}

Your html template can remain unchanged.您的 html 模板可以保持不变。

https://rxjs-dev.firebaseapp.com/guide/subject https://rxjs-dev.firebaseapp.com/guide/subject

https://angular.io/api/common/AsyncPipe https://angular.io/api/common/AsyncPipe

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM