Instructor: Welcome back
and let's now implement the architecture
that we just discussed in the previous lecture.
And so, let's start by implementing that App class
that I just showed you previously.
And actually, I also have this architecture diagram
right here, and it is this one called part one.
And so as you see, this is exactly
what we just discussed before.
And so I will now go ahead and start to implement
this class here and all of these methods.
All right.
So class App,
and then we need the constructor,
which for now I'm gonna leave empty here.
Then we need getPosition,
Next we need loadMap,
let's take a look at what's next here.
Then we have showForm toggleElevationField,
and newWorkout.
So, showForm,
toggleElevationField,
and finally newWorkout, okay.
And so now, we have this structure
where we can put our code that we already have.
So let's start with this one here.
So getting the user's current position using geolocation
and so I'm gonna start by taking all of this code here.
So, it ends here I believe.
Yap, that's the one, cut it
and put it here into getPosition.
But now I actually want to refactor this a little bit better
because I think it's still a bit messy
to have this callback function here
off to get current position function right here, okay.
And so this function here, so this callback
of getCurrentPosition is gonna become the loadMap.
Okay, because that is exactly what we do here in function.
So let's cut this here
and put it here in loadMap
and actually of course we don't need this function keyword
here because we already called it a loadMap, right?
So let's just copy the parameter name here
and put it here and then get rid of these.
Okay.
And so now here as the first callback function,
which is the one for success, so actually here,
we need to call loadMap.
And so since we're now in a class,
we need to say this.loadMap.
And so in JavaScript, we'll then call
this callback function here and pass
in the position argument, as soon as the current position
of the user is determined, all right?
And so that event that I just mentioned
is basically this receive position.
And as I said, it is not an event in the common sense
that we used before.
So one that we hand using addEventListener,
but we can still think of this as an event.
All right, and so basically on that event,
the, this.loadMap method is called.
Now, here we have some other code,
So to event list notice, but for now,
let's actually make this code here work
because right now of course, none of this will do anything
to our application, will it?
And why is that?
Well, it is because first of all,
we need to create an actual object out of this class, okay?
So right now this is all just a plan.
So like the blueprint of a house,
but it is not the actual house yet
and so that analogy is sometimes very helpful.
So in this case, this is just a theoretical plan
or for application, but it's not a real object yet.
And so let's create that down here.
And so I will again call this app, but lowercase.
And so this app will be new App like this
and this application actually does need any arguments.
And so here in the constructor
we don't have any parameters in this method, okay.
Because right now we don't need any inputs
into our application.
If we need it then, could add that here to the constructor,
but in this case, that's just not necessary.
One example that we could use for inputs
in an application like this would be for example,
an object of options,
which is pretty common in third party libraries.
So if we were building a library for some other people
to use, then we could allow these developers to customize
the library, using some options.
But again does just not necessary in this case, all right?
Now in order to actually trigger the geolocation API,
this method here needs to be called, right?
But right now dad is not happening.
So how can we do that?
We could do this, right?
So app.getPosition and so then this code here
would get executed, right at a point
where the application loads
and that's because as we already know all the code,
that's here in the top level scope.
So out here outside of any function
will get executed immediately as the script loads,
all right?
And so, right in the beginning,
this new app variable here is created out of the class.
And then right now, immediately afterwards,
we would get to the position of the user.
However, why should we do this out here,
if we could simply do it inside of the class?
That would actually be a lot cleaner.
So inside of the class, we also have a method
that automatically gets called as the page loads.
So let's think how we could do this instead.
So inside of the App class,
we also have a method that gets executed
as soon as this app here is created, right?
And that is the constructor method, remember?
So this constructor method is called immediately
when a new object is created from this class
and this object that is created,
so this app object is created right in the beginning
when the page loads down.
So that means that the constructor is also executed
immediately as the page loads.
And so what we can do is to simply getPosition
in the constructor.
And so here, all we have to do is to change it
to the this word, so to the current object
and that's actually it.
So our code should actually already be working
at this point.
So the current position should be determined
here in this method and then the loadMap method
should be called with that current position, right?
And actually that's exactly what we also have here
in our diagram.
So you'll see the load page events will trigger
the constructor, which will then trigger getPosition.
And then as soon as we receive the position,
the loadMap method is called with the position, okay?
And so let's see if that actually works.
So here in our version,
and let's just reload it to manually here,
and here we go, so here is our map again.
So our first part of the refactoring is already working.
So that's great,
but now let's go back to our code here
because we still have some things to fix here.
So remember that right here in the loadMap function,
we have this global variable here called map.
I mean, we don't have here,
we are simply redefining it because it is defined up here
in the global scope.
But now, remember that we actually want everything
that is related to our application.
And that includes the map right in this App class.
And so therefore we're now gonna define the map
and map event as properties of the object.
And for that, let's actually use a private class field.
So remember that works like this and so map,
and now we will not set it to anything.
So just like this, and then the same for the mapEvent.
So both of them will now become private instance properties.
So properties that are gonna be present
on all the instances created through this class.
Now let's keep them here for now, in order to not break
the rest of the application,
but now we need to fix of course, the rest of this code.
So at least this load map method,
because that one relies on map and so therefore
we need to start using it now.
So here we now need to actually use this.map,
because again, this is now like a property that is defined
on the object itself,
it's no longer just a normal variable, right?
And then here the same, this.map
and here also this.map
and then here we need to define the mapEvent.
And so this one, we also called this.#mapEvent.
Now here, this is just wrong,
so let's fix that and all right, that's actually it,
but actually this will not yet work,
but let me show you the error before I actually fix it.
So let's go back here and reload this page again,
and now it should not work.
And so indeed we get, Cannot set property #map of undefined.
So on line 38, so that's right here.
And so if the error message says that we cannot set map
on the this keyword.
It means that something must be wrong
with the this keyword, right?
So let's use our friend console.log to take a look at it
and indeed it is undefined here.
So, why is that?
Well, this loadMap method is actually called
by function here, right?
So that's right here and in fact this is actually treated
as a regular function call, not as a method call.
So again, since this is a callback function,
we are not calling it ourselves.
It is to getCurrentPosition function that we'll call
this callback function once that it gets
the current position of the user.
And when it calls this method, so this function here,
then it does so, as a regular function call.
And as we learned before in a regular function call,
the this keyword is set to undefined.
And so that's why in here that this keyword is undefined.
But fortunately for us, there is a good solution
that we already know about.
And that solution is to manually bind the this keyword
to whatever we need.
And in this case, that is simply this.
So right here, this points to the current object.
All right?
And so that's exactly the this keyword
that we also want inside of loadMap.
And so here we explicitly say that, okay?
And remember that binds will simply return a new function
and so all of this here is still a function that JavaScript
can then call whenever it needs to.
So that should now work and so if we go back here,
then our maps should still work as it should.
And indeed, now we get the this keyword here.
So we have the map and we have the mapEvent
and we already see that or map is this big object here
that is coming from the leaflet library.
Now, all right.
So that was the most important thing
and the biggest thing we had to refactor,
but now let's quickly also talk about these two event
listeners that we have down here.
So the one listening for the event of submitting the form
and to one of toggling the input type field.
So basically that's this event here and this one, okay?
So where do you think these event listeners
should be located?
Do you think that they should be outside here?
So outside of the class.
Well, that doesn't make a lot of sense, does it?
So instead, just like before we actually want
these event listeners, of course, to be set
right at the beginning, so when a script first loads,
but again, of course, that should be inside of the class.
And so what is the method that automatically gets called
as soon as the script loads?
At least in this case.
Well, again, it is the constructor.
And so, we are gonna attach or event listeners
to the dumb elements right here in the constructor.
So, of course now we want to, again refactor this code
a little bit more and take this function here,
out of here to basically create its own function
with this code.
So that's all of this I believe, yeah.
And so where should that go?
Well, it should go here into newWorkout,
because submitting the form for the newWorkout
will of course create a new workout
and so that's what this method here is all about.
But then here, we also need the event
so we can preventDefault.
And then here again, we have the map event and the map,
so we need the this keyword,
or actually here we are just logging it to the console.
So there's no need for this one,
but here we need this.map like this, all right?
But now, well, let's actually analyze
what's gonna happen here.
So first of all, of course, here we need to replace this one
and actually call the newWorkout method
and actually not call it, but just passing it in, right?
Now, keep in mind that this method here is basically
an event handler function.
So it's a function that's gonna be called
by an event listener.
Now, do you remember what the this keyword looks like
inside of an event listener function
or of an event handler function?
Well, just to make it really clear, let's take a look at it.
So let's log the this keyword, now right?
So again, we need to wait for the map to load
and now as I click somewhere, then you see,
we get some error here.
So that's on line 60 cannot write private member map event.
Okay, so let's go to line 60 and see what happens there.
And so that's right here in this event handler function
of the event listener on clicking on the map.
So remember this is basically the add event listener
equivalent from the leaflet library.
So we need to fix this next, but for now,
let's go back to the newWorkout method
that we were working on and so I was trying to take a look
at the this keyword here,
but apparently that's not possible for now,
so let's just remove it here
and I will just tell you the solution.
So I was asking you,
what do you think the this keyword will be like
in this method now, because this is an event handler
function and so an event handler function
will always have the this keyword of the dumb element
onto which it is attached.
And so in this case, that's gonna be the form Element.
So again, inside of this method here,
the this keyword is gonna point to Form
and no longer to the App object.
And so once again, we need to fix that using bind,
all right?
And this is actually a real pain point of working
with event handlers in classes like we are doing right now.
So even when you're working in the real world
and you have event listeners inside of a class,
you will be binding the this keywords all the time
because otherwise many parts of your code
are not gonna work.
So again, in this case, because right
in this method call, here did this keyword
will simply point to the Form,
but that's just not where we want.
In most of these methods, we want this keyword
to still point to the object itself.
So in this case, the app object,
which is what this is currently pointing to.
And so here again, we will need to use the bind keyword
and so this should now work, all right?
Then we also have this one here to refactor,
but actually there is another method
that we want to implement, which is showForm, right?
And so showForm is basically this code right here.
So let's just cut it from here.
then paste that here, all right?
Now we of course also need this parameter here, now okay.
And then here we will call this.showForm,
but now if we run this code, then we will actually
get the same error that we already saw the last time
that we inspected our code.
So the error that we saw previously, when I clicked
on the map, and so that's gonna be this one.
And so it says, again, that we cannot write this mapEvent
property onto this object that we are trying to currently.
And so once again, the reason for that
is a incorrectly set this keyword.
So again, this method here is right now being used
as an event handler function right here.
And so just like in regular JavaScript,
the this keyword in this function here will then be set
to the object onto which the event handler is attached.
And so that's gonna be simply the map itself, okay.
And so here we are trying to write mapEvent on the map.
So again, the this keyword points to the map
because this is where we attached the event handler on.
And so once again, the solution to that is
to bind the this keywords because the this keyword
is the App object and so then here,
this will also be the App object.
And of course that's where we have the mapEvent property,
so right here.
Correct.
So with that fixed it should now work
and yeah, it does.
And so now we can also test the refactoring of the Form
that we just had before
and indeed we get an error, but that's not problem
that is completely normal when we are refactoring.
So I'm sure we just forgot like a this keyword on line 78.
So 78, yep that's correct.
So here, of course we need this.mapEvent like this,
let's give it another safe and another
try and to then, we're actually almost done with this video.
So, beautiful, now it works
and now all we need to do is to refactor this last piece
of code that we have in a constructor, because of course,
we also don't want this function here
to be right here in the constructor.
So basically every small piece of functionality
that is in our application,
we now want to be its own function.
So let's take this and for that,
we have the toggleElevationField method, all right?
Now this one here actually does not use
the this keyword anywhere
and so here, it doesn't matter
what the this keyword will be like.
And so therefore we don't have to bind it manually here
because as I just said, it doesn't really matter.
ElevationField, all right.
So now our constructor nicely simply
gets the currentPosition and then it adds
these two event listeners to the Form
and the input type element.
So that's very nice and clean,
everything is neatly organized into these methods.
And so I think this is a really great structure
that we just created here.
And now indeed, we no longer need these very ugly
global variables that we relied on before.
And now let's just test if this one here is working as well
and of course it is, all right.
So great, we successfully implemented or architecture
at least part of it.
And so now in the next lecture, we will actually implement
the rest of the architecture.
So that's gonna be these other three classes here.
So I hope to see you there soon.