Get a value from the localstorage and store it in a variable

Hello everyone,
I would like to retrieve a value that I stored in my localstorage and put it in a variable so that I could use this variable later.

Here is my clean code, I get the position of the user.

https://jsfiddle.net/6w10cxgz/

Edit :

console.log(this.storage.get(this.latUserKey));
return :

1. ZoneAwarePromise {__zone_symbol__state: null, __zone_symbol__value: Array(0)}
1. __zone_symbol__state: true
2. __zone_symbol__value: 49.431347200000005
3. Symbol(Symbol.toStringTag): (...)
4. __proto__: Object

How to recover __zone_symbol_value ?

Hi, I believe that should be something like this:

latVal: any;
    
getPositionUser() {
    this.geolocation.getCurrentPosition({
        enableHighAccuracy: true,
        maximumAge: 0
    }).then((result: Geoposition) => {
        this.storage.set(this.latUserKey, result.coords.latitude);
        this.storage.set(this.longUserKey, result.coords.longitude);
    });

    return this.storage.get(this.latUserKey).then((val) => {
    	console.log(val);
        return val; // <--- good result        
    });
}

ngOnInit(): void {
    this.getPositionUser().then(val => {
        this.latVal = val;
    });
}

In your case you are logging the whole promise, you should resolve it first and .then use the resolved value.

I tried doing a console.log (latVal) but the result is undefined. :confused:

Ok, let’s try something different then. I don’t know if this will work though:

latVal: any;
    
getPositionUser() {
    return this.geolocation.getCurrentPosition({
        enableHighAccuracy: true,
        maximumAge: 0
    }).then((result: Geoposition) => {
        this.storage.set(this.latUserKey, result.coords.latitude);
        this.storage.set(this.longUserKey, result.coords.longitude);
    }).then(() => {
        this.storage.get(this.latUserKey).then((val) => {
            console.log(val);
            return val; // <--- good result        
        });
    });    
}

ngOnInit(): void {
    this.getPositionUser().then(val => {
        console.log(val);
        this.latVal = val;        
    });
}

I’m cascading the promises, so storage.get() will only run after storage.set() and this will only run after geolocation.getCurrentPosition().

this code works pretty well but latVal remains undefined.
Is it possible at least to retrieve the value in a variable?

This is strange, in ngOnInit() this:this.latVal = val; should rewrite the value of latVal and you should be able to use it anywhere in the page.

How and where in your code you are retrieving the value of this variable now?

If possible create a stackblitz so we can better identify the errors. If you use Ionic 4 there is a blank template:

I don’t know how to use StackBlitz but here is my code :

file.ts

import {Component, OnInit} from '@angular/core';
import {Geolocation, Geoposition} from '@ionic-native/geolocation/ngx';
import {Map, marker, polyline, tileLayer} from 'leaflet';
import {Storage} from '@ionic/storage';

@Component({
    selector: 'app-home',
    templateUrl: 'home.page.html',
    styleUrls: ['home.page.css'],
})
export class HomePage implements OnInit {
    map: Map;
    latUserKey = 'Latitude';
    longUserKey = 'Longitude';
    private longVal: any;
    private latVal: any;


    constructor(private geolocation: Geolocation, private storage: Storage) {

    }

    getPositionUser() {
        this.geolocation.getCurrentPosition({
            enableHighAccuracy: true,
            maximumAge: 0
        }).then((result: Geoposition) => {
            this.storage.set(this.latUserKey, result.coords.latitude);
            this.storage.set(this.longUserKey, result.coords.longitude);
        }).then(() => {

            this.storage.get(this.latUserKey).then((val) => {
                this.latVal = val;
            });
            this.storage.get(this.longUserKey).then((val) => {
                this.longVal = val;
            });
        });
    }

    showMap() {
        this.map = new Map('myMap').setView([this.latVal, this.longVal], 18);
        tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
            .addTo(this.map);
    }


    ngOnInit(): void {
        this.getPositionUser();
        this.showMap();
    }
}

what I want is:

  1. know the user’s position
  2. store the value of the position in the localstorage
  3. get the localstorage values ​​and store them in a variable
  4. display the user’s position on the map

Okay, I just realize that we are doing an unnecessary thing: we are storing some data in device storage, then we are getting this same data just to populate in our variable. We can simplify this by populating the local variables the first time you get the geolocation (result: Geoposition). And again we need to resolve the getPositionUser() Promise before we run showMap():

getPositionUser() {
    this.geolocation.getCurrentPosition({
        enableHighAccuracy: true,
        maximumAge: 0
    }).then((result: Geoposition) => {
        this.storage.set(this.latUserKey, result.coords.latitude);
        this.latVal = result.coords.latitude;
        this.storage.set(this.longUserKey, result.coords.longitude);
        this.longVal = result.coords.longitude;
    });
}

showMap() {
    this.map = new Map('myMap').setView([this.latVal, this.longVal], 18);
    tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
        .addTo(this.map);
}


ngOnInit(): void {
    this.getPositionUser().then(() => {
        this.showMap();
    });        
}

Every time you type the word any in your code, you should feel anguish and guilt. I believe it’s really a crucial part of the issue here, because there are fundamentally three ways to deal with data that is going to arrive at some point in the future, which we have here:

A. Initialize it with a default and overwrite that when the true value comes in.
B. Initialize it with a sentinel and test for that sentinel value whenever used.
C. Never refer to it directly; always refer to it via a future.

Each of these strategies is viable, and, importantly, each lends itself to a different type signature. The ultimate type we’re going to get from the geolocation plugin is Geoposition, so our three strategies’ declarations look like this:

A. coords: Geoposition = { ...bunch of whatever defaults you like... };
B. coords: Geoposition | null = null;
C. coords$: Promise<Geoposition>; (initialize me in constructor once we have geolocation)

This makes the code somewhat self-documenting, and helps protect you from bugs.

Specifically, it’ll make showMap harder to write (and that’s a good thing).

In case A, showMap actually can just forget about all of the timing issues, but they get kicked upstairs to the caller, which has to know to call showMap again whenever the underlying coords changes. That’s ordinarily something you want Angular change detection to do, so A may not be the best choice here.

In case B, showMap has to check for null, and we still have the same calling timing problems we had in case A.

Which leaves case C, which I would prefer here, and is more or less what @leonardofmed is also suggesting. showMap then becomes responsible for waiting on coords$:

showMap(): void {
  this.coords$.then(coords => {
    // only inside here coords are reliable
  }
}

This also scales well to using an Observable instead of a Promise when we want to start having multiple changes to coords over the life of the app.

My main point here is that aggressively typing everything in your app makes you think early about important design choices, and reinforces what choices you do make, so that you code consistently and defensively.

1 Like

I am very novice in the field, I do not know if what I did is very viable and compliant but here is my functional code:

    constructor(private geolocation: Geolocation, private storage: Storage) {
        this.geolocation.getCurrentPosition({
            enableHighAccuracy: true,
            maximumAge: 0
        }).then((result: Geoposition) => {
            this.storage.set(this.latUserKey, result.coords.latitude);
            this.storage.set(this.longUserKey, result.coords.longitude);


            this.storage.get('Etablissements').then((val) => {
                let map = L.map('myMap').setView([result.coords.latitude, result.coords.longitude], 18);
                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png')
                    .addTo(map);
            });

        });
    }

I could not recover my data in a variable so I put everything in my constructor

Edit : Works on my browser but the card is no longer displayed on my phone

Why this logic is not the same as pure javascript

https://jsfiddle.net/bm3esgtd/1/

That’s what I understood about the promises.
A promise is asynchronous, that is, it will run once the script finishes loading.

That’s why I can not get my variable in another function, because my function is executed before the variable is changed.

So, I’m trying to do this:

1 - Declare my variable
2 - Run my promise
3 - Run my function

Is it possible to make a promise synchronous?
Because I do not have the choice to do so, the getCurrentPosition () method returns a promise.

See the doc:

This is an imperative programming mindset, which doesn’t fit the reactive paradigm used in Ionic apps. You’re fighting the framework, which is a recipe for frustration.

You can make them look more synchronous, but it’s a fool’s errand. Instead, I think you will be much happier in the long run if you embrace reactive thinking.

It demonstrates the nested Promise antipattern.

Constructors should only be about putting the object into a sane initial state. Specifically, you should not be trying to interact with the DOM at all until you are certain the view hierarchy has been built. See the Angular lifecycle docs, especially ngAfterViewInit.