【问题标题】:Using Angular Observable to subscribe to Session Storage key使用 Angular Observable 订阅 Session Storage 密钥
【发布时间】:2021-07-14 22:26:46
【问题描述】:

我正在尝试完成我希望的简单“查看 Session Storage 中的密钥并在它发生变化时对其进行更新”的场景,但 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的人的洞察力。

【问题讨论】:

    标签: angular rxjs-observables


    【解决方案1】:

    我认为您正在寻找的是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 pointed out,如果您打算仅在模板中使用它,则无需订阅此 observable。

    已编辑: 上述解决方案仅在您打开多个选项卡并希望将更改同步到存储时才有效。检查this answer

    我认为您必须更改将值设置为 sessionStorage 的位置。我的建议是使用您自己的 StorageService (my example),您可以在其中将您的值设置为 storage 并保持一个 observable,当您设置 key/value 时它应该会发出。

    我做了一个example on stackblitz

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

    【讨论】:

    • 因此,fromEvent 检测存储中消息键中的值何时发生变化,并更新 message$,因为它是通过与异步管道的模板绑定订阅的,新值应自动更新DOM。但是,即使我刷新页面,它也不会发生任何事情,如果找到它,它会呈现任何值,但现在不再这样做了。但我确实喜欢这种方法,并希望更好地了解我所缺少的。
    • 是的,你没看错。使用 fromEvent 您可以使用任何本机浏览器事件(例如按钮单击,或者在本例中为“存储”事件),并将它们变成可观察的。但是我在 StorageEvent 上犯了一个错误。事实证明,此事件仅在您打开多个选项卡时触发。因此,唯一的解决方案是在您为存储设置新值的同时触发您自己的 observable。我用示例更新了我的答案
    • 另外,你可以找到我使用ReplaySubject而不是Subject here的原因
    【解决方案2】:

    这个帖子有点老了,但我也遇到了同样的问题。

    如@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;
      }
    

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

    【讨论】:

    • 这看起来很漂亮。我要试试这个
    【解决方案3】:

    由于您使用的是异步管道,因此您无需订阅组件,您可以将其取出。 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

    【讨论】:

    • 只是好奇,如果只有一个订阅者(或者,AKA 观察者——这就是为什么 Observable 让我如此困惑),为什么要使用 Subject 而不是新的 Observable?在我的情况下,只有一个订阅者,但是会话中消息键的更改可以来自应用程序中的任何位置,因此最好有一些东西来监视该值的更改,而不是在发送时手动发出发生了变化(如果我理解的话,这就是你的 updateTheSubject 方法会做的事情)。
    • 当您不确定何时何地更新值时,API 和设置更易于使用。您在原始帖子中的代码只会发出一个值,即当时会话存储的值。如果 Quake 的答案对你有用,我建议你把它作为你的第一选择,而不是我作为备份。
    • 感谢您的反馈和您的时间,这对我获得一些理解很有帮助。如果我可以在这里继续讨论:如果我的原始代码只发出一次,我认为 observables 跟踪值随时间变化。但是我的可观察到的显然没有,那是如何工作的?如果是 Observable 类型,但没有观察到,这是怎么回事?
    • 那时我建议打谷歌并在 Angular 之外做一些简单的教程。 RxJS 和 observables 帮助你处理流。它们并不是天生就创造出来的(尽管你绝对可以这样做)。
    猜你喜欢
    • 2017-11-01
    • 1970-01-01
    • 2017-11-30
    • 2018-07-13
    • 2020-11-13
    • 2020-06-19
    • 1970-01-01
    • 2017-07-25
    • 2019-08-16
    相关资源
    最近更新 更多