Hi,
I’m implementing ionic firebase ( firestore ) pagination feature with infinite scroll and crud.
When I scroll down, it looks working. At the end of data it disable infinite scroll as well.
After that I implemented update/delete features which shows a bug.
The problem is, when I do update/delete action it trigger observable subscribe method which added up the result ( not merge and create another Observable)
My question is ( not sure if this is valid point though )
-
How to prevent adding up new result to existing result, when I update/delete action.
-
How to merge multiple observable to one single observable ( concat )
-
In this use case, Is Subscription better than Observable?
Any suggestion would be greatly appreciated.
Thanks.
Here is a source code.
// Test.ts
interface Test {
doc?: any;
id: number;
title: string;
}
//home.page.html
<ion-header>
<ion-toolbar color="primary">
<ion-title>Total {{testList?.length}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let test of testList">
<ion-label class="ion-text-wrap">
<p>{{test.id}}</p>
<p>{{test.title}}</p>
</ion-label>
<ion-button color="secondary" (click)="deleteTestDataById(test.doc.id)">
<ion-icon slot="icon-only" name="trash"></ion-icon>
</ion-button>
</ion-item>
</ion-list>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button>
<ion-icon name="add"></ion-icon>
</ion-fab-button>
<ion-fab-list side="top">
<ion-fab-button color="light" (click)="deleteAllTestData()">
<ion-icon name="close"></ion-icon>
</ion-fab-button>
<ion-fab-button color="light" (click)="addAllTestData()">
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab-list>
</ion-fab>
<ion-infinite-scroll (ionInfinite)="getMoreData($event)">
<ion-infinite-scroll-content loading-spinner="circles" loading-text="Loading...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>
//home.page.ts
import {Component, OnInit, ViewChild} from '@angular/core';
import {Subscription} from 'rxjs';
import {IonInfiniteScroll} from '@ionic/angular';
import {DataService} from '../services/data.service';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
// result, I also tested with Observable<any> but same.
testList: Test[] = [];
testSubscription: Subscription;
// 15 records per page
recordsPerPage = 15;
// firestore next cursor
startAfterCursor: any;
// End of data check
endOfData = false;
@ViewChild(IonInfiniteScroll) infinite: IonInfiniteScroll;
constructor(private dataService: DataService) {
}
ngOnInit(): void {
this.getMoreData(null);
}
getMoreData(event: any) {
this.testSubscription = this.dataService.getPaginatedData(this.recordsPerPage, this.startAfterCursor)
.subscribe(result => {
// concat returned data to existing result
this.testList = this.testList.concat(result.slice(0, this.recordsPerPage));
if (this.recordsPerPage > result.length) {
this.endOfData = true;
this.infinite.disabled = true;
} else {
this.endOfData = false;
this.infinite.disabled = false;
}
// for next call, save last firestore doc info from the result
if (result[this.recordsPerPage - 1] !== undefined) {
this.startAfterCursor = result[this.recordsPerPage - 1].doc;
}
});
if (event) {
event.target.complete();
}
}
deleteTestDataById(id: any) {
this.dataService.deleteTestDataById(id);
}
deleteAllTestData() {
this.dataService.deleteAllTestData();
}
addAllTestData() {
this.dataService.addAllTestData();
}
}
//data.service.ts
import {Injectable} from '@angular/core';
import {AngularFirestore, QueryFn} from '@angular/fire/firestore';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {TestData} from '../model/TestData';
@Injectable({
providedIn: 'root'
})
export class DataService {
FIREBASE_TEST_NODE = 'FB_TEST_NODE';
constructor(private angularFireStore: AngularFirestore) {
}
getPaginatedData(recordsPerPage, cursor?): Observable<any> {
let queryFn: QueryFn;
if (cursor === undefined) {
queryFn = value => value.orderBy('id').limit(recordsPerPage);
} else {
queryFn = value => value.orderBy('id').limit(recordsPerPage).startAfter(cursor);
}
return this.angularFireStore
.collection(this.FIREBASE_TEST_NODE, queryFn)
.snapshotChanges()
.pipe(
// typical data mapping
map(result => {
return result.map(allData => {
const data = allData.payload.doc.data();
const doc = allData.payload.doc;
return {doc, ...(data as object)};
});
})
);
}
deleteTestDataById(id: any) {
return this.angularFireStore.doc(this.FIREBASE_TEST_NODE + '/' + id).delete();
}
addAllTestData(): any {
TestData.PaginationTestData
.forEach(data => {
this.angularFireStore.collection(this.FIREBASE_TEST_NODE)
.add(data);
}
);
}
deleteAllTestData(): any {
const listTestDBRef = this.angularFireStore.collection(this.FIREBASE_TEST_NODE)
.get()
.subscribe(result => {
result.forEach(element => {
element.ref.delete();
});
});
}
}
//TestData.ts
export const TestData = {
PaginationTestData: [
{
id: 1,
title: 'Test1 Title',
},
{
id: 2,
title: 'Test2 Title',
},
{
id: 3,
title: 'Test3 Title',
},
{
id: 4,
title: 'Test4 Title',
},
{
id: 5,
title: 'Test5 Title',
},
{
id: 6,
title: 'Test6 Title',
},
{
id: 7,
title: 'Test7 Title',
},
{
id: 8,
title: 'Test8 Title',
},
{
id: 9,
title: 'Test9 Title',
},
{
id: 10,
title: 'Test10 Title',
},
// 40 more records for test.
]
};
1 post - 1 participant