问题
So, I have an Angular 9 app hosted on Firebase and using Firestore for the data. I have an issue that seems very simple but I can't wrap my head around why it's happening. I've simplified the app a lot to find the root cause of this and will try to explain the issue as well as I can below.
The app: I have 2 pages, one Homepage, and one Transactions page. Both pages are reading from the same Firebase collection "transactions". However, on the Home page, I want to show the 4 most recent transactions (sorted by date, descending), whereas on the Transactions page I want to show the 10 most profitable transactions (sorted by amount, descending). For the time being, I'm just logging the data to the console for debugging. Before logging the data, I'm also manipulating it slightly (see code below).
The problem: when I start on the Homepage, I can see my 4 most recent transactions as expected in the console. However, when I go to the Transactions page, for a brief second it logs the 4 most recent transactions in the console again, which are supposed to be only shown on the Homepage. After a second or so, it shows the expected 10 most profitable transactions.
The code: Here is my code for the home.page.ts:
txSubscription: Subscription;
constructor(
public afAuth: AngularFireAuth,
private readonly firestore: AngularFirestore
) { }
// Function to get the 4 most recent transactions
async getRecentTransactions() {
this.txSubscription = this.firestore
.collection('transactions', ref => ref.orderBy('date', 'desc').limit(4))
.valueChanges()
.subscribe(rows => {
this.recentTransactions = [];
rows.forEach(row => {
let jsonData = {};
jsonData['ticker'] = (row['ticker'].length <= 10 ? row['ticker'] : row['ticker'].substring(0, 10) + '...');
jsonData['date'] = formatDate(row['date'].toDate(), 'dd/MM/y', 'en');
jsonData['amount'] = prefix + formatNumber(row['netAmount'], 'be', '1.2-2');
this.recentTransactions.push(jsonData);
})
console.log("home page", this.recentTransactions);
})
}
ngOnInit() {
this.afAuth.onAuthStateChanged(() => {
this.getRecentTransactions();
})
}
ngOnDestroy() {
this.txSubscription.unsubscribe();
}
And the code for transaction.page.ts is very similar:
txSubscription: Subscription;
constructor(
public afAuth: AngularFireAuth,
private readonly firestore: AngularFirestore
) { }
// Function to load the data for the home page
loadHomeData() {
this.txSubscription = this.firestore
.collection('transactions', ref => ref.orderBy('profitEur', 'desc').limit(10))
.valueChanges()
.subscribe(rows => {
this.resultRows = [];
rows.forEach(row => {
this.resultRows.push(row['ticker'].slice(0, 8));
});
console.log("transaction page", this.resultRows);
})
}
ngOnInit() {
this.afAuth.onAuthStateChanged(() => {
this.loadHomeData();
})
}
ngOnDestroy() {
this.txSubscription.unsubscribe();
}
The result: here's what is outputted to the console at every step
- I open the app on the Home page (4 rows as expected):
home page (4) [{…}, {…}, {…}, {…}]
0: {ticker: "BAR", date: "21/07/2020", amount: "- € 1 086,10"}
1: {ticker: "ASL C340.0...", date: "18/07/2020", amount: "€ 0,00"}
2: {ticker: "ASL C340.0...", date: "14/07/2020", amount: "- € 750,85"}
3: {ticker: "TUI C7.00 ...", date: "20/06/2020", amount: "€ 0,00"}
length: 4
__proto__: Array(0)
- I navigate to the Transactions page:
transaction page (4) ["TUI C7.0", "ASL C340", "BAR", "ASL C340"]
transaction page (10) ["ASL C240", "ASL C232", "REC", "ASL C270", "ASL C310", "ASML", "ASL P220", "BAR", "CFEB", "MELE"]
Why, when navigating to the Homepage, is it showing again the same 4 rows from the Homepage in the console?
回答1:
You get results 2 times on transactions page. It is very likely valueChanges returns data from firestore in-memory cache as a quick response and then it reads from real data. In your case 4 rows are cached when they are returned on home page and they are processed from cache on transactions page.
回答2:
I think to problem lays here:
loadHomeData() {
this.txSubscription = this.firestore
...
.subscribe(rows => {
...
})
}
ngOnInit() {
this.afAuth.onAuthStateChanged(() => {
this.loadHomeData();
})
}
ngOnDestroy() {
this.txSubscription.unsubscribe();
}
You are correctly unsubscribing. However, look at what happens when onAuthStateChanged fires again. Your first subscription is lost and there is no way to unsubscribe. I think using the switchmap operator on onAuthStateChanged will fix your problem. Something like this:
this.subscription = this.af.authState.pipe(
switchMap(auth => {
if(auth !== null && auth !== undefined){
return this.firestore.collection('transactions', ref => ref.orderBy('profitEur', 'desc').limit(10)).valueChanges())
} else{
throw "Not loggedin";
}
).subscribe(...)
回答3:
This issue is happening maybe because of
this.afAuth.onAuthStateChanged()
getting triggered twice.
Instead of checking for the auth state on every component.
you can simply subscribe to the auth state at app.component.ts.if a user is unauthenticated or auth state changes it will redirect to login page otherwise it will redirect to home.page.ts
export class AppComponent {
constructor(private readonly auth: AngularFireAuth, private router: Router) {
this.auth.authState.subscribe(response => {
console.log(response);
if (response && response.uid) {
this.router.navigate(['dashboard', 'home']); //Home page route
} else {
this.router.navigate(['auth', 'login']); //Login page route
}
}, error => {
this.auth.signOut();
this.router.navigate(['auth', 'login']); //Login Page route
});
}
}
In your home.component.ts & transaction.page.ts no need to checking the auth state.
- home.component.ts
txSubscription: Subscription;
constructor(
public afAuth: AngularFireAuth,
private readonly firestore: AngularFirestore
) { }
// Function to get the 4 most recent transactions
async getRecentTransactions() {
this.txSubscription = this.firestore
.collection('transactions', ref => ref.orderBy('date', 'desc').limit(4))
.valueChanges()
.subscribe(rows => {
this.recentTransactions = [];
rows.forEach(row => {
let jsonData = {};
jsonData['ticker'] = (row['ticker'].length <= 10 ? row['ticker'] : row['ticker'].substring(0, 10) + '...');
jsonData['date'] = formatDate(row['date'].toDate(), 'dd/MM/y', 'en');
jsonData['amount'] = prefix + formatNumber(row['netAmount'], 'be', '1.2-2');
this.recentTransactions.push(jsonData);
})
console.log("home page", this.recentTransactions);
})
}
ngOnInit() {
this.getRecentTransactions();
}
ngOnDestroy() {
this.txSubscription.unsubscribe();
}
- transaction.page.ts
txSubscription: Subscription;
constructor(
public afAuth: AngularFireAuth,
private readonly firestore: AngularFirestore
) { }
// Function to load the data for the home page
loadHomeData() {
this.txSubscription = this.firestore
.collection('transactions', ref => ref.orderBy('profitEur', 'desc').limit(10))
.valueChanges()
.subscribe(rows => {
this.resultRows = [];
rows.forEach(row => {
this.resultRows.push(row['ticker'].slice(0, 8));
});
console.log("transaction page", this.resultRows);
})
}
ngOnInit() {
this.loadHomeData();
}
ngOnDestroy() {
this.txSubscription.unsubscribe();
}
来源:https://stackoverflow.com/questions/63363151/angular-app-with-firebase-data-why-am-i-seeing-data-from-the-previous-page