Let's now use the local storage API
in order to make the workout data
persist across multiple page reloads.
And let's start with a look at our flowchart
so that we can see exactly what we have to implement.
So basically the idea is that
whenever a new workout is added,
then all the workouts will be added to local storage.
And so local storage is basically a place in the browser,
where we can store data that will stay there
even after we close the page.
So basically the data is basically linked to the URL
on which we are using the application.
So, again whenever there is a new workout,
we will take the entire workouts array
and store it in local storage.
And then whenever the page loads,
so up here in this event,
then we will load all the workouts from the local storage,
and render them on the map and also on the list.
So just like we do it when a user submits a new workout.
And so like this, when you reload the page,
it will then appear
as if all the workouts you had previously
are still in the same place.
And hopefully this makes sense,
and so let's now get started,
and we will start with this part here.
So storing all the workouts in local storage,
and remember that happens
whenever the user creates a new workout.
And so we have to get started in the new workout method.
So down here, we need to add yet another point.
So set local storage to all workouts.
And here I'm actually gonna create yet another method,
so that we can keep this method here,
basically nice and clean.
So as you see, this new workout method here
is basically a bit of a method that does delegation.
So we do some stuff here, of course,
but then once we're done creating the actual object,
then down here, we delegate all kinds of functionality
to other methods like these ones.
And now I'll also create setLocalStorage.
So here I'm of course calling it already,
but now I will create it afterwards.
So setLocalStorage and once again,
let's add it here at the end.
And this one doesn't need any parameters
because we will simply get the workouts
from the workout property.
And so this is how we use the local storage API.
And I say API because local storage is, once again,
an API that the browser provides for us
and that we can use.
So localStorage.setItem,
and then we have to give it a name, so workouts,
and then the second argument
needs to be a string that we want to store
and which will be associated with this key here.
So basically local storage is a simple key value store,
and so we need a key which is this one here,
and we need a simple value,
which must also be a string.
But we can convert an object to a string
by doing this.
So JSON.stringify.
So this is another new one, all right.
But we can use this method here
to convert any object in JavaScript to a string.
And so what we want to convert
is this.workouts, like this.
And that's actually it already.
With this, we are setting all the workouts to local storage.
Now, I just want to mention that local storage
is a very simple API.
And so it is only advised to use
for small amounts of data, all right.
That's because local storage is blocking,
and for now you don't know what blocking actually means
but it is something that's very bad,
and we will learn why that is
actually in the next section.
But for now, what matters here is
that you shouldn't use local storage
to store large amounts of data,
because that will surely slow down your application.
But anyway, let's already test this out,
because we actually have a way
of seeing this data in the browser.
So let's add something here.
Okay.
So everything worked fine.
So now let's go here to this application tab,
and then here on the left side
you have this local storage tab, all right.
And if we click this arrow here, and then here,
well, then actually it should be here already.
Well, let's see if we have everything correctly set up.
So we have setLocalStorage, then setItem
and here everything looks correct.
And here, I believe we're also correctly calling it,
setLocalStorage.
So this should work.
So let's simply try it again here.
(clicking)
(typing) Okay.
Ah, and now you saw it appearing here, right.
So we have the key of workout and then the value.
And the value is basically now this string here.
So, actually here it looks like an object,
because apparently JavaScript
somehow then converts it back to an object,
but actually it is stored as a string,
here in this workouts key.
So you see that we have this single workout
that we currently have,
but of course, if we add another one,
then now we got this bigger string here.
And so it now contains these two objects.
And so like this, the local storage
always is gonna be updated.
So it's up to date, according to the data
that we have in our application.
Great.
And now all we have to do
is to then actually show the data back here
on the list and the map once we reload the page.
So let me actually show that to you
because here in the application,
as you see the local storage is still there.
And so now we will make use of this
by then displaying that data back in our application.
So remember when we want to do that,
it is right here in the constructor, right.
Because this is where we have all the code
that is executed right when the application loads.
So let's add that here.
And I'm gonna start with some comments here.
(typing)
Handlers,
then get user's position,
and then get data from local storage.
And so again,
I will now call a method that I'm going to create later.
So getLocalStorage.
And so let's now go ahead and create this method
down here in the end.
(clicking) (typing)
And so now, here we do the opposite.
So localStorage.getItem.
And now we simply have to pass in the key.
So basically the identifier of our local storage item,
because we could set multiple items.
So we could set everything all for application.
So for example, if we wanted,
we could store everything that's in the application
in local storage.
We would just have to define one key for each of them,
and then we could use that key
to basically retrieve it back.
(clicking) (typing)
Alright, so let's now store this data.
And so here, we can then see it for the first time.
So now as the page loads,
it should display this data to us
in the console.
And, well, it's nowhere to be found.
Let's reload here maybe again.
So something must be wrong, now,
and it's called workouts.
And here you go, all right.
So you see that indeed, it is this big string.
And so now we want to convert this string
back to the objects.
And so, basically we need to use the opposite
of JSON.stringify which is JSON.parse.
So let's try that again.
And now indeed we get an array
with some real objects in there, all right.
Now there's still a problem with this,
but more about that a little bit later,
because all the relevant data
that we need for now is actually in there.
So now let's do something with that data.
And the first thing that we should actually do
is to check if there is actually some data.
So when there is nothing in local storage,
then of course the data will be, like undefined.
So in that case, we don't want to do anything.
So let's use another guard clause here and say,
if there is no data then simply return.
But anyway, let's keep going.
And so what we want to do with this data,
which is basically our array of workouts,
is to restore our workouts array.
So we can say, this.workouts
should be equal to the data that we just read.
So, keep in mind that this method here
is gonna be executed right in the very beginning.
And so at that point,
the workouts array is always gonna be empty.
But if we already had some data in the local storage,
then, we will simply set that workouts array
to the data that we had before.
And so essentially we are restoring the data here
across multiple reloads of the page.
Okay, and so now let's take all these workouts
and render them in the list.
So this.workouts,
and so we want to do something
for each of the workouts.
And so we loop over this array,
but we don't want to create a new array.
And so forEach is perfect for this.
Then now, I'm gonna call each of them work.
And so, now I can do this.renderWorkout
the current workout.
And so now it becomes really, really helpful
that we have all the logic
of rendering a workout in the sidebar
here nicely inside of this method, right.
Because, imagine that we had all this code here,
inside of this method here,
so the new workout method, right.
So if we did all of that here,
then now we would have to copy that code
or we would have to refactor it.
But instead, you can start to get into the habit
of exporting functionality into its own methods
or its own functions.
And so then it is very easy to reuse them,
such as we are doing now with renderWorkout.
Right.
So here, all we do is to call renderWorkout
that we already had written before.
So let's now test this,
and right now,
we should already get the two workouts there,
and indeed, here they are.
So these are the two ones that we had created previously.
And so we see that it did indeed now persist across loads.
Now, we also need to add them here on the map.
And so for that,
we are going to use our renderWorkout marker method
that we also already have.
And let's start by actually putting it here,
just so I can show you
that it's actually not gonna work.
But then it becomes easier to explain
why it doesn't work.
So let's see.
And so here we get,
"Cannot read property 'addlayer' of undefined."
So indeed, we get an error
when we try to add this first workout
also to the map.
So let's go back and think why that is.
Well, once again, remember that this method here
is executed right at the beginning,
so right after the page loaded.
And so we are trying to add this marker to the map
right at the beginning.
However, at this point,
the map has actually not yet been loaded.
And so essentially,
we are trying to add a marker,
let's say where that is.
So we are trying to add this marker to the map,
which is this one here,
which isn't yet defined at this point.
And so once again, this is a first glimpse
into the nature of asynchronous JavaScript,
which we will talk about in the next section.
But what matters here is that you grasp this idea
that the map is not yet created
right at the beginning
when the application is first loaded, right.
It takes some time.
So, first the position of the user
needs to beget using geolocation,
so this one here.
And then after that,
the map has also to be loaded.
So there's a lot of stuff that has to happen
before we can actually render any markers on the map.
So instead of trying to render the markers,
right at the very beginning,
we should only do that
once the map has been loaded.
And so we can put that logic
right here in this method of load map, okay.
So here at the very end, basically.
So let's get the code from down here.
So I will get all of this,
but I will then only keep this part, all right.
So let's copy this.
And so here we will not render the markers,
so only the workout on the sidebar list.
And then it is here.
So, yeah.
Right here in load map,
this is where we will then render the markers,
because at this point,
the map is, of course, already available.
And so with this, it should be working, okay.
But make sure that you really understand
why we have to call this method here
and not right in the beginning.
So, let's see what happens now,
and we didn't get any error and yeah.
So now we have the two markers
right where I had put them previously.
And now let's just add another one right here.
All right.
And now if I reload the page,
then indeed, there is the marker,
and this one and, of course, all the other ones as well.
Beautiful.
So this is great.
And our application is now essentially finished.
I just want to show you one problem that we have now.
And that has to do with local storage.
So, remember that when I click here
on one of these workouts,
it will move the map to the workout,
and so that's gonna be fine,
but what also happens is that this clicks property
is gonna be increased by using the click method
that is inherited from the workout class.
But watch what happens now as I try to do that.
And so we get workout.click is not a function.
So, why do you think that is?
Well, let's try to take a look at the objects
that we get back from local storage.
So that's this one here.
And I know that right now
we have a ton of console.logs here,
so we should get rid of them at some point,
but anyway, for now let's take a look
at the objects that we basically have right now.
And so this is currently also the data
that is in this .workouts, right?
Now, if we take a look at this,
then here everything works okay.
But now if we take a look at the prototype chain,
you see that now it's no longer an object
of the type of running,
and also not of the type of workout, right.
So the entire prototype chain that we had before is gone.
So contrast that to one of these objects.
So again, the data is fine,
but here, we actually have the prototype with calcPace,
and then that has the click entities,
that description methods in the workout prototype, right.
So, the problem here is that basically,
when we converted our objects to a string,
and then back from the string to objects,
we lost the prototype chain.
And so these new objects here
that we recovered from the local storage
are now just regular objects.
They are now no longer objects
that were created by the running
or by the cycling class.
And so therefore,
they will not be able to inherit
any of their methods.
And so in the end,
that's the reason why workout.click
is now not a function anymore.
So, again, because the object now no longer has it
in its prototype.
So you see that this is just the regular methods
that are available on any object, all right.
So this can be a big problem
when you work with local storage
and object oriented programming
like we are doing here.
Now to fix this problem, we could go ahead
and restore the object right here.
So, in our getLocalStorage,
we could now loop over this data here,
and then restore the objects
by creating a new object using the class,
based on the data that is coming here from local storage.
But that's a little bit of work
and so we're not gonna do that here.
And so instead what I will do
is to simply disable the functionality
of counting the clicks.
So remember that when I first introduced this method here,
I told you that one of the reasons for it
is that I also wanted to show you something
in the next lecture.
And so that's exactly here,
what I wanted to show you.
So basically, that objects coming from local storage
will not inherit all the methods
that they did before.
All right.
And so with that, we are basically done.
Let's just get rid of some of these console.logs here,
because it's not really a good practice
to have them in the final code.
So we can have maybe one while we're developing,
but then at some point it simply clutters,
like our entire console workout.
And so that's not helpful.
And here again.
And you see that here
we still have all of these workouts, of course.
Now actually to finish,
I want to create a quick and easy way
to delete all of these workouts at once,
at least from local storage.
And so let's go back here,
and basically add a method
to the public interface of this class
for the first time.
So this class is a little bit unusual,
because right now, we don't have any public method.
So there's no public interface.
But now we will create a reset method here,
which we can then use out here
or even in the console.
And so, actually that's what we will do,
so using it in a console.
So what I want to do in this one here
is to basically remove this workouts item
from local storage.
And so besides setting and getting items,
we can also remove items based on the key.
All right.
And with this we removed our workouts
from the local storage,
and now we can then reload the page programmatically.
And so then the application will look completely empty.
And we can do this with location.reload.
And location is basically a big object
that contains a lot of methods
and properties in the browser.
And so one of the methods
is the ability to reload the page.
So, let's try that now.
And as I said, I will do that in the console.
So let's go actually back to our code
just to see where we can use it.
So, we are creating the app object
and then storing it in app, right.
And so we have access to this variable here,
of course, in the console.
So let's take a look at that actually.
And you'll see that indeed,
here we have these private fields,
and then we have all of these methods here
in the prototype.
And so one of them is of course, reset.
And so let's do that.
So reset.
And yeah, it worked.
So our application is back to being empty now
so that we can add some more realistic data
if we wanted to.
(typing)
For example like this,
or maybe we went cycling right here,
which is a really nice location.
And here, let's say we took two hours
and gained 500 meters, all right.
And now, of course,
everything is back to working here
because we got rid of that click method.
We still have some logins here to the console,
but, never mind, that's not a problem.
All right.
But what matters here
is that our application is now feature complete.
We implemented everything from our flowchart.
Right.
So everything is done here.
And so at this point, the final architecture
of our application looks like this.
So I have all of the methods here
that we implemented in the app class,
and also for workout, running and cycling.
Now, I'm not gonna go over this here now,
but it's a good idea
for you to actually check it out,
to get a really good idea
of how the different methods
call each other in our application.
So this arrows that you see here
are basically methods calling other methods.
All right.
And that's it, that's our application,
I hope that you liked it
and that you had as much fun as I had building it.
So this was a really great experience,
and now before we actually finish the section,
let's have one more final lecture,
where I will give you some suggestions
of how to improve this application even more.
So let's go there right now.