Fragmentos en Thymeleaf 3 cargados con ajax

Introducción

En este entrada voy a explicar cómo aprovechar las posibilidades que nos ofrecen los fragmentos de Thymeleaf para poder reutilizar bloques de un template y renderizarlos directamente mediante ajax.

Para ello vamos a poner como ejemplo de uso un caso recurrente…. la carga de una lista de elementos en un formulario filtrados según el valor de otro campo. En el ejemplo utilizaremos un formulario de selección de vehículos, seleccionando la marca se cargará automáticamente el listado de modelos existentes para la marca seleccionada.

 

Manos a la obra

La solución que vamos a implementar va a cargar mediante ajax la lista de modelos realizando una petición al servidor. La respuesta devolverá el código HTML que se corresponde con el listado de modelos filtrado por marca, posteriormente reemplazaremos el código HTML del listado de modelos por el nuevo utilizando jQuery.

Para ello lo primero que debemos implementar es la capa de servicios en la que podemos obtener un listado de marcas y modelos filtrados por marcas.

CarService.java

public interface CarService {
	List<String> findAllBrands();
	List<String> findAllModelsByBrand(String brand);
}

También necesitaremos crear el controlador que gestione las peticiones.

HomeController.java

@Controller
@RequestMapping
public class HomeController {

	private CarService carService;

	@Autowired
	public HomeController(CarService carService) {
		this.carService = carService;
	}

	@RequestMapping("/")
	public String index(Model model) {
		List<String> brands = carService.findAllBrands();
		Car car = Car.builder().build();
		model.addAttribute("car", car);
		model.addAttribute("brands", brands);
		return "home";
	}

	@RequestMapping("/ajax/brands")
	public String ajaxBrands(@RequestParam("brand") String brand, Model model) {
		List<String> models = carService.findAllModelsByBrand(brand);
		model.addAttribute("models", models);
		return "home :: models";
	}
}

Como se puede apreciar tenemos dos métodos que responden a distintas urls:

  • / Devuelve el template home que muestra el formulario de selección de vehículos.
  • /ajax/brands realiza una búsqueda de modelos filtrados por marca y con esa información recarga únicamente el fragmento models del template home (renderiza únicamente el HTML correspondiente al listado de modelos).

 

La última pieza que nos falta es el template que muestra el formulario de selección de vehículos.

home.html

<div id="form-panel">
	<h3>Formulario</h3>
	<div class="panel panel-primary">
		<div class="panel-heading">Selecciona el modelo de coche</div>
		<div class="panel-body">
			<form th:object="${car}" method="post">
				<div class="form-group">
					<label for="marca">Marca</label>
					<select th:field="*{brand}" required="required" class="form-control">
						<option th:each="brand : ${brands}" th:value="${brand}" th:text="${brand}" />
					</select>
				</div>
				<div class="form-group">
					<label for="modelo">Modelo</label>
					<select th:field="*{model}" required="required" class="form-control">
						<option th:fragment="models" th:each="model : ${models}" th:value="${model}" th:text="${model}" />
					</select>
				</div>
			</form>
		</div>
	</div>
</div>

La parte importante a destacar es el tag th:fragment="models" añadido al elemento <option> de la lista de modelos. Con esto conseguimos que desde el método ajaxBrands del controlador podamos regenerar únicamente este fragmento inyectando en él la variable models. De esta forma si hacemos una petición a la url /ajax/brands?brand=Seat onbtendremos el HTML con todas las opciones existentes de modelos para la marca Seat.

Para finalizar únicamente nos queda añadir en el home.html un poco de javascript con jquery para que tanto al cargar la página y como al seleccionar una marca se recargue mediante ajax el listado de modelos.

<script th:inline="javascript">
/*[+
	$(function() {
		$("#model").load('/ajax/brands', $("#brand").serialize());
		$('#brand').on('change', function() {
			$("#model").load('/ajax/brands', $("#brand").serialize());
		});
	});

+]*/ 
</script>

 

Conclusión

Gracias a esta técnica podemos realizar cargas de fragmentos de nuestra página mediante ajax sin realizar el trabajo adicional de la solución tradicional que nos obliga a generar una respuesta con los datos en formato JSON y realizar un procesamiento de esa información mediante javascript para recargar la información.

El código de completo del ejemplo está disponible en el repositorio spring-boot-thymeleaf-fragment-ajax del GitHub del blog,
Recursos:

 

Código fuente – Repositorio GitHub

Sobre el autor

Apasionado de las tecnologías, entusiasta del desarrollo de software. Especializado en Java EE. Cofundador de www.logrosxbox.com.