从另一个组件更新时如何检测服务变量的变化?

How can I detect service variable change when updated from another component?

我正在尝试从服务变量 (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 中,它始终打印默认值并且从未听过更改。

请帮我解决这个问题。

移动对服务的订阅,两个组件都可以访问这个值。如果你只需要一次值,你可以直接使用它(就像我在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()
    }
}

您还可以订阅 either/both 组件中的主题并在那里获取值:

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

如果您想了解更多关于 Subjects 的信息,请查看 here

@isherwood 让我走上正轨,谢谢!这是最符合我情况的问答

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

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

按照上面的 @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
    }
  });
}

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

这只是我的第二个 ionic 应用程序,所以如果我的 structuring/setup 出现严重错误,我非常希望收到有关此解决方案的评论:)

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