Search code examples
angularfirebaseionic-frameworkdata-binding

Angular freezing when binding a function


TL;DR, how can I bind a function like so without having the browser freeze?

<ion-chip *ngFor="let m of chatroom.members">
    <ion-label>{{getName(m.uid)}}</ion-label>
</ion-chip>

The idea is that I have a chat room and I would like to display the users who are members in it. Here is my Firebase data:

Firebase

The idea is that only the uids of the users will be stored in an array called members. When it comes to retrieving from the database, the following service retrieves a user based on the uid:

GetUserFromDatabaseService.ts

import { User } from 'src/app/objects/classes/user';
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';

@Injectable({
  providedIn: 'root'
})
export class GetUserFromDatabaseService {
  constructor(private afs: AngularFirestore) { }

  async retrieveUser(uid: string) {
    let user: User = null;
    await this.afs.collection('USERS').doc(uid).ref.get().then(async function (doc) {
      if (doc.exists) {
        user = doc.data() as User;//User is my custom class that has the name of the user.
      } else {
        console.log("There is no document!");
      }
      return user;
    }).catch(function (error) {
      console.log("There was an error getting your document:", error);
    });
    return user;
  }
}

In my chatroom.ts, this is what I have:

chatroom.ts

...
currentMemberName: string = '';
...
async getName(uid: string): Promise<string> {
    this.currentMemberName = (await this.getUserFromDataBase.retrieveUser(uid)).name;
    return this.currentMemberName;
}
...

In my chatroom.page.html, I will loop through the members array and I want to retrieve the names of all the users based on their uids. It will look something like:

chatroom.page.html

...
<ion-chip *ngFor="let m of chatroom.members">
    <ion-label>{{getName(m.uid)}}</ion-label>
</ion-chip>
...

My solution:

The solution that I have come up with is to create a new array (call it names) which basically pre-retrieves the names from the database and store them as strings. My chatroom.page.html would then look like:

...
<ion-chip *ngFor="let n of names">
    <ion-label>{{n}}</ion-label>
</ion-chip>
...

I am not too happy with it as I will store twice the data (at least temporarily). Is there a way to make this more efficient?

Thank you in advance :)


Solution

  • Due to async await, your program is waiting for the output in synchronous way. This results in freezing application.

    Now Javascript is single threaded language, so if you wait for something then your application will freeze. For this we can use Promise, with Promise we will get callback when there is value available. This is async approach.

    Service

    import { AngularFirestore } from '@angular/fire/firestore';
    
    @Injectable({
      providedIn: 'root'
    })
    export class GetUserFromDatabaseService {
      constructor(private afs: AngularFirestore) { }
    
      retrieveUser(uid: string) {
        return this.afs.collection('USERS').doc(uid).ref.get();
      }
    }
    

    chatroom.ts (call getNames function after receiving chatroom.member)

    memberNames: string[] = [];
    
    getNames() {
        chatroom.members.forEach(member => {
            this.getUserFromDataBase.retrieveUser(member.uid).then(doc => {
                this.memberNames.push((doc.data() as User).name);
            });
        });
    }
    

    chatroom.page.html

    <ion-chip *ngFor="let m of memberNames">
        <ion-label>{{m}}</ion-label>
    </ion-chip>