Angular Material MatTableDataSource with Firestore
This may work better for the getAccounts method:
getAccountsX() {
return this.afs.collection<Account[]>('accounts').snapshotChanges().map((accounts) => {
return accounts.map(a => {
const data = a.payload.doc.data() as Account;
const id = a.payload.doc.id;
return { id, ...data }
});
});
}
I have never tried making a firestore call in the constructor of the service but always make the database calls in a method that gets called during the ngOnInit in my component.
So in the component you could have a object accounts: Observable<Account[]>
that is of type Observable<Account[]>
and set it to equal getAccountsX(). Then in your markup I would *ngIf the entire table like this: *ngIf="(accounts | async) as acts"
. Then the dataSource would actually be acts
. I have never used the DataTable yet but this is just an approach I would take to try and keep the subscription to the data active. If you want I can EDIT your question with this.
EDIT:
Here is an explanation of two separate ways to handle that subscription:
So here in my Component I am fetching the Observable and then also subscribing to it to save the array of whatever data model you are fetching:
accountsObservable: Observable<Account[]>;
accountsArray: Account[];
constructor(
private ds: DatabaseService
) {}
ngOnInit() {
this.accountsObservable = this.ds.getAccountsX();
this.accountsObservable.subscribe(accounts => {
this.accountsArray = accounts;
});
}
Then here in my markup you can create the subscription using *ngFor and the ASYNC pipe, or simply loop through the array after it has been acknowledged by the subscription:
<!-- This div below is subscribing to the Observable in markup using the 'async' pipe to make sure it waits for data -->
<div id="markup-subscription" *ngFor="let act of (accountsObservable | async)">
<p>{{ act?.id }}</p>
</div>
<!-- This div below is looping through the array that was pulled from the subscription of the Observable -->
<div id="component-subscription" *ngFor="let act of accountsArray">
<p>{{ act?.id }}</p>
</div>
One reason for waiting for the subscription in the Component code is if there is a need to manipulate the data before spitting it out on the UI. I believe if you are using the second option of subscribing in the Component code instead of your markup you would want to make sure the *ngFor isn't trying to loop through an empty Array as the subscription may not have set the array before the content wants to load on the DOM. So I would *ngIf
the accountsArray to make sure it is set first like so:
<div id="component-subscription" *ngIf="accountsArray" *ngFor="let act of accountsArray">
Granted this is not using the MatDataTable as I wanted to show an example of how these subscriptions work and the goal is to use one subscription
In regards to unsubscribing, the reason that is not an option is because you must set the Observable subscription to a variable like so:
const subscription = this.accountsObservable.subscribe(accounts => {
this.accountsArray = accounts;
});
subscription.unsubscribe();
I hope this can help explain the state of the subscription as you are looping through the collection or document in the UI.