返回上一页时离子列表不刷新
Ion-list is not refreshing when coming back to a previous page
我在尝试在离子列表中显示新值时遇到问题。
我正在做一个有两个页面的应用程序。在页面 A 中,您会看到您在页面 B 中标记的地点列表。页面 B 是一个带有搜索栏的地图,您可以在其中搜索地点,然后单击按钮来标记该地点。
当您在页面 A 中单击一个按钮时,您会转到页面 B,在标记一个位置后,您会返回到页面 A,但新标记的位置不会显示在列表中,除非您刷新应用程序并转到再次列出。
我正在使用 DataStorage 保存标记列表,不要在列表中丢失它们。据我所知,这是异步处理的,所以我认为这个错误一定与这样的事情有关。
我想知道如何重新初始化视图或组件以在列表中显示新项目。
我已经尝试使用 ionViewWillEnter 和 NgZone,但它仍然无法正常工作。这是我现在的代码:
A页:
ngOnInit() {
console.log("On init");
}
async storageControl() {
this.storage.get("markers").then( (val) => {
if(val != null){
console.log("[NewTripPage] Markers val: " + val);
this.list_locations = this.transformToTrip(JSON.parse(val));
}
return this.list_locations;
});
}
ionViewWillEnter(){
this.refresh();
}
refresh(){
this.storageControl().then( () => {
this.zone.run( (data) => {
this.list_locations = data;
console.log("Locations: " + this.list_locations);
});
})
}
transformToTrip(listToConvert): Array<MyLocation> {
// This function transforms the JSON stored in DataStorage to an array
let convertedList: Array<MyLocation> = new Array;
for(let location of listToConvert){
convertedList.push(new MyLocation(location._id, location._title, location._name, location._lat, location._lng));
}
return convertedList;
}
openMap(){
this.router.navigate(['position-marker'])
}
B页:
async ngOnInit() {
this.loadMap();
}
loadMap() {
// create map in HTML element
this.geoCoder = new google.maps.Geocoder();
const mapEle: HTMLElement = document.getElementById('map');
//lat len object
const myLatLng = {lat: 4.65838, lng: -74.093940};
// create map
this.map = new google.maps.Map(mapEle, {
center: myLatLng,
mapTypeControl: false,
streetViewControl: false,
zoom: 12
});
this.marker = new google.maps.Marker({
position: myLatLng,
map: this.map,
draggable: true,
title: 'Prueba',
visible: false
});
google.maps.event.addListenerOnce(this.map, 'idle', () => {
// when the map is ready, this will add a class to the map
mapEle.classList.add('show-map');
});
google.maps.event.addListener(this.marker, 'dragend', () => {
this.geocodePosition(this.marker.getPosition());
});
}
async setMarker(){
console.log("Setted marker: " + this.marker.getPosition());
let temp_marker = {
"id": this.placeid,
"lat": this.marker.getPosition().lat(),
"lng": this.marker.getPosition().lng(),
"title": this.marker.getTitle(),
"description": this.location.description,
}
this.marker.setVisible(false);
this.placeSearched = false;
await this.storageMarker(temp_marker).then( () => {
this.router.navigate(['new-trip']);
}).catch( (err) => {
console.log("Error setting marker: " + err);
})
}
async storageMarker(temp_marker) {
console.log("Temp_marker: " + temp_marker.position);
let aux_location = new MyLocation(
temp_marker.id,
temp_marker.title,
temp_marker.description,
temp_marker.lat,
temp_marker.lng
);
let currentMarkers = this.storage.get("markers").then((val) =>{
if(val==null){
console.log(aux_location);
this.dataArray.push(aux_location);
} else {
this.dataArray = JSON.parse(val);
this.dataArray.push(aux_location);
}
console.log(this.dataArray);
this.storage.set("markers", JSON.stringify(this.dataArray));
});
}
}
页面 A (HTML):
<div class="locations">
<ion-list>
<ion-item *ngFor="let location of list_locations; let i=index">
<ion-grid>
<ion-row>
<ion-col size="2" class="flag-icon">
<ion-icon name="flag-outline"></ion-icon>
</ion-col>
<ion-col size="10">
<ion-row>
{{ location.name}}
</ion-row>
<ion-row>
<ion-col>{{ location.lat }}</ion-col>
<ion-col>{{ location.lng }}</ion-col>
</ion-row>
</ion-col>
</ion-row>
</ion-grid>
</ion-item>
</ion-list>
<div class="div-button" *ngIf="list_locations.length!=0">
<ion-icon slot="icon-only" name="add-circle-outline" (click)="openMap()" class="add-marker"></ion-icon>
</div>
</div>
我知道代码有点糟糕,但我是 Ionic 新手。
总结:
我想刷新页面 A 上的离子列表或刷新所有视图,以便我可以看到新的设置位置。
----------------编辑 1:----------------
我已经创建了一个用于标记位置的服务,但我不知道我是否遗漏了或做错了什么,因为列表没有正确更新。
所以,当我从页面 A 打开页面 B 时,我调用了 openMap() 函数,它看起来像这样:
openMap(){
this.markerService._locationConfirmed.subscribe(
(marker) => {
console.log("[Marker service]: Finished" + marker);
this.list_locations.push(marker);
});
this.markerService.locationAnnounced();
this.router.navigate(['position-marker']);
}
我想这里有问题,因为日志 Finished + 标记没有被触发。
这是我提供的服务:
标记服务:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { MyLocation } from './entities/my-location';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
private locationAnnouncedSource = new Subject<MyLocation>();
private locationConfirmedSource = new Subject<MyLocation>();
_locationAnnounced = this.locationAnnouncedSource.asObservable();
_locationConfirmed = this.locationConfirmedSource.asObservable();
locationAnnounced() {
this.locationAnnouncedSource.next();
console.log("[Marker Service] Location will be added");
}
locationConfirmed(marker: MyLocation) {
this.locationConfirmedSource.next(marker);
console.log("[Marker Service] Location has been added: " + marker);
this.locationConfirmedSource.complete();
}
}
In the page B I have modified some functions to use the services:
**Page B:**
constructor(
// omitted code
private markerService: MarkerService,
) {
//omitted code
this.subscription = markerService._locationAnnounced.subscribe(
() => {
}
);
}
ngOnDestroy(): void {
console.log("[Position Marker] Destroyed");
this.subscription.unsubscribe();
}
setMarker(){
console.log("[Position Marker] Set Marker");
let temp_marker = new MyLocation(this.placeid,
this.marker.getPosition().lat(),
this.marker.getPosition().lng(),
this.marker.getTitle(),
this.location.description);
this.marker.setVisible(false);
this.placeSearched = false;
//this.storageMarker(temp_marker);
this.markerService.locationConfirmed(temp_marker);
console.log("[Position Marker] Set Marker --END");
}
callSetMarker(){
this.setMarker();
console.log("[Position Marker] setMarker called");
this.router.navigate(['new-trip']);
}
我不太满意的是页面 A 订阅何时被触发。正如我订阅的那样,但我根本不使用 complete() 函数。我试图遵循 Angular 文档,但我认为我只是一个懒惰的想法,我遗漏了一些东西。
理想情况下,对于此类用例,您需要 a shared service 和两个组件 update/subscribe 的主题。
这里的问题是,即使在 ionViewWillEnter 挂钩中您从存储中获取数据,到检索数据时(从磁盘异步)- Angular 已经呈现页面并且不知道列表中的数据更改。
因此,作为一种解决方法,您可以在检索到数据后触发更改检测:
import { ChangeDetectorRef } from '@angular/core';
...
constructor( private cdr: ChangeDetectorRef ) {
}
...
refresh(){
this.storage.get("markers").then(val => {
if (val) {
console.log("[NewTripPage] Markers val: " + val);
this.list_locations = this.transformToTrip(JSON.parse(val));
this.cdr.detectChanges();
};
});
}
好的,最后我通过删除所有 DataStorage 用法解决了我的问题。我没有这样做,而是制作了一个 Singleton class 来处理添加的数据并正确显示。所以这是最终代码。
单例class:
export class Singleton{
private list_markers: Array<MyLocation>;
private static instance: Singleton;
constructor() { this.list_markers = []; }
public static getSingleton() {
if(!Singleton.instance){
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
getMarkers() {
return this.list_markers;
}
setMarkers(markers: Array<MyLocation>) {
this.list_markers = markers;
}
addMarker(location: MyLocation) {
this.list_markers.push(location);
}
}
A页:
constructor(private instance: Singleton) {...}
ionViewWillEnter(){
this.dataService = Singleton.getSingleton();
this.list_locations = this.dataService.getMarkers();
}
openMap(){
// triggered by a button, it goes to page B
console.log("[New Trip] Map opened " + this.list_locations);
this.router.navigate(['position-marker']);
}
B页:
constructor(private dataService: Singleton) { ... }
ngOnInit() {
this.dataService = Singleton.getService();
this.dataArray = this.dataService.getSingleton();
...
}
setMarker(){
console.log("[Position Marker] Set Marker");
let location = new MyLocation(
this.placeid,
this.marker.getPosition().lat(),
this.marker.getPosition().lng(),
this.marker.getTitle(),
this.location.description
);
this.dataService.addMarker(location);
}
我读到在 TS Singleton 中的使用最终会消失,因为还有其他更好的选择,但这是我发现让它起作用的唯一方法。欢迎其他更好的解决方案,但我希望这能帮助其他与我有同样问题的人。
警告:
此解决方案与使用数据存储的效果不同,因为刷新应用时信息将被删除。
我在尝试在离子列表中显示新值时遇到问题。
我正在做一个有两个页面的应用程序。在页面 A 中,您会看到您在页面 B 中标记的地点列表。页面 B 是一个带有搜索栏的地图,您可以在其中搜索地点,然后单击按钮来标记该地点。
当您在页面 A 中单击一个按钮时,您会转到页面 B,在标记一个位置后,您会返回到页面 A,但新标记的位置不会显示在列表中,除非您刷新应用程序并转到再次列出。
我正在使用 DataStorage 保存标记列表,不要在列表中丢失它们。据我所知,这是异步处理的,所以我认为这个错误一定与这样的事情有关。
我想知道如何重新初始化视图或组件以在列表中显示新项目。
我已经尝试使用 ionViewWillEnter 和 NgZone,但它仍然无法正常工作。这是我现在的代码:
A页:
ngOnInit() {
console.log("On init");
}
async storageControl() {
this.storage.get("markers").then( (val) => {
if(val != null){
console.log("[NewTripPage] Markers val: " + val);
this.list_locations = this.transformToTrip(JSON.parse(val));
}
return this.list_locations;
});
}
ionViewWillEnter(){
this.refresh();
}
refresh(){
this.storageControl().then( () => {
this.zone.run( (data) => {
this.list_locations = data;
console.log("Locations: " + this.list_locations);
});
})
}
transformToTrip(listToConvert): Array<MyLocation> {
// This function transforms the JSON stored in DataStorage to an array
let convertedList: Array<MyLocation> = new Array;
for(let location of listToConvert){
convertedList.push(new MyLocation(location._id, location._title, location._name, location._lat, location._lng));
}
return convertedList;
}
openMap(){
this.router.navigate(['position-marker'])
}
B页:
async ngOnInit() {
this.loadMap();
}
loadMap() {
// create map in HTML element
this.geoCoder = new google.maps.Geocoder();
const mapEle: HTMLElement = document.getElementById('map');
//lat len object
const myLatLng = {lat: 4.65838, lng: -74.093940};
// create map
this.map = new google.maps.Map(mapEle, {
center: myLatLng,
mapTypeControl: false,
streetViewControl: false,
zoom: 12
});
this.marker = new google.maps.Marker({
position: myLatLng,
map: this.map,
draggable: true,
title: 'Prueba',
visible: false
});
google.maps.event.addListenerOnce(this.map, 'idle', () => {
// when the map is ready, this will add a class to the map
mapEle.classList.add('show-map');
});
google.maps.event.addListener(this.marker, 'dragend', () => {
this.geocodePosition(this.marker.getPosition());
});
}
async setMarker(){
console.log("Setted marker: " + this.marker.getPosition());
let temp_marker = {
"id": this.placeid,
"lat": this.marker.getPosition().lat(),
"lng": this.marker.getPosition().lng(),
"title": this.marker.getTitle(),
"description": this.location.description,
}
this.marker.setVisible(false);
this.placeSearched = false;
await this.storageMarker(temp_marker).then( () => {
this.router.navigate(['new-trip']);
}).catch( (err) => {
console.log("Error setting marker: " + err);
})
}
async storageMarker(temp_marker) {
console.log("Temp_marker: " + temp_marker.position);
let aux_location = new MyLocation(
temp_marker.id,
temp_marker.title,
temp_marker.description,
temp_marker.lat,
temp_marker.lng
);
let currentMarkers = this.storage.get("markers").then((val) =>{
if(val==null){
console.log(aux_location);
this.dataArray.push(aux_location);
} else {
this.dataArray = JSON.parse(val);
this.dataArray.push(aux_location);
}
console.log(this.dataArray);
this.storage.set("markers", JSON.stringify(this.dataArray));
});
}
}
页面 A (HTML):
<div class="locations">
<ion-list>
<ion-item *ngFor="let location of list_locations; let i=index">
<ion-grid>
<ion-row>
<ion-col size="2" class="flag-icon">
<ion-icon name="flag-outline"></ion-icon>
</ion-col>
<ion-col size="10">
<ion-row>
{{ location.name}}
</ion-row>
<ion-row>
<ion-col>{{ location.lat }}</ion-col>
<ion-col>{{ location.lng }}</ion-col>
</ion-row>
</ion-col>
</ion-row>
</ion-grid>
</ion-item>
</ion-list>
<div class="div-button" *ngIf="list_locations.length!=0">
<ion-icon slot="icon-only" name="add-circle-outline" (click)="openMap()" class="add-marker"></ion-icon>
</div>
</div>
我知道代码有点糟糕,但我是 Ionic 新手。
总结: 我想刷新页面 A 上的离子列表或刷新所有视图,以便我可以看到新的设置位置。
----------------编辑 1:----------------
我已经创建了一个用于标记位置的服务,但我不知道我是否遗漏了或做错了什么,因为列表没有正确更新。
所以,当我从页面 A 打开页面 B 时,我调用了 openMap() 函数,它看起来像这样:
openMap(){
this.markerService._locationConfirmed.subscribe(
(marker) => {
console.log("[Marker service]: Finished" + marker);
this.list_locations.push(marker);
});
this.markerService.locationAnnounced();
this.router.navigate(['position-marker']);
}
我想这里有问题,因为日志 Finished + 标记没有被触发。
这是我提供的服务:
标记服务:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { MyLocation } from './entities/my-location';
@Injectable({
providedIn: 'root'
})
export class MarkerService {
private locationAnnouncedSource = new Subject<MyLocation>();
private locationConfirmedSource = new Subject<MyLocation>();
_locationAnnounced = this.locationAnnouncedSource.asObservable();
_locationConfirmed = this.locationConfirmedSource.asObservable();
locationAnnounced() {
this.locationAnnouncedSource.next();
console.log("[Marker Service] Location will be added");
}
locationConfirmed(marker: MyLocation) {
this.locationConfirmedSource.next(marker);
console.log("[Marker Service] Location has been added: " + marker);
this.locationConfirmedSource.complete();
}
}
In the page B I have modified some functions to use the services:
**Page B:**
constructor(
// omitted code
private markerService: MarkerService,
) {
//omitted code
this.subscription = markerService._locationAnnounced.subscribe(
() => {
}
);
}
ngOnDestroy(): void {
console.log("[Position Marker] Destroyed");
this.subscription.unsubscribe();
}
setMarker(){
console.log("[Position Marker] Set Marker");
let temp_marker = new MyLocation(this.placeid,
this.marker.getPosition().lat(),
this.marker.getPosition().lng(),
this.marker.getTitle(),
this.location.description);
this.marker.setVisible(false);
this.placeSearched = false;
//this.storageMarker(temp_marker);
this.markerService.locationConfirmed(temp_marker);
console.log("[Position Marker] Set Marker --END");
}
callSetMarker(){
this.setMarker();
console.log("[Position Marker] setMarker called");
this.router.navigate(['new-trip']);
}
我不太满意的是页面 A 订阅何时被触发。正如我订阅的那样,但我根本不使用 complete() 函数。我试图遵循 Angular 文档,但我认为我只是一个懒惰的想法,我遗漏了一些东西。
理想情况下,对于此类用例,您需要 a shared service 和两个组件 update/subscribe 的主题。
这里的问题是,即使在 ionViewWillEnter 挂钩中您从存储中获取数据,到检索数据时(从磁盘异步)- Angular 已经呈现页面并且不知道列表中的数据更改。
因此,作为一种解决方法,您可以在检索到数据后触发更改检测:
import { ChangeDetectorRef } from '@angular/core';
...
constructor( private cdr: ChangeDetectorRef ) {
}
...
refresh(){
this.storage.get("markers").then(val => {
if (val) {
console.log("[NewTripPage] Markers val: " + val);
this.list_locations = this.transformToTrip(JSON.parse(val));
this.cdr.detectChanges();
};
});
}
好的,最后我通过删除所有 DataStorage 用法解决了我的问题。我没有这样做,而是制作了一个 Singleton class 来处理添加的数据并正确显示。所以这是最终代码。
单例class:
export class Singleton{
private list_markers: Array<MyLocation>;
private static instance: Singleton;
constructor() { this.list_markers = []; }
public static getSingleton() {
if(!Singleton.instance){
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
getMarkers() {
return this.list_markers;
}
setMarkers(markers: Array<MyLocation>) {
this.list_markers = markers;
}
addMarker(location: MyLocation) {
this.list_markers.push(location);
}
}
A页:
constructor(private instance: Singleton) {...}
ionViewWillEnter(){
this.dataService = Singleton.getSingleton();
this.list_locations = this.dataService.getMarkers();
}
openMap(){
// triggered by a button, it goes to page B
console.log("[New Trip] Map opened " + this.list_locations);
this.router.navigate(['position-marker']);
}
B页:
constructor(private dataService: Singleton) { ... }
ngOnInit() {
this.dataService = Singleton.getService();
this.dataArray = this.dataService.getSingleton();
...
}
setMarker(){
console.log("[Position Marker] Set Marker");
let location = new MyLocation(
this.placeid,
this.marker.getPosition().lat(),
this.marker.getPosition().lng(),
this.marker.getTitle(),
this.location.description
);
this.dataService.addMarker(location);
}
我读到在 TS Singleton 中的使用最终会消失,因为还有其他更好的选择,但这是我发现让它起作用的唯一方法。欢迎其他更好的解决方案,但我希望这能帮助其他与我有同样问题的人。
警告:
此解决方案与使用数据存储的效果不同,因为刷新应用时信息将被删除。