- - Quick start guides
- - How Jumpydoll works
- - Tutorials
- - Java/Spring Boot tutorial
- - Step 1: Introduction
- - Step 2: Setting up the project
- - Step 3: Building the first API endpoint
- - Step 4: Storing data in MongoDB Atlas
- - Step 5: Adding more API endpoints
- - Step 6: Creating a user interface
- - Step 7: Conclusion
Step 6: Creating a user interface - Java/Spring Boot tutorial
Adding the frontend
In order to make it easy to use your to-do list, you can make a website that makes the HTTP requests instead of using curl. In the future, we will update this guide with more details on how the frontend code works.
The HTML file is responsible for defining the layout of the website. It also imports the Javascript file. The Javascript file is
responsible for running code and handling interaction. It waits for the user to press a button, and makes the corresponding HTTP call
using the fetch
method. After calling a write API endpoint, it reads all the data again so the list remains up-to-date.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
<script src="index.js"></script>
<title>Jumpydoll Spring Boot app</title>
</head>
<body>
<div class="container" style="max-width: 50em">
<h1>Todo List</h1>
<div class="spinner-border" role="status" id="loading">
<span class="visually-hidden">Loading...</span>
</div>
<ul class="list-group mb-2" id="itemList"></ul>
<form id="addItemForm">
<div class="input-group flex-nowrap">
<input type="text" class="form-control" id="description"></input>
<button class="btn btn-primary" type="submit" id="addButton">Add</button>
</div>
</form>
</div>
</body>
</html>
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('addItemForm').addEventListener('submit', (e) => {
e.preventDefault()
const description = document.getElementById('description').value;
const postBody = {
description,
finished: false,
}
document.getElementById("addButton").disabled = true;
fetch('api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postBody),
}).then(res => res.json())
.then(body => {
document.getElementById('description').value = '';
document.getElementById("addButton").disabled = false;
refreshList();
});
})
refreshList();
})
function refreshList() {
document.getElementById('loading').style.display = 'block';
document.getElementById('itemList').style.display = 'none';
document.getElementById('addItemForm').style.display = 'none';
fetch('api/tasks/')
.then(res => res.json())
.then(body => {
const itemList = document.getElementById('itemList');
while (itemList.firstChild) {
itemList.removeChild(itemList.firstChild)
}
body.forEach((item) => createItem(itemList, item));
document.getElementById('loading').style.display = 'none';
document.getElementById('itemList').style.display = 'block';
document.getElementById('addItemForm').style.display = 'block';
})
}
function createItem(itemList, todoItem) {
const element = document.createElement("li")
const deleteButton = document.createElement("button");
deleteButton.setAttribute('class', 'btn btn-light ms-auto');
const deleteButtonIcon = document.createElement("i");
deleteButtonIcon.setAttribute('class', 'bi bi-trash-fill');
deleteButton.appendChild(deleteButtonIcon);
deleteButton.addEventListener('click', createDeleteHandler(todoItem.id));
const checkBox = document.createElement("input");
checkBox.setAttribute('type', 'checkbox');
checkBox.setAttribute('class', 'form-check-input me-1');
checkBox.checked = todoItem.finished;
checkBox.addEventListener('change', createCompletedHandler(todoItem));
element.id = todoItem.id;
const flex = document.createElement('div');
flex.setAttribute('class', 'd-flex')
flex.appendChild(checkBox);
if (todoItem.finished) {
const text = document.createTextNode(todoItem.description);
const strike = document.createElement("s");
strike.appendChild(text);
flex.appendChild(strike);
} else {
const text = document.createTextNode(todoItem.description);
flex.appendChild(text);
}
flex.appendChild(deleteButton);
element.append(flex);
if (todoItem.finished) {
element.setAttribute('class', 'list-group-item list-group-item-secondary');
} else {
element.setAttribute('class', 'list-group-item');
}
itemList.appendChild(element);
}
function createCompletedHandler(todoItem) {
return function (e) {
fetch(`api/tasks/${todoItem.id}`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ ...todoItem, finished: !todoItem.finished }),
}).then(res => {
refreshList();
});
}
}
function createDeleteHandler(id) {
return function (e) {
fetch(`api/tasks/${id}`, {method: 'DELETE'})
.then(res => {
refreshList();
});
}
}
Testing the UI
Run the application again in your IDE. Now when you visit http://localhost:8080/jumpydoll-todo-list/ again in your browser, you will see the new frontend UI. You can add an item, mark it as complete, and delete an item. Now all of the API endpoints can be called through the web browser using buttons.
Pushing the changes
In your IDE, add the two new files to Git and create a commit with the message Add the frontend website
. Push the changes to GitHub again.
After the changes are done building, you should see your new website on Jumpydoll!