So we did now build and test this API.
This API which runs on a server
and we tested it with Postman.
Now I wanna come back to this slide,
which I discussed earlier.
I mentioned that you could be building an API,
which is not that useful to companies
from all over the world
but which you plan on using yourselves
but where you might have different clients
that should talk to it,
and that is what I wanna simulate now.
Now, I don't have a mobile app here
because building a mobile app is a totally different thing.
You need different skills for that.
There are entire courses on that,
and we also got courses on that if you wanna learn
building mobile apps.
But instead here what I got for you
is a decoupled web front end.
And I'll explain what that is
and why we might have that in just a second.
Attached, you find this project here.
A very simple separate project,
which is a different project than this backend
where I still have my server up and running.
This attached project is a different project.
And in the end, if you download and extract it,
you will see that it's basically a static website.
It just contains HTML, CSS
and some browser-side JavaScript code.
A lot of browser-side JavaScript code actually
and I'll explain that code
in a couple of minutes, no worries.
What this attached project doesn't include
is any backend code.
It has no server
and therefore, as mentioned,
it's a static site
and you could deploy it if you wanted to deploy it
to any static site hosting provider.
Now, this is a decoupled front end,
which we will connect to our backend.
I say decoupled because it's a separate project
and we would run it on a totally different server,
which has a different domain, a different IP address.
It's not running on the same server
as the server that hosts our backend.
The API itself.
These would be two different servers.
Now, the idea with this static site here
is that we have fairly simple HTML code.
I just got a styles import,
a script import, which will become important.
And then a dummy header
and then a main section with a form,
and then a section with an empty unordered list.
And this list is empty
because it will be populated
by the browser-size JavaScript code.
Now, I'll skip the styles.
Feel free to browse through them.
It's nothing too fancy in there.
Just a bunch of colors, margins, paddings,
what we always add.
Now, what is important is this JavaScript code here though.
In this code, I do a lot of things.
Therefore, it's quite a big file.
And what I do in there
is I start by getting hold of the FormElement
with this first line of code
and there I get hold of this from element,
which we got here.
Then in the next line of code,
I get hold of the todoListElement.
So of this empty unordered list,
which I have here, I get hold of it by using its ID.
I use that here to get this ElementById
and I store both the form and the TodosListElement
in separate constants.
Then I have this helper variable here,
which will become important later.
And then I have a first function here, loadTodos.
What I do in this function
is I send a request, a Ajax request,
a JavaScript-driven request as we did it many times before
in this course to localhost:3000/todos.
Now, keep in mind, this will be our backend server here.
This is the server, which runs on port 3000
and therefore, it's localhost:3000.
So this is the API on which we worked
over the previous minutes and lectures.
I'm sending a request to this,
and please note that unlike before in this course,
I don't just send a request to some path,
but instead, I include the full domain
and have http:// in front of it.
The reason for this is that here this will send a request
to a different server.
As I mentioned before,
this front end will be decoupled
and runs on a different server than the backend.
If I would be sending a request to just some path,
this would automatically be added
to the domain on which this front end code runs.
So which was responsible for serving that front end code.
And that would be the wrong domain
because the backend runs on a different server
with a different domain,
hence we have to enter the full domain here.
Then I send the request, handle some errors,
if the response is not OK and so on.
And then ultimately, I parse the response data again
as we did it before in the course.
Then I extract the todos
because keep in mind that on the backend code,
when we get all the todos,
we have this todos key in the response data,
which holds this todos array.
And I extract this here in line 21.
And then I loop through all these todos
to then call createTodoListItem for every todo.
And to createTodoListItem to this function,
which is defined down there,
I pass todo.text and todo.id.
These are the two data fields,
which I did set up on the backend before.
There in the todo.model,
we make sure that every todo has a text and id field.
So that's what I extract here
and pass to createTodoListItem.
Then in createTodoListItem,
I create a bunch of HTML DOM elements in JavaScript.
And that's also what we learned before in the course
and what we did before in the course.
I create those elements with document.createElement
and I create a list item
to which I add a data- attribute.
So this adds such a data-todoid attribute.
That's what this line does.
And I store the todoId,
which I got back in my response here
on this data- attribute.
We will need that later.
Then I create a paragraph
where I set the textContent equal to the todoText we got.
And then I create two buttons,
a Edit and a Delete button
to which I add click listeners
where I trigger startTodoEditing for the Edit button
and where I trigger deleteTodo
for the Delete button.
And then here I create a wrapper div
to which I add these two buttons,
and I only create that for styling reasons
to group these two buttons together in a div
and then I add my todoTextElement,
which I created before this paragraph.
And this WrapperElement, which contains the button
to the TodoItemElement.
So to the list item element.
I add the TextElement
and the button's WrapperElement to this ItemElement.
And then as a last step, I take this ItemElement itself
and add it to my todosListElement,
which is this unordered list,
which then won't be empty anymore
because I added a item.
And keep in mind,
this function's executed for all todos,
so all these todos are created
and added to this unordered list
when I fetched them here.
Of course, if I failed to fetch any todos,
no todos will be added
because then this for loop won't execute a single time.
Now that's this loadTodos function.
Before we have a look at that in greater detail,
let's go to the bottom of this file
because there you'll see I executed loadTodos at the end
so that when this script is loaded,
which happens when the page is loaded,
I do execute loadTodos
so that whenever I load the page,
I load all todos and I then output them on the page
with help of that JavaScript code I just showed you.
Now, you also see that here at the bottom,
I add an EventListener to the todoFormElement
to which we go hold in the first line before.
And I add an EventListener where I listen
for the submit event to then execute saveTodo.
Now, we'll have a look at that in a second.
First of all here,
we see that there is the startTodoEditing function,
and that was the function which I triggered
when this Edit button was clicked.
So the code I just show you,
there we have this EventListener
where we call startTodoEditing
when the Edit button is clicked.
That function is defined down there.
And what I do in startTodoEditing
is I get the button on which we clicked.
Then I use this button to go up one level
and a second level to select the list item
because keep in mind that the buttons are inside
of a div, as I showed you,
so that's the first parent element
and then that div is inside of the list item,
hence I have parentElement again
to get access to that list item.
And then I get the currentText of that todo
for which I clicked on a button
by accessing the first element child
of the edited todo element,
which will be that paragraph that I added,
the paragraph, the todoTextElement here
is the first child I add to the new todoItem,
so therefore it's the first element child,
this paragraph here.
And I get hold of that paragraph in startTodoEditing
to get the currently entered text of this todo,
and I then get access of the input element
inside of the todoFormElement
to populate that with that text of the todo
for which I clicked on the Edit button.
And in case this is all a bit abstract here,
you will see it in action
in a couple of minutes, no worries.
So that's what we got here.
Please note that I store this list item
in editedTodoElement,
which is not a variable or a constant
that belongs to this function
but which instead is this helper variable,
which I defined outside of all functions
at the top of this file.
And I'm doing it like this
so that the scope of this variable
is this entire file
and not a single function,
and I can therefore use this variable
in all the functions
because I will need it in other functions as well.
That's why I'm doing it like this.
So what startTodoEditing does in the end
is it sets this helper variable
to the todo for which we clicked on the edit button
and it populates the form input
with the text of that todo.
Now we have the saveTodo function as well,
which is this function which is triggered
if that form is submitted.
And that form is used for two scenarios
that we wanna add a new todo
or that we are editing an existing todo.
Now, saveTodo therefore then first prevents the default
so that the browser does not automatically generate
a HTTP request.
I don't want that to happen
because instead I wanna handle it myself here in JavaScript
and send my own behind-the-scenes
JavaScript-driven HTTP requests.
And then I get the formInput with the FormData object,
which we also saw before in the course already
where I get the text input value from the form.
And that is simply this only input I have in there,
which has this name of text.
So that is what I get here in line 139.
And then I check if I this editedTodoElement,
which is this helper variable we got here at the top.
I check if it's undefined or null,
which it would be initially
or if it is set to some other value.
Then here if it is not set,
so if it is undefined,
we're clearly adding a new todo
and therefore I then call createTodo
and pass the enteredTodoText
to that function, and we will have a look at this function
in a second as well.
Or if editedTodoElement
is set I know that I am editing a todo.
And therefore in that case,
I actually call a different function
and pass the entered text to that.
Now, let's take a look at these two functions.
We also have a deleteTodo function
to which I'll come back in a section.
You can probably guess what it does.
But let's, first of all, have a look at createTodo.
In there in the end, I get this todoText
because we passed it in
in the function we just had a look at.
And then here I send another request,
a POST request to our backend domain /todos.
I add my test there in the request.body.
And I named the field here in the request.body text
because on the backend in the todos.controller
in addTodo, I'm looking for a text field on request.body.
So we'll have to use this key on the front end as well.
I set my headers so that the body parser works correctly
on the backend.
Then I handle errors
and then I extract the responseData
so that I get the ID of the createdTodo
because on the backend, we do update our todo.id
and then we send this updated todo back under createdTodo.
So that's what I get here on the front end then.
And then again I call this createTodoListItem function,
which we saw a couple of minutes ago
to add a new todo list item
to this unordered list.
In updateTodo, I get the ID of the todo
that should be updated
by looking at the dataset,
so these data- properties
on the todo item element,
which we stored in our helper variable.
And then I use this todoId to add it to my URL here.
So I sent this to my domain /todos/ this todoId,
which I got here.
And it's a PATCH request here
with a request.body where we have the newText field
because again, on the backend in updateTodo,
which is triggered for such patch requests,
I'm looking for a new text field
on the incoming request body.
So that's what I sent here.
Then I've got a bunch of error handling here.
And then in the end, I'm updating my DOM element,
my editedTodoElement, so this selected TodoElement,
which we stored in that helper variable.
I'm reaching out to the firstElementChild in there,
which is still the paragraph in that todo item
for which we clicked on the edit button.
And I set the textContent to this newTodoText we have
for this todo.
And then I reset my input in my form to clear its value.
And I reset the editedTodoElement to set it back to null
so that this is now null
and if we would submit the form again therefore,
for example, this would now be mapped
and we're adding a new todo
unless we click editedTodo
on an existing todo item again.
Now, last but not least, I also got the deleteTodo function,
which is triggered when that Delete button is clicked
for a todo item.
We added such a delete button as well
when we add a todo item.
Keep that in mind.
And in deleteTodo, we do what the name suggests.
I get the todoid by getting the todoElement
for which we clicked on a button
by again going up two levels from the button
to dive into the div
that groups the buttons and then into the list item
that holds the div
and then here I've got the todoid,
and then I send a DELETE request
to my domain /todos/todoid
so that this is part of the path.
No body because the DELETE route needs no body
because in deleteTodo,
I just need the ID from the path
and then I'll delete the todo on the backend here.
So I'll send this request
and then have some error handling,
and then I remove that todoElement,
which I selected here to remove it from the DOM,
to remove this list item from the DOM.
That is the front end code.