iOS页面回退时自动跳转无ID详情页的技术问题求助
Hey there, let's dig into this frustrating iOS-specific issue you're hitting. When navigating back to your list page and then into a detail page, sometimes the trip ID goes missing, leading to errors because you can't fetch the journey info. Let's break down why this happens and how to fix it.
Why This Happens on iOS
iOS Safari (and the Ionic WebView built on it) uses something called Back-Forward Cache (BF Cache). When you navigate back from the detail page, the list page is stored in this cache. When you re-enter a detail page, in some cases the cached state of the detail page might be restored instead of creating a new instance—meaning ngOnInit might not fire again, and the trip ID could be stale or missing.
Solutions to Try
1. Use ionViewWillEnter Instead of ngOnInit for Detail Page Loading
Ionic's ionViewWillEnter lifecycle hook fires every time the page is entered, even if it's restored from the BF Cache. This is more reliable than ngOnInit for mobile navigation scenarios.
Update your detail page code to move the loading logic into ionViewWillEnter, and always fetch the latest trip ID from the route:
import { ActivatedRoute, NavController } from '@angular/router'; // ... constructor( private route: ActivatedRoute, private navCtrl: NavController, // your other dependencies ) {} public ionViewWillEnter() { // Get the latest trip ID from the route first this.tripID = this.route.snapshot.paramMap.get('tripID'); // Handle the case where tripID is missing immediately if (!this.tripID) { this.showAlertConfirm("Vale", "No se ha encontrado el viaje", true); this.navCtrl.navigateBack('/your-list-page-path'); // Replace with your actual list page route return; } // Proceed with your existing loading logic this.showLoading().then(() => { this.getToken().then(() => { this.getJourneyInfo(this.userToken).then(() => { this.initMap().then(() => { this.loading.dismiss(); }).catch((error) => { this.loading.dismiss().then(() => { this.showAlertConfirm("Vale", error, false); }); }); }); }); }); }
2. Subscribe to Route Param Changes
If you prefer to stay with ngOnInit, subscribe to the route's paramMap instead of using the snapshot. This ensures you catch any updates to the route parameters, even if the page is cached:
ngOnInit() { this.route.paramMap.subscribe(params => { this.tripID = params.get('tripID'); if (!this.tripID) { this.navCtrl.navigateBack('/your-list-page-path'); return; } // Your existing loading code here this.showLoading().then(() => { this.getToken().then(() => { this.getJourneyInfo(this.userToken).then(() => { this.initMap().then(() => { this.loading.dismiss(); }).catch((error) => { this.loading.dismiss().then(() => { this.showAlertConfirm("Vale", error, false); }); }); }); }); }); }); }
3. Add Guardrails in getJourneyInfo
Even if the above fixes work, adding a safety check in your data-fetching method will prevent unnecessary API calls and errors:
private getJourneyInfo(tkn: string): Promise<any> { // Guard clause for missing tripID if (!this.tripID) { return Promise.reject('No trip ID available'); } return new Promise<any>((resolve) => { this.journey.getJourneyInfo(this.tripID, tkn).subscribe((data: any) => { this.initializeValues(data); resolve(); }, () => { this.showAlertConfirm("Vale", "No ha sido posible obtener la información", true); }); }); }
4. Disable BF Cache for the Detail Page (Optional)
If the above fixes don't work, you can disable BF Cache for the detail page by adding this to your component's ionViewWillEnter or ngOnInit:
// Prevent page from being cached in iOS BF Cache window.addEventListener('pageshow', (event) => { if (event.persisted) { window.location.reload(); } });
Note: This is a more drastic approach since it reloads the page, but it guarantees a fresh state.
Your Original Code for Reference
List Page Loading Code
public ionViewDidEnter() { if (this.role === "passenger") { this.getCurrentPosition(); } } private getCurrentPosition() { const loading = this.loadingCtrl.create({ content: "Obteniendo ubicación actual...", spinner: "dots", }); loading.present(); const positionOptions = { enableHighAccuracy: true, maximumAge: 600000, timeout: 8000, }; this.geolocation.getCurrentPosition(positionOptions).then((position) => { this.originCoords = new google.maps.LatLng( position.coords.latitude, position.coords.longitude ); this.getNearbyJourneys(); loading.dismiss(); }).catch(() => { loading.dismiss().then(() => { this.showErrorAlert("Error al obtener la ubicación"); }); }); } private getNearbyJourneys() { const body = { lat: this.originCoords.lat(), lng: this.originCoords.lng(), token: this.userToken, }; this.journey.listJourneysByOrigin(body).subscribe((d: any) => { this.dataTrips = []; this.items = d[0]; for (let i = 0; i < this.journeysToLoad; i++) { if (d[0][i] != null) { this.dataTrips.push(d[0][i]); } } }); }
Detail Page Code
public ngOnInit() { this.showLoading().then(() => { this.getToken().then(() => { this.getJourneyInfo(this.userToken).then(() => { this.initMap().then(() => { this.loading.dismiss(); }).catch((error) => { this.loading.dismiss().then(() => { this.showAlertConfirm( "Vale", error, false, ); }); }); }); }); }); } private getToken(): Promise<any> { return new Promise<any>((resolve) => { this.tokenProvider.getToken().then((tkn: any) => { this.userToken = tkn; resolve(); }, (error: any) => { this.errorHandler(error); }); }); } private getJourneyInfo(tkn: string): Promise<any> { return new Promise<any>((resolve) => { this.journey.getJourneyInfo(this.tripID, tkn).subscribe((data: any) => { this.initializeValues(data); resolve(); }, () => { this.showAlertConfirm( "Vale", "No ha sido posible obtener la información", true); }); }); } private initMap(): Promise<any> { return new Promise<any>((resolve, reject) => { this.plt.ready().then(() => { const mapOptions = this.gmapsProvider.initMapOptions() this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions); this.gmapsProvider.codeAddress( this.journeyInfo.origin.direction, this.originIcon, this.map, ); this.gmapsProvider.codeAddress( this.journeyInfo.destination.direction, this.destinationIcon, this.map, ); this.gmapsProvider.calculateRouteAndDisplay( this.map, this.journeyInfo.origin.direction, this.journeyInfo.destination.direction, ).then(() => { resolve(); }).catch((error) => { reject(error); }); }); }); }
内容的提问来源于stack exchange,提问作者Borja Alonso

