2

我正在尝试完成我希望的简单的“查看会话存储中的密钥并在它发生变化时更新它”的场景,但是 Observables 真的让我很受挫。

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

message$ 绑定到 HTML,非常简单:

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

如果“Observable消息HTML”中已经存储了值,但如果 init 上没有消息然后它被添加,或者消息值确实存在并被更新,则不会发生任何事情。我显然做错了什么,我会很感激有知识的人的洞察力Observables

4

3 回答 3

8

我认为您正在寻找的是StorageEvent

您可以使用fromEvent从此事件创建您的 observable ,如下所示:

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

请注意,我添加了过滤器来专门查找 sessionStorage 的更新,并指定了密钥 === "message",但您也可以使用 localStorage 和您想要的任何密钥

此外,正如Damian C指出的那样,如果您打算仅在模板中使用它,则无需订阅此 observable。

已编辑: 仅当您打开多个选项卡并希望同步对存储的更改时,上述解决方案才有效。检查这个答案

我认为您必须更改将值设置为 sessionStorage 的位置。我的建议是使用您自己的 StorageService (我的示例),您将在其中将值设置为 storage 并保留一个 observable,它应该在您设置键/值时发出。

在stackblitz上做了一个例子

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

于 2020-02-15T01:48:00.677 回答
1

这个线程有点旧,但我也遇到了同样的问题。

正如@quake 所指出的,在同一窗口内的更改不会引发 storageEvent。

要实现所需的“设置拦截”,您必须修补 localSorage 或 sessionStorage 的存储功能。例如:

   // 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;
  }

如您所见,在截获的函数中,您可以调用next()@damian-c 提到的 Subject 的函数。

于 2020-09-04T09:16:52.787 回答
0

由于您使用的是异步管道,因此您不需要订阅您的组件,您可以将其取出。AsyncPipe 将在幕后处理订阅/取消订阅。

尝试改用主题。

// 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'));
}

您的 html 模板可以保持不变。

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

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

于 2020-02-15T01:34:55.703 回答