Búsqueda de sitios web

typescript - No se obtiene el ID al iniciar sesión antes de actualizar la página - Angular


Estoy trabajando en mi proyecto de Angular y tengo este error donde no obtengo mi authService.currentUserId que uso para buscar en mis colecciones de Firebase Firestore, cuando navego a las páginas por primera vez después de iniciar sesión.

Tengo que actualizar la página para que funcione y luego no vuelvo a recibir el error. Este error no ocurre cuando uso mi función registerWithEmail y cargo la página, pero sí ocurre con mi función loginWithEmail.

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.componentes.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;
          })
        )
      );
  }
}

Respuesta #1

No veo ScheduleService inyectado en ningún componente. Necesitas inyectar ese servicio para poder usarlo y hacerlo después de AuthService, ya que el servicio de autenticación se inyecta en ScheduleService. AuthService debería inicializarse antes que ScheduleService.

Respuesta #2

Asumiendo que estás utilizando inyección de dependencias estándar, se instancia una nueva instancia de tu servicio cada vez, por lo que un campo en la clase del servicio no se almacenará, como authState.

Utiliza el almacenamiento local o el almacenamiento de sesión para guardar estos datos. Como ejemplo,

Para ahorrar:

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;
      });
  }

Para cargar:

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;
          })
        )
      );
  }

Y cuando el usuario cierre sesión, asegúrese de borrar todas las claves (o las claves que desee).

Para eliminar un elemento de localStorage, utiliza

localStorage.removeItem("uid");

Para limpiar el almacenamiento local, es decir, vaciarlo (eliminar todas las claves almacenadas en el almacenamiento local)

localStorage.clear();