【问题标题】:How to get a Service to to run upon refreshing the page?如何在刷新页面后运行服务?
【发布时间】:2021-12-25 01:11:19
【问题描述】:

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

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

我遇到的问题在页面刷新时出现。当页面刷新时,似乎 HandleAuthService 运行得比其他所有东西都晚一点。所以组件无法检索到uid等相关信息。

以下是每个文件的 sn-ps 代码:

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。如果页面已刷新,则返回undefined。但是,如果用户在登录后(或从主页)直接导航到此页面,它就可以工作。这是为什么?如何确保 HandleAuthService 始终是最先运行的?

CrowboxdbService 也面临同样的问题 在此服务中,要从 RTDB 推送/更新/读取值,我需要用户 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!;
      }
    }

【问题讨论】:

    标签: angular firebase firebase-authentication angularfire


    【解决方案1】:

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

    我通常做的是将值分配给可观察对象,我认为也不需要 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 有自己的类型,但你也可以编写自己的接口。值得,以后对你有帮助! :)

    【讨论】:

    • 感谢您的帮助!然后我是否需要在订阅currentUser$ 时执行所有其他从一开始就运行的功能?例如,我有一个函数可以设置对 firebase RTDB 的引用:this.userReference = db.object(this.usersDataPath+${this.currentUserId}); 为了使其工作,我需要已经设置了 this.currentUserId。所以我认为我应该将这段代码粘贴到订阅中以使其正常工作?
    • 是的,如果您需要使用此用户信息执行某些操作,则需要在 subscribe 中执行,这就是它在 JS 中的工作方式。如果您需要根据用户信息执行另一个异步操作,您可以使用switchMap 更改流。您只需要注意取消订阅,因此我喜欢异步管道,因为它会为您取消订阅。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 2014-05-12
    • 2020-06-29
    相关资源
    最近更新 更多