Reputation:
I think it's a simple question for the most of you, but I need help to get data from my service.ts to component.ts and display it on my HTML..
This is my code in service.ts
async letzteKontakteAbrufen(userid: string) {
const docref = await getDocs(collection(this.afStore, 'Nutzer', userid, 'chats'));
docref.forEach(async (docu) => {
const docid = docu.data().id;
console.log('DOKUMENTID: ', docid);
const docRef = doc(this.afStore, 'Chat', docid);
await getDoc(docRef);
});
}
Now I'd like to get the documents from "Chat"-Collection to Component.ts:
this.meineKontakte = this.chatService.letzteKontakteAbrufen(userid);
And want to display it in Angular expressions:
<ion-list *ngFor="let kontakt of meineKontakte | async" (click)="waehleNutzer(kontakt.data().id)">
<ion-item>
<ion-icon name="person-circle-outline"></ion-icon>
<div *ngIf="kontakt.nicknameeins !== meinNickname">
{{ kontakt.nicknameeins }}
</div>
<div *ngIf="kontakt.nicknamezwei !== meinNickname">
{{ kontakt.nicknamezwei }}
</div>
<div id="rechtsunten">
<ion-label>{{kontakt.zeit?.toMillis() | date:'dd.MM.yy HH:mm'}}</ion-label>
</div>
</ion-item>
But there is no data on my HTML page, but I'm sure that "await getDoc(docRef).data() is working. Hope someone can help..
Upvotes: 1
Views: 255
Reputation: 1061
I have never used the async pipe (in ngFor) for promises, only observables. So this was a litle fun to discover and explore.
While testing, in a similar situation I manged to to display the data when i wrapped the (in your situation <ion-list></ion-list>
tag in a <ng-container *ngIf="meineKontakte | async as contacts">
One of the reasons the data didn't get rendered without this is beacause the result of the Promise of the meineKontakte was undefined when the view first rendered. The *ngIf prevents the renderer to crash because this prevents the ngfor to loop over the undefined value.
For you this would look like:
<ng-container *ngIf="meineKontakte | async as kontakter"> <!-- added the results of the promise to a new variable called kontakter -->
<ion-list *ngFor="let kontakt of kontakter"> <!-- loop on kontakter instead of meineKontakte -->
...
</ion-list>
</ng-container>
The <ng-container>
tag is just a placeholder that you can insert logic into without adding extra elements to your DOM.
Another reason your data doesnt show is because in your service.ts file, your letzteKontakteAbrufen function doesn't actually return anything to your component. You should make sure it returns the Promise. I wasn't able to understand exactly what your letzteKontakteAbrufen function does, but make sure it returns a Promise:
async letzteKontakteAbrufen(userid: string) {
const docref = await getDocs(collection(this.afStore, 'Nutzer', userid, 'chats'));
...
return docref
}
or maybe something like this:
letzteKontakteAbrufen(userid: string) {
return getDocs(collection(this.afStore, 'Nutzer', userid, 'chats'));
}
Another solution Another way to solve this, is to store the results of the promise in meinKontakte instead of the promise itself (this is the way I usually do it).
async yourFunctionName(userId:string):Promise<void> {
try {
this.meineKontakte = await this.chatService.letzteKontakteAbrufen(userid: string)
} catch(error) {
console.error(error);
}
}
and then remove the async pipe in your html. You still need to change your letzteKontakteAbrufen function to return the await'ed data. And make sure your call the yourFunctionName function from ngOnInit or the constructor;
I hope this helps you in finding your final solution.
Edit
As a sidenote, always wrap your code in your functions in a try{...} catch(error) {console.error(error)}
block when you deal with Promises. This helps you to detect and handle errors :)
Edit 2 - converting promise to an observable
There are different methods to creating and using observables. One relatively easy and simple way I like to do it is using the BehaviourSubject observable.
In your service file, first create a new variable to hold the observable and change your letzteKontakteAbrufen to update that observable with the data instead of returning data:
$contacts = new BehaviorSubject<Array<any>>([])
// you should crate an interface to pass instead of using any, it would make it easier for you, but I don't know how your data looks like so I used any;
async letzteKontakteAbrufen(userid: string) {
const docref = await getDocs(collection(this.afStore, 'Nutzer', userid, 'chats'));
... // if you need to change your data for something do it here (reordering, retrieving extra information etc...)
this.$contacts.next(docref) //inside the next function, add the data you want to show on your page
}
then inside your component.ts file you need to store a refference to the observable into a variable and to trigger the letzteKontakteAbrufen function in ngOnInit
...
$contacts = this.chatService.$contacts;
ngOnInit():void {
this.chatService.letzteKontakteAbrufen(this.userId)
}
...
And the in your html file:
<ion-list *ngFor="let kontakt of $contacts | async">
...
</ion-list>
Example on an interface: Write the interface in a sepperate file, for example called contacts.interface.ts or outside of the class in your service file. And then replace the any keyword with the name of the interface:
export interface Kontakt {
userid: string;
imageUrl?: string; // the question marks makes the property optional
firstname: string;
lastname:string;
email:string
// ... etc
}
and then change your Observable to
$contacts = new BehaviourSubject<Array<Kontakt>>([])
if your data retrieved in your letzteKontakteAbrufen is not an array you need to remove the Array keyword and only use Kontakt keyword (but then the ngfor wouldt work either).
Upvotes: 1