Library
I dag bygger vi ovenpå jeres bibliotekssystem.
Målet er, at vi går fra:
“Jeg kan logge ind”
til:
“Serveren ved, hvem jeg er, og kan vise mig de data, der hører til mig.”
Derefter laver vi en adminbruger, som får adgang til nogle ekstra funktioner.
Først: Hvis I vil undgå at skriv templates\ og .html hver gang I renderer en side, så kan i lave en lille konfigurationsklasse:
package configuration;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
public class ThymeleafConfig {
public static TemplateEngine templateEngine() {
TemplateEngine templateEngine = new TemplateEngine();
ClassLoaderTemplateResolver templateResolver =
new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
}
og bruge den i jeres main:
public static void main(String[] args) {
Javalin app = Javalin.create(config -> {
config.staticFiles.add("/public");
config.fileRenderer(new JavalinThymeleaf(ThymeleafConfig.templateEngine()));
UserController.setRoutes(config);
LoanController.setRoutes(config);
AdminController.setRoutes(config);
}).start(7070);
}
Lav funktionaliteten til at oprette en bruger.
Når formularen sendes:
- Hent
usernameogpasswordmedctx.formParam(). - Opret brugeren gennem
UserService. - Hvis brugeren bliver oprettet, skal brugeren gemmes i session scope.
- Vis velkomstsiden.
Test, at brugeren bliver oprettet.
Vi gemmer hele User-objektet i sessionen:
ctx.sessionAttribute("user", user);
På velkomstsiden skal vi vise brugernavnet på den bruger, der er logget ind.
Brug Thymeleaf til at hente brugeren fra sessionen:
<span th:text="${session.user.username}">bruger</span>
Test derefter, at brugernavnet stadig bliver vist, når I laver en ny request.
- Hvad er en session?
- Hvorfor har vi brug for session scope?
- Hvad er forskellen på session scope og attribute scope?
På velkomstsiden skal brugeren kunne se sine bøger.
Brugeren ligger allerede i sessionen, og brugerens lån ligger på User.
Brug th:each:
<div class="books">
<div class="book" th:each="loan : ${session.user.loans}">
<h2 th:text="${loan.book.title}">
Grapes of Wrath
</h2>
<p th:text="${loan.book.author.name}">
John Steinbeck
</p>
</div>
</div>
Hvad sker der egentlig her?
session.user
↓
loans
↓
loan
↓
book
↓
author
↓
name
Og hvad betyder:
th:each="loan : ${session.user.loans}"
Nu skal vi have forskellige typer brugere.
Opret en adminbruger i vores system. I kan bare hardcode det i UserFactory.
Adminbrugeren skal have:
username
password
isAdmin
Test, at adminbrugeren kan logge ind.
På velkomstsiden skal administratoren have adgang til nogle ekstra funktioner.
Lav knapper til:
- Alle brugere
- Alle bøger
- Find bruger
- Find forfatters bøger
De skal kun vises, hvis den aktuelle bruger er administrator.
Brug Thymeleaf th:if:
<div th:if="${session.user.isAdmin}">
...
</div>
Hvad er forskellen på:
Authentication
Hvem er du?
Authorization
Hvad må du?
Nu skal vi begynde at sende data fra controlleren til den konkrete HTML-side.
Lav en route:
/users
Controlleren skal:
- hente alle brugere fra
UserService - gemme listen i attribute scope
- rendere en Thymeleaf-side
Fx:
ctx.attribute("users", users);
ctx.render("users.html");
Lav derefter users.html.
Vis brugerne i en HTML-tabel.
Tabellen kan fx indeholde:
| Brugernavn | Admin |
|---|---|
| Sally | false |
| admin | true |
Brug th:each til at lave én række for hver bruger:
<tr th:each="user : ${users}">
<td th:text="${user.username}">Sally</td>
<td th:text="${user.isAdmin}">false</td>
</tr>
Lav en route:
/books
Controlleren skal:
- hente alle bøger
- lægge dem i attribute scope
- rendere
books.html
Vis bøgerne i en Thymeleaf-tabel.
Tabellen skal vise:
| Titel | Forfatter | ISBN |
|---|---|---|
| … | … | … |
Brug th:each.
Lav en formular, hvor administratoren kan søge efter en bestemt bruger.
<form action="/finduser" method="get">
<input type="text" name="username">
<button type="submit">Find bruger</button>
</form>
Controlleren skal hente brugernavnet med:
String username = ctx.queryParam("username");
Find brugeren gennem UserService.
Hvis brugeren findes:
ctx.attribute("user", user);
ctx.render("userinfo.html");
Vis brugerens oplysninger i en Thymeleaf-tabel.
Hvis brugeren ikke findes, skal der vises en passende fejlbesked.
Lav en formular, hvor administratoren kan søge efter en forfatter.
<form action="/booksbyauthor" method="get">
<input type="text" name="author">
<button type="submit">Find forfatters bøger</button>
</form>
Controlleren skal hente:
String author = ctx.queryParam("author");
Brug findByAuthor(...) i BookService til at finde bøgerne.
Læg resultatet i attribute scope:
ctx.attribute("books", books);
Vis resultatet i books.html.
Læg mærke til, at books.html ikke behøver at vide, hvor bøgerne kommer fra.
Den får bare en liste:
books
Derfor kan den samme side bruges til:
- alle bøger
- bøger af en bestemt forfatter
Vi har nu bevæget os gennem hele kæden:
HTML
↓
Request
↓
Controller
↓
Service
↓
Data
↓
Controller
↓
Attribute / Session
↓
Thymeleaf
↓
HTML
Og vi har brugt:
Session scope
til data, der skal leve på tværs af requests.
Attribute scope
til data, der skal bruges ved den konkrete rendering.
Thymeleaf
til at bruge data fra Java til at generere HTML.
Controlleren
til at koble HTTP, Java og HTML sammen.