【问题标题】:How can I detect service variable change when updated from another component?从另一个组件更新时,如何检测服务变量更改?
【发布时间】:2017-08-26 19:29:27
【问题描述】:

我正在尝试从服务变量 (isSidebarVisible) 中获取更新后的值,该服务变量由另一个组件 (header) 通过点击事件 (toggleSidebar) 不断更新。

sidebar.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable() 
export class SidebarService {
    isSidebarVisible: boolean;

    sidebarVisibilityChange: Subject<boolean> = new Subject<boolean>();

    constructor()  {
        this.isSidebarVisible = false;
    }

    toggleSidebarVisibilty() {
        this.isSidebarVisible = !this.isSidebarVisible
        this.sidebarVisibilityChange.next(this.isSidebarVisible);
    }
}

sidebar.component.ts

export class SidebarComponent implements OnInit {
    asideVisible: boolean;
    _asideSubscription: any;

    constructor(private sidebarService: SidebarService) {
        this.asideVisible = sidebarService.isSidebarVisible
        this._asideSubscription = sidebarService.sidebarVisibilityChange.subscribe((value) => {
            this.asideVisible = value
        })
    }
    
    ngOnInit() {
    }
}

header.component.ts(更新服务变量的地方)

export class HeaderComponent implements OnInit {
    isSidebarVisible: boolean;
    _subscription: any;

    constructor(private sidebarService: SidebarService) {
        this._subscription = sidebarService.sidebarVisibilityChange.subscribe((value) => {
            this.isSidebarVisible = value
        })
    }

    toggleSidebar() {
        this.sidebarService.toggleSidebarVisibilty()
    }

    ngOnInit() {

    }
}

{{ isSidebarVisible }} 时,我可以看到header.component.html 中的服务变量值发生了变化,但在sidebar.component.html 中,它总是打印默认值并且从未听过更改。

请帮我解决这个问题。

【问题讨论】:

    标签: angular angular2-services


    【解决方案1】:

    移动订阅到服务,两个组件都可以访问这个值。如果你只需要一次 value,你可以直接使用它(就像我在 sidebar.component 中所做的那样);如果您需要使用此值更新某些内容,您可以使用 getter(header.component 中的示例)。

    sidebar.service.ts:

    @Injectable() 
    export class SidebarService {
        isSidebarVisible: boolean;
    
        sidebarVisibilityChange: Subject<boolean> = new Subject<boolean>();
    
        constructor()  {
            this.sidebarVisibilityChange.subscribe((value) => {
                this.isSidebarVisible = value
            });
        }
    
        toggleSidebarVisibility() {
            this.sidebarVisibilityChange.next(!this.isSidebarVisible);
        }
    }
    

    sidebar.component.ts

    export class SidebarComponent {
        asideVisible: boolean;
    
        constructor(private sidebarService: SidebarService) {
            this.asideVisible = sidebarService.isSidebarVisible;
        }
    }
    

    header.component.ts

    export class HeaderComponent {
        constructor(private sidebarService: SidebarService) { }
    
        get isSidebarVisible(): boolean {
            return this.sidebarService.isSidebarVisible;
        }
    
        toggleSidebar() {
            this.sidebarService.toggleSidebarVisibility()
        }
    }
    

    您还可以订阅任一/两个组件中的主题并在那里获取值:

    this.sidebarService.sidebarVisibilityChange.subscribe(value => {...});
    

    如果您想了解有关主题的更多信息,请查看here

    【讨论】:

    • 谢谢!如果我想在 header.component 中打印 isSidebarVisible() 的返回值,我该怎么做?
    • &lt;h1&gt;{{ isSidebarVisible | json }}&lt;/h1&gt; (:
    • 谢谢!这真的很有帮助。
    • 我花了很多时间在我的项目上,以此作为参考。我知道它应该如何工作,但事实并非如此。我缺少的信息是关于分层注入的。一旦我明白我的新全局级服务需要在 app.module 中的提供者(而不是子组件中)中引用,以便信息可以沿树继承,一切都会完美运行。
    • 订阅者是如何被销毁的?谁做的?
    【解决方案2】:

    @isherwood 让我走上正轨,谢谢!这是最像我的情况的问题和答案。

    就我而言,我有一个应用范围的 userService 来监听 AngularFireAuth (authState) 的变化,当存在有效用户时,它会设置一个监听器该用户的 AngularFirestore 文档,包含用户的信息,例如姓名、电子邮件、颜色等 (/appusers/${user.id})。所有需要从 userService 获取任何内容的页面都会实时获取这些数据,因为它是一个侦听器( .subscribe() )

    我的问题是,在继续执行更多数据库操作之前,依赖于用户文档的页面已准备好(登录并获取用户文档)。当通过深层链接直接导航到内部页面时,该页面会在用户文档准备好之前尝试执行数据库操作,从而导致错误/崩溃。

    按照上面的@isherwoods 说明,这就是我如何确保我的应用页面等待用户数据准备好,然后再尝试使用它执行其他数据库操作:

    user.service.ts

    export class UserService {
    
    userDocumentReady: boolean = false; // initial value is "userdoc is not ready"
    userDocObserver: Subject<boolean> = new Subject<boolean>(); // observing that bool
    
    constructor(
      public fireauth: AngularFireAuth,
      public afs: AngularFirestore,
    ) {
    
    // watch variable
    this.userDocObserver.subscribe(value => this.userDocumentReady = value);
    
    this.fireauth.authState
      .subscribe(user => {
        if (user) {
    
          this.afs.doc(`/tooluser/${user.uid}`).snapshotChanges()
          .subscribe(usr => {
            if (usr.payload.data()) {
              console.log(`got user object from database:`, usr.payload.data());              
              this.currentUser = usr.payload.data();
    
              this.userDocObserver.next(true); // flip UserDocumentReady flag
    

    mytools.page.ts

    // this is a logged-in page of the app,
    // accessible through deep link https://my.app.domain/mytools
    
    export class UsersPage implements OnInit {
    
    constructor(
      public userService: UserService,
    ) { }
    
    ngOnInit() {
      if (this.userService.userDocumentReady) {
        console.log(`users: userdoc already here, no need to wait`);
        this.setupPageData();
        return;
      }
    
      // wait for userdoc before setting up page
      this.userService.userDocObserver.subscribe(docReady => {
        if (docReady){
          console.log(`mytools: user ready value ${docReady}. now setup page.`);
          this.setupPageData(); // this function does the database stuff that depends on the user data to be ready first
        }
      });
    }
    

    这个“workforme”现在和行为似乎是一致的。在开发时也非常方便,因为在浏览器栏中键入 URL 会导致这种直接导航。写这篇文章是为了记住它——谁知道它是否对其他人有用?

    这只是我的第二个 ionic 应用程序,所以如果我的结构/设置出现严重错误,我对这个解决方案中的 cmets 非常感兴趣 :)

    编辑:在 mytools.page.ts 中添加了检查,这样我们就不会(永远)等待新的用户文档(如果已经存在)

    【讨论】:

      猜你喜欢
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-23
      • 2020-07-30
      • 2019-05-02
      • 1970-01-01
      • 2021-03-30
      相关资源
      最近更新 更多