0

i have a messaging room application that create a discussion chat foreach room between users signed in with same room , i m facing this error :

Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

i used 3 functions :

  • getChatMessages() : to get all chat messages from firestore for users with same room
  • getCurrentRoom():to get the room of the connected user
  • getUsers(): return all users with same room

chat.services.ts

import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/compat/auth';
import { AngularFirestore } from '@angular/fire/compat/firestore';
import { Observable } from 'rxjs';
import { Timestamp } from 'rxjs/internal/operators/timestamp';
import { switchMap,map, timestamp, filter } from 'rxjs/operators';
import { query, orderBy, limit, where } from "firebase/firestore";  
import firebase from 'firebase/compat/app';

 
export interface User {
  uid: string;
  email: string;
  displayName:string;
  username?:string;
  room?:string
}
 
export interface Message {
  createdAt: firebase.firestore.FieldValue;
  id: string;
  from: string;
  msg: string;
  fromName: string;
  myMsg: boolean;
}
 
@Injectable({
  providedIn: 'root'
})
export class ChatService {
  currentUser: User ;
  currentRoom:string="";
  updatedRoom:string="";
  constructor(private afAuth: AngularFireAuth, private afs: AngularFirestore) {
    this.afAuth.onAuthStateChanged((user) => {
      this.currentUser=user;
      console.log("current email"+this.currentUser.email);
           
  });
}
  async signup({ username,email, password,room }): Promise<any> {
     
    const credential = await this.afAuth.createUserWithEmailAndPassword(
      email,
      password
    );
 
    const uid = credential.user.uid;
 
    return this.afs.doc(
      `users/${uid}`
    ).set({
      uid,
      email: credential.user.email,
      username:username,
      room:room,
    })
  }
 
  signIn({ email, password }) {
    return this.afAuth.signInWithEmailAndPassword(email, password);
  }
 
  signOut(): Promise<void> {
    return this.afAuth.signOut();
  }

  addChatMessage(msg) {
    return this.afs.collection('messages').add({
      createdAt:firebase.firestore.FieldValue.serverTimestamp(),//firebase.default.firestore.Timestamp,
      msg: msg,
      from: this.currentUser.uid
    });
  }
   
   

   async getChatMessages() {
    let users = [];
    return   (await this.getUsers()).pipe(
      switchMap(res => {
        users = res;
        console.log("resssssss"+res);
        return this.afs.collection('messages', ref => ref.orderBy('createdAt','asc')).valueChanges({ idField: 'id' }) as Observable<Message[]>;
      }),

      map(messages => {
        console.log("messages"+messages);
        // Get the real name for each user
        for (let m of messages) {    
          m.fromName = this.getUserForMsg(m.from, users);
          m.myMsg = this.currentUser.uid === m.from;
        }      
        return messages
      })       
    )
  }
   
 


  public async getCurrentRoom() {
    await this.afs.collection('users', ref => ref.where("email", "==", this.currentUser.email)).get().toPromise()
        .then(snapshot => {
            snapshot.forEach(doc => {
              this.currentRoom=JSON.parse(JSON.stringify(doc.data())).room;
              console.log("current room" + this.currentRoom);
            });
        });
}
   
public async getUsers() {
  console.log("this room" + this.currentRoom);
  return this.afs.collection('users', ref => ref.where("room", "==", this.currentRoom)).valueChanges({
      idField: 'uid'
  }) as Observable < User[] > ;
}

  private getUserForMsg(msgFromId, users: User[]): string { 
    for (let usr of users) {
      if (usr.uid == msgFromId) {
        return usr.username ?? 'undefind';
      }
    }
    return 'Deleted';
  }


}

my chat.page.ts :

import { Component, OnInit, ViewChild } from '@angular/core';
import { IonContent } from '@ionic/angular';
import { Observable } from 'rxjs';
import { ChatService } from '../chat.service';
import { Router } from '@angular/router';
import { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/compat/storage';
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/compat/firestore';
import { finalize, tap } from 'rxjs/operators';
export interface FILE {
  name: string;
  filepath: string;
  size: number;
}
@Component({
  selector: 'app-chat',
  templateUrl: './chat.page.html',
  styleUrls: ['./chat.page.scss'],
})


export class ChatPage implements OnInit {
  ngFireUploadTask: AngularFireUploadTask;

  progressNum: Observable<number>;

  progressSnapshot: Observable<any>;

  fileUploadedPath: Observable<string>;
  room:any;
  files: Observable<FILE[]>;
ImgtoSend:any;
  FileName: string;
  FileSize: number;

  isImgUploading: boolean;
  isImgUploaded: boolean;

  private ngFirestoreCollection: AngularFirestoreCollection<FILE>;


  @ViewChild(IonContent) content: IonContent;
 
  messages:any=[];
  newMsg = '';
 
  constructor(private angularFirestore: AngularFirestore,
    private angularFireStorage: AngularFireStorage,private chatService: ChatService, private router: Router) { 
      this.isImgUploading = false;
      this.isImgUploaded = false;
      
      this.ngFirestoreCollection = angularFirestore.collection<FILE>('filesCollection');
      this.files = this.ngFirestoreCollection.valueChanges();

    }
 
  ngOnInit() {
    this.messages= this.chatService.getChatMessages();
  }
    
  sendMessage() {
    this.chatService.addChatMessage(this.newMsg).then(() => {
      this.newMsg = '';
      this.content.scrollToBottom();
    });
  }
 
  signOut() {
    this.chatService.signOut().then(() => {
      this.router.navigateByUrl('/login', { replaceUrl: true });
    });
  }








  fileUpload(event: FileList) {
      
    const file = event.item(0)

    if (file.type.split('/')[0] !== 'image') { 
      console.log('File type is not supported!')
      return;
    }

    this.isImgUploading = true;
    this.isImgUploaded = false;

    this.FileName = file.name;

    const fileStoragePath = `filesStorage/${new Date().getTime()}_${file.name}`;
    console.log("filestoragepath"+fileStoragePath);
    const imageRef = this.angularFireStorage.ref(fileStoragePath);
    console.log("image ref"+imageRef);
         
    this.ngFireUploadTask = this.angularFireStorage.upload(fileStoragePath, file);
    this.ImgtoSend=this.FileName;
    console.log("image to Send"+this.ImgtoSend);
    this.progressNum = this.ngFireUploadTask.percentageChanges();
    this.progressSnapshot = this.ngFireUploadTask.snapshotChanges().pipe(
      
      finalize(() => {
        this.fileUploadedPath = imageRef.getDownloadURL();
        console.log("uploaded path"+this.fileUploadedPath);

        this.fileUploadedPath.subscribe(resp=>{
          this.fileStorage({
            name: file.name,
            filepath: resp,
            size: this.FileSize
          });
          this.isImgUploading = false;
          this.isImgUploaded = true;
        },error => {
          console.log(error);
        })
      }),
      tap(snap => {
          this.FileSize = snap.totalBytes;
      })
    )
}


fileStorage(image: FILE) {
    const ImgId = this.angularFirestore.createId();
    
    this.ngFirestoreCollection.doc(ImgId).set(image).then(data => {
      console.log("data"+data);
    }).catch(error => {
      console.log(error);
    });
}  

 
}
enter code here

1 Answer 1

1

First, you have to eleminate all the code that is not relevant to the question to help people help you.

Second, the issue is simple:

What the compiler is telling you here is, Hey Mohammed Amir, *ngFor="" is used to loop through an Array of objects while you are passing to it an object literal.

Check the value of the property you bind to *ngFor="let msg of [YourAssumedArrayFromEndpointResponse]" in your template you will find that YourAssumedArrayFromEndpointResponse is not an array. That's why the compiler is complaining

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.