Controlador
package com.trifulcas.SpringBootVistas.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.trifulcas.SpringBootVistas.model.Autor;
import com.trifulcas.SpringBootVistas.repository.AutorRepository;
// La anotación nos indica que es un controlador normal, que requerirá una vista
@Controller
// Nos indica la ruta de entrada general a este controlador
@RequestMapping("/autor")
public class AutorController {
// Necesitamos acceder a los datos por lo tanto creamos el repositorio
// el autowired nos hace inyección de dependencia automática
@Autowired
AutorRepository autorRepository;
// Aquí especificamos que accedemos vía get
@GetMapping("")
// Pongo el parámetro Model que nos permita pasar datos a la vista
public String getAutores(Model model) {
try {
// Obtengo los datos como en la API rest
List<Autor> autores = autorRepository.findAll();
// Paso la información a la vista vía model
// La vista tendrá una variable 'autores' con la lista de autores
model.addAttribute("autores", autores);
// Le digo que me cargue la vista 'autores' la buscará en templates
return "autores";
} catch (Exception ex) {
System.out.println(ex.getMessage());
return "error";
}
}
// Mapeo que me pasen un parámetro id
@GetMapping("/{id}")
// Tengo el parámetro id que me pasan y el model para pasar datos a la vista
public String getAutor(Model model, @PathVariable Integer id) {
try {
// Recupero el autor
Autor autor = autorRepository.findById(id).orElse(null);
if (autor != null) {
// Lo paso a la vista
model.addAttribute("autor", autor);
// Devuelvo la vista
return "autor";
} else {
// Me he creado una vista para mostrar un error
return "error";
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
return "error";
}
}
// El primer mapeo es con get para simplemente mostrar la vista
@GetMapping("/add")
// Pasamos como parámetro el autor para que la vista lo pueda tener disponible
public String addAutor(Autor autor) {
// Simplemente mostramos la vista
return "addautor";
}
// Cuando desde la vista nos añaden el autor entramos por 'POST'
@PostMapping("/add")
// Con @Validated recuperamos los datos y los metemos dentro de una entidad,
// spring lo hace solo
// En result se guardan los datos de la validación, es decir ¿Lo que nos mandan
// son datos válidos? Si es que sí, no dará error, en caso contrario
// en result tenemos la lista de errores
public String addAutorDatos(@Validated Autor autor, BindingResult result) {
System.out.println(autor);
System.out.println(result);
try {
// Si hay algún error volvemos a mostrar la vista y además
// fields.error tendrá la información de los errores
if (result.hasErrors()) {
return "addautor";
}
// Si no hay ningún error guardamos el autor
autorRepository.save(autor);
// Y en vez de devolver una vista, redirigimos al índice
return "redirect:/autor";
} catch (Exception ex) {
System.out.println(ex.getMessage());
return "error";
}
}
@GetMapping("/edit/{id}")
// Usamos el model porque tenemos que recuperar al autor
public String addAutor(@PathVariable Integer id, Model model) {
try {
// Primero, buscamos el autor que se quiere editar
Autor autor = autorRepository.findById(id).orElse(null);
if (autor != null) {
// Añadimos el autor al modelo
model.addAttribute("autor", autor);
return "updateautor";
} else {
return "error";
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
return "error";
}
}
@PostMapping("/update/{id}")
public String updateAutor(@PathVariable Integer id, @Validated Autor autor, BindingResult result) {
System.out.println(autor);
try {
if (result.hasErrors()) {
return "updateautor";
}
autorRepository.save(autor);
return "redirect:/autor";
} catch (Exception ex) {
System.out.println(ex.getMessage());
return "error";
}
}
}
Vistas
autores
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista de autores</title>
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<h2>Autores</h2>
<!-- El th:each es como un bucle foreach, en este caso le estamos diciendo
que recorra la variable autores y lo guarde en una variable llamada autor
-->
<a class="btn btn-success" th:href="@{/autor/add}">Añadir autor</a>
<div class="card w-50" th:each="autor: ${autores}">
<!-- Si yo estoy guardando cada elemento en una variable llamada 'autor'
puedo acceder a sus propiedades y mostrarlas
Pongo un enlace al detalle del autor -->
<div class="card-header"><a class="btn btn-primary" th:href="@{/autor/{id}(id=${autor.idautor})}"><span
th:text="${autor.idautor}"></span></a></div>
<!--
Cuando accedemos a la propiedad nombre spring boot busca el getter
Es decir, intentará acceder a getNombre() -->
<div class="card-body"><span th:text="${autor.nombre}"></span> <a class="btn btn-secondary" th:href="@{/autor/edit/{id}(id=${autor.idautor})}">Editar</a></div>
</div>
</body>
</html>
addautor
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Añadir Autor</title>
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<h2>Añadir Autor</h2>
<!-- Lo primero es hacer un formulario con los campos de autor, en este caso el nombre
-->
<form th:action="@{/autor/add}" th:object="${autor}" method="POST">
<label for="name">Nombre</label>
<!-- el campo hace referencia a la propiedad nombre -->
<input type="text" th:field="*{nombre}" id="nombre" placeholder="Nombre">
<!-- Si hay algún error lo mostramos (viene de result) -->
<span th:if="${#fields.hasErrors('nombre')}" th:errors="*{nombre}"></span>
<input class="btn btn-success" type="submit" value="Añadir">
</form>
</body>
</html>
updateautor
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Editar Autor</title>
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<h2>Editar Autor</h2>
<!-- Lo primero es hacer un formulario con los campos de autor, en este caso el nombre
-->
<form th:action="@{/autor/update/{id}(id=${autor.idautor})}" th:object="${autor}" method="POST">
<input type="hidden" th:field="*{idautor}" id="idautor">
<label for="name">Nombre</label>
<!-- el campo hace referencia a la propiedad nombre -->
<input type="text" th:field="*{nombre}" id="nombre" placeholder="Nombre">
<!-- Si hay algún error lo mostramos (viene de result) -->
<span th:if="${#fields.hasErrors('nombre')}" th:errors="*{nombre}"></span>
<input class="btn btn-success" type="submit" value="Modificar">
</form>
</body>
</html>
autor
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Autor</title>
<!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Latest compiled JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<h2>Autor</h2>
<!-- El th:each es como un bucle foreach, en este caso le estamos diciendo
que recorra la variable autores y lo guarde en una variable llamada autor
-->
<div class="card w-50">
<!-- Si yo estoy guardando cada elemento en una variable llamada 'autor'
puedo acceder a sus propiedades y mostrarlas -->
<div class="card-header"><span th:text="${autor.idautor}"></span></div>
<!--
Cuando accedemos a la propiedad nombre spring boot busca el getter
Es decir, intentará acceder a getNombre() -->
<div class="card-body"><span th:text="${autor.nombre}"></span></div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Id</th>
<th>Titulo</th>
<th>Páginas</th>
</tr>
</thead>
<tbody>
<!-- Puedo acceder a la propiedad libros porque mi entidad autor
tiene un getLibros() que me devuelve los libros y los recorro con el each -->
<tr th:each="libro: ${autor.libros}">
<td><span th:text="${libro.idlibro}"></span></td>
<td><span th:text="${libro.titulo}"></span></td>
<td><span th:text="${libro.paginas}"></span></td>
</tr>
</tbody>
</table>
<a class="btn btn-primary" th:href="@{/autor}">Ir al índice</a>
</body>
</html>