返回上一页时离子列表不刷新

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 中的使用最终会消失,因为还有其他更好的选择,但这是我发现让它起作用的唯一方法。欢迎其他更好的解决方案,但我希望这能帮助其他与我有同样问题的人。

警告:

此解决方案与使用数据存储的效果不同,因为刷新应用时信息将被删除。