Angular app breaks when reloading. Probably lifecycle hook error

Claudio Ramírez Guichard from

I have an Angular app that post the firebase uid to my server and then it returns a response containing an array of objects to be displayed in a data table.

When I go to the data table from the default page, it works fine, but when I reload the app it breaks and I get Error trying to diff '[object Object]'. Only arrays and iterables are allowed.

I assume that error it is because its returning an error message and can't be displayed in the table.

I think that the problem is that when I reload the page, it does not get the user uid, so the parameter I send to my server is undefined and therefore it returns that error.

So, my question is how to get the user uid when reloading the data table component before I send the request to my server.

service:

private user: Observable<firebase.User>;
public userDetails: firebase.User = null;
uid: any;

  constructor(private http: HttpClient,
              private authFirebase : AngularFireAuth) {
                this.user = authFirebase.authState;
                this.user.subscribe(
                  (user) => {
                    if (user) {
                      this.userDetails = user;
                      console.log(this.userDetails.email);
                      this.uid = this.userDetails.uid;
                    }
                    else {
                      this.userDetails = null;
                    }
                  }
                );
               }

getFullRegistryUniversity(): Observable<DegreeDetails[]> {
    console.log(`serviceuid: ${this.uid}`);
    let uidString = {
      "uid" : this.uid
    }
    return this.http.post<DegreeDetails[]>(this.serviceUrl, uidString)
  }

component:

export class GraduateComponent implements OnInit {

  dataSource = new UserDataSource(this.registryService);
  displayedColumns = ['graduateRut', 'major', 'university', 'gradYear'];

  constructor(private registryService: RegistryService) { }

  ngOnInit() {}

}

export class UserDataSource extends DataSource<any> {
  constructor(private registryService: RegistryService) {
    super();
  }
  connect(): Observable<DegreeDetails[]> {

    return this.registryService.getFullRegistryUniversity();
  }
  disconnect() {}
}

EDIT:

html:

<div>
  <mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="graduateRut">
      <mat-header-cell *matHeaderCellDef> RUT </mat-header-cell>
      <mat-cell *matCellDef="let degreeDetails"> {{degreeDetails.graduateRut}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="major">
      <mat-header-cell *matHeaderCellDef> E-Mail </mat-header-cell>
      <mat-cell *matCellDef="let degreeDetails"> {{degreeDetails.major}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="university">
      <mat-header-cell *matHeaderCellDef> University </mat-header-cell>
      <mat-cell *matCellDef="let degreeDetails"> {{degreeDetails.university}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="gradYear">
      <mat-header-cell *matHeaderCellDef> GradYear </mat-header-cell>
      <mat-cell *matCellDef="let degreeDetails"> {{degreeDetails.gradYear}} </mat-cell>
    </ng-container>
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>

angular typescript firebase

Answers

answered 6 days ago dAxx_ #1

As you assumed, your are correct, you are calling the API before the uid has been initialized, hence, there is an error.
We can try to overcome this issue with many ways, I will provide one to start with.
First, change your uid in the service to BehaviorSubject:

uid = new BehaviorSubject<any>(null);

now in the constructor :

this.user.subscribe(
  (user) => {
    if (user) {
      this.userDetails = user;
      console.log(this.userDetails.email);
      this.uid.next(this.userDetails.uid);
    }
    else {
      this.userDetails = null;
    }
  }
);

the http post request it should change to support the BehviorSubject:

getFullRegistryUniversity(): Observable<DegreeDetails[]> {
    let uidString = {
      "uid" : this.uid.value // <-- here
    }
    return this.http.post<DegreeDetails[]>(this.serviceUrl, uidString)
  }

and add this get method:

getUidObservable() {
 return this.uid.asObservable()
}

In your component, dont initialize your datasource on the start, but wait for the UID to become a viable value.

dataSource: UserDataSource;

ngOnInit() {
 this.registryService.getUidObservable().subscribe(_uid => {
  if(_uid !== null) {
     dataSource = new UserDataSource(this.registryService);
   }
 }
}

and last thing, in your html, add to the wrap div :

<div *ngIf="dataSource">

I hope its clear, and fix your problem. There might be faster ways, but I think it will secure your calls.