如何在刷新页面时获取到 运行 的服务?

How to get a Service to to run upon refreshing the page?

我正在通过 angular/angularfire 库使用 Angular 和 Firebase。我有两个服务:管理用户身份验证的 HandleAuthService 和管理实时数据库的 CrowboxdbService。目前,在我的主页组件中,我正在检查用户是否已登录。如果他们已登录,他们可以访问其他站点页面。如果他们没有登录,我会为他们提供一个按钮来注册或登录。这样做后,他们将被重定向到另一个页面,在那里他们可以查看他们的数据(在 data.component.ts).

当用户首次注册或首次登录时,我没有遇到任何问题。我的所有组件都可以从 HandleAuthService 访问 authState,因此可以访问诸如用户名和uid。我使用 uid 更新 RTDB 中的用户数据。

我遇到的问题是在页面刷新时出现的。当页面刷新时,似乎 HandleAuthService 运行s 比其他一切都晚了一点。所以组件无法检索相关信息,例如 uid.

以下是每个文件的代码片段:

app.module.ts: 在这里,我将 HandleAuthService 设置为提供者:

//imported the service (as well as in the angular import)
import { HandleAuthService } from './services/shared/handle-auth.service';
//set it as a provider for the other components
providers: [HandleAuthService]

HandleAuthService

  constructor(private fireAuth: AngularFireAuth, private ngZone: NgZone, private router: Router) { 
    /* initialise the currentUserId by retrieving it from the authState */

    this.fireAuth.authState.subscribe(user => {
      if (user) {
        console.log("User is LOGGED IN");
        this.currentUserState = {
          uid: user.uid!,
          email: user.email!,
          displayName: user.displayName!
        };

        console.log("Current User State is:");
        console.log(this.currentUserState);
        //set the object in the localstorage
        localStorage.setItem('user', JSON.stringify(this.currentUserState));
      } else {
        console.log("User is not logged in?");
        localStorage.setItem('user', "null");
      }
    })
  }

  login() {
    /* Sign in or Sign Up with google's pop up.*/
    return this.googleLogin( new firebase.auth.GoogleAuthProvider());
  }


  googleLogin(provider:any) {
    return this.fireAuth.signInWithPopup(provider)
    .then((result)=> {
      this.ngZone.run(() => {
        this.router.navigate(['data']);
      });
      this.setUser(result.user);
    })
    .catch((error) => {
      console.log(error);
    })
  }

  setUser(result:any) {
    this.currentUserState = {
      uid : result.uid,
      email : result.email,
      displayName : result.displayName
    };
  }

现在,在 data.component.ts 中,我调用 HandleAuthService 并使用它,只要它有效由于页面尚未刷新。

constructor(private authService: HandleAuthService, private crowboxService: CrowboxdbService) {}
  
ngOnInit(): void {
    //upon the view rendering, get the User Id 
    this.currentUserId = this.authService.currentUserState?.uid;
    console.log("Current User Id is " );
    console.log(this.currentUserId);

    this.checkIfUserExists();
  }

ngOnInit()中,我尝试打印出用户ID。如果页面已刷新,则 returns undefined。但是,如果用户在登录后(或从主页)直接导航到该页面,它就可以工作。这是为什么?我怎样才能确保 HandleAuthService 总是第一个成为 运行?

CrowboxdbService 也面临同样的问题 在此服务中,对于来自 RTDB 的 push/update/read 值,我需要用户 ID。当用户首次登录时,我可以从 HandleAuthService 中获取用户 ID。但是,刷新页面后,此服务无法从 HandleAuthService 获取 ID,而是求助于从本地存储获取 ID。但是,我不想这样做,因为它对已在 firebase 中设置的受限身份验证规则没有帮助。


  constructor(private db: AngularFireDatabase, private handleAuth: HandleAuthService) {
     
    //try to get the user id from handleAuth (if this is the first time loggin in)
    this.currentUserId = this.handleAuth.currentUserState?.uid;
  }

    //if you cannot get the user id from handleAuth, then get it from the localStorage
    if(!this.currentUserId) {
      console.log("Error in crowboxdb Service - Cannot retrieve user id from handleAuth");
      //get the user information from the local storage
      const item = localStorage.getItem('user');
      if (item !=='undefined' && item!==null) {
        const currentUser = JSON.parse(item);
        this.currentUserId = currentUser.uid!;
      }
    }

是的,这是异步的,所以刷新后需要一段时间才能设置用户。这就是它的工作原理并尝试接受它:)

我通常做的是将值赋给一个 observable,在我看来也不需要 localStorage,firebase 总是会发出给你,这就是它的美妙之处。如果存在,您总是从 authstate 获取用户。所以我会如前所述分配给一个可观察对象并在组件中订阅它。如果你附上take(1),你不需要退订,它只会发出一次。所以服务:

 currentUser$ = this.fireAuth.authState.pipe(
   map(user => {
     if (user) {
       return { uid: user.uid,  email: user.email, displayName: user.displayName }
     }
     return null;
   })
 );    

现在无论你在哪里需要此用户信息,只需订阅即可:

ngOnInit(): void {
  this.authService.currentUser$.subscribe(user => console.log(user))
}

更好的是,如果您在模板中使用此用户信息,而不是使用 subscribe,请使用 async 管道。另外我建议你输入你的数据,angularfire 有自己的类型,但你也可以编写自己的接口。值得,以后对你有帮助! :)