Not getting ID on login before refreshing page - Angular

Viewed 339

I'm working on my Angular project and I have this error where I'm not getting my authService.currentUserId that I use for searching in my firebase firestore collections, when I'm routing to pages for the first time after i login.

I have to refresh the page to get it to work and then i don't get the error again. This error does not occur when I use my registerWithEmail function and load the page but not with my loginWithEmail function.

enter image description here auth.Service.ts

import {Injectable} from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import {Router} from '@angular/router';
import {AngularFirestore} from '@angular/fire/firestore';
import {CompanyUserInterface} from '../../models/companyuser';


@Injectable({
  providedIn: 'root'
})
export class AuthService {

  authState: any = null;

  constructor(private afAuth: AngularFireAuth, private router: Router, public fireService: AngularFirestore) {
    this.afAuth.authState.subscribe((auth => {
      this.authState = auth;
    }));
  }

  // is called when company register. creates firebase auth login with email and password
  // this also creates a document with the information from the registration in the collection companies
  registerWithEmail(email: string, password: string, companyUser: CompanyUserInterface) {
    console.log(email, password);
    return this.afAuth.createUserWithEmailAndPassword(email, password).then((data) => {
      console.log('reg email', data);
      this.authState = data;
      // Gets user id and added to company for unique id
      companyUser.UID = data.user.uid;
      return this.fireService.collection('companies').add(Object.assign({}, companyUser));
    }).catch(error => {
      console.log(error);
      throw error;
    });
  }

  // is called in login and checks if login info is correct in firebase
  loginWithEmail(email: string, password: string) {
    return this.afAuth.signInWithEmailAndPassword(email, password).then((data) => {
      this.authState = data;
    })
      .catch(error => {
        console.log(error);
        throw error;
      });
  }


  logout(): void {
    this.afAuth.signOut().then();
    this.router.navigate(['/login']).then();
  }

  // all firebase getter functions
  get isUserAnonymousLoggedIn(): boolean {
    return (this.authState !== null) ? this.authState.isAnonymous : false;
  }

  get currentUserId(): string {
    return (this.authState !== null) ? this.authState.uid : '';
  }

  get currentUserName(): string {
    return this.authState.email;
  }

  get currentUser(): any {
    return (this.authState !== null) ? this.authState : null;
  }

  get isUserEmailLoggedIn(): boolean {
    return (this.authState !== null) && (!this.isUserAnonymousLoggedIn);
  }
}

login.components.ts

import {Component, OnInit} from '@angular/core';
import {AuthService} from '../../services/auth/auth.service';
import {Router} from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  email = '';
  password = '';
  errorMessage = ''; // validation error handling
  error: { name: string, message: string } = {name: '', message: ''}; // for firebase error handling

  constructor(private authService: AuthService, private router: Router) {
  }

  ngOnInit(): void {
  }

  // checks with firebase is user info is inputted and then if its valid. routes to userinfo site
  login() {
    this.clearErrorMessage();
    if (this.validateForm(this.email, this.password)) {
      this.authService.loginWithEmail(this.email, this.password).then(() => {
          this.router.navigate(['schedule']).then();
        }).catch(error => {
        this.error = error;
        this.router.navigate(['login']).then();
      });
    }
  }
}

register.components.ts

import {Component, OnInit} from '@angular/core';
import {AuthService} from '../../services/auth/auth.service';
import {Router} from '@angular/router';
import {NgbModal, NgbModalConfig} from '@ng-bootstrap/ng-bootstrap';
import {CompanyUserInterface, CompanyUser} from '../../models/companyuser';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {

  companyUser: CompanyUserInterface;
  password = '';

  message = '';
  errorMessage = ''; // validation error handling
  error: { name: string, message: string } = {name: '', message: ''}; // for firebase error handling

  constructor(public authService: AuthService,
              private router: Router,
              private config: NgbModalConfig,
              private modalService: NgbModal,
  ) {
    // customize default values of modals used by this component tree
    config.backdrop = 'static';
    config.keyboard = false;
    this.companyUser = new CompanyUser('', '', '', '', '');
  }

  ngOnInit(): void {
  }

  // register company/association. checks if value are true before creating with authService
  // sends company info from registry to create new doc in collection
  registerWithEmail(content) {
    this.clearErrorMessage();
    console.log(this.companyUser);
    if (this.validateRegisterForm(this.companyUser.email, this.password)) {
      this.authService.registerWithEmail(this.companyUser.email, this.password, this.companyUser).then(() => {
        this.modalService.open(content);
        this.router.navigate(['userinfo']).then();
        this.message = '\nCongratulations on creating your profile\n'.toUpperCase();
      }).catch(error => {
        this.error = error;
        this.router.navigate(['register']).then();
      });
    }
  }
}

schedule.service.ts

import {Injectable} from '@angular/core';
import {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} from '@angular/fire/firestore';
import {ScheduleInterface} from '../../models/schedule';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {AuthService} from '../auth/auth.service';


@Injectable({
  providedIn: 'root'
})
export class ScheduleService {
  schedulesCollection: AngularFirestoreCollection<ScheduleInterface>;
  schedules: Observable<ScheduleInterface[]>;
  scheduleDoc: AngularFirestoreDocument<ScheduleInterface>;

  constructor(private afs: AngularFirestore, public authService: AuthService) {
  }

  getSchedules(): Observable<ScheduleInterface[]> {
    // I'm not getting my authService currentUserId on the first call.
    console.log('test in getSchedules', this.authService.currentUserId);
    return this.afs.collection('schedules', (ref) => ref
      .where('UID', '==', this.authService.currentUserId)
      .orderBy('user', 'asc'))
      .snapshotChanges()
      .pipe(
        map((changes) =>
          changes.map((a) => {
            const data = a.payload.doc.data() as ScheduleInterface;
            data.docRef = a.payload.doc.id;
            return data;
          })
        )
      );
  }
}
2 Answers

I don't see ScheduleService injected in any component. You need to inject that service in order to use it and inject it after AuthService as auth service is injected in ScheduleService. AuthService should initialize before ScheduleService does.

Assuming you are using standard dependency injection, a new instance of your service is instantiated each time, so therefore a field in the service class will not be stored, like authState.

Use local storage or session storage to store these data. As an example,

For saving:

loginWithEmail(email: string, password: string) {
    return this.afAuth.signInWithEmailAndPassword(email, password).then((data) => {
      // this.authState = data;
      // uid is the variable containing your value to save
      localStorage.setItem('uid', data.uid);
      // If you want to use the sessionStorage
      // sessionStorage.setItem('uid', data.uid);
    })
      .catch(error => {
        console.log(error);
        throw error;
      });
  }

For loading:

getSchedules(): Observable<ScheduleInterface[]> {
    return this.afs.collection('schedules', (ref) => ref
      .where('UID', '==', localStorage.getItem('uid')) // <--- Here
      .orderBy('user', 'asc'))
      .snapshotChanges()
      .pipe(
        map((changes) =>
          changes.map((a) => {
            const data = a.payload.doc.data() as ScheduleInterface;
            data.docRef = a.payload.doc.id;
            return data;
          })
        )
      );
  }

And when the user sign out make sure to clear all keys (or the keys that you want).

To drop an item from localStorage use

localStorage.removeItem("uid");

To clear local storage i.e to empty it (to remove all keys stored in local storage)

localStorage.clear();
Related