Search code examples
angularionic4

Ion-list is not refreshing when coming back to a previous page


I'm having a problem when trying to show a new value in a ion-list.

I'm doing an app in which I have two pages. In Page A, you see a list of the places you have marked in page B. Page B is a map with a searchbar where you search a place and then click a button to mark that place.

When you click a button in page A you go to page B and after marking a place you go back to page A but the new marked place isn't showing in the list unless you refresh the app and go to the list again.

I'm using DataStorage to save the list of the markers and don't lose them in the list. As far as I know, this is treated asynchronously, so I think the mistake got to be related to something like this.

I'd like to know how to re-initialize the view or the component to show the new item in the list.

I've tried to use ionViewWillEnter and NgZone but it's still not working as it is supposed. This is my code right now:

Page 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'])
  }

Page 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));
    });
    
  }
}

Page 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>

I know the code is kinda bad but I'm new to Ionic.

Summary: I want to refresh ion-list on page A or to refresh all the view so i can see the new setted place.

----------------EDIT 1:----------------

I have created a Service for marking a position but I don't know if I'm missing or doing something wrong as the list isn't updating correctly.

So, when i open the page B from page A I call openMap() function, which looks like this:

  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']);
  }

I suppose that here is something wrong as the log Finished + marker is not triggered.

Here is the service I've made:

Marker Service:

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']);
    
  }

What I don't get well, is when the page A subscription is triggered. As i subscribe but i don't use complete() function at all. I've tried to follow the Angular documents but I think I only got a lazy idea and I'm missing something.


Solution

  • Okay so finally I solved my issue by deleting all the DataStorage usages. Instead of doing this way, I made a Singleton class to deal with the data added and it's displayed correctly. So here is the final code.

    Singleton 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);
      }
    }
    

    Page 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']);
    }
    

    Page 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);
      }
    

    I've read that in TS Singleton's use is eventually dissapearing as there are other better choices, but this is the only way i found to make it work. Other better solutions are welcomed, but I hope this helps someone else who is having the same trouble as me.

    Warning:

    This solution has not the same effect than using data storage as the information will erase when te app is refreshed.