Passare dati dal Controller alla View con Spring MVC

Proseguiamo il nostro viaggio alla scoperta di Spring MVC.

Nel post di oggi ci concentriamo sulla View, ovvero sull’elemento del pattern MVC che ci permette di visualizzare dati e d’interagire con l’applicazione.

Scopriamo quali meccanismi utilizzare per passare attributi dal Controller alla View e come sfruttare l’integrazione tra Spring MVC e Thymeleaf per costruire pagine con contenuto dinamico.

L’interfaccia Model

Diamo un’occhiata al codice seguente:

@Controller
@RequestMapping("/")
public class SalutiController {

  @GetMapping
  public String unSaluto(Model model) {
    model.addAttribute("nome", "David");
    return "ciao-mondo";
  }
}

Ho ripreso il nostro caro SalutiController e ho apportato qualche modifica all’handler unSaluto. In particolare, ho aggiunto al metodo un parametro di tipo Model.

L’interfaccia Model rappresenta un contenitore in cui inserire tutti gli oggetti “condivisi” da Controller e View. Si tratta, a tutti gli effetti, di un wrapper per un oggetto di tipo Map, che, difatti, possiamo utilizzare in modo analogo, aggiungendo valori associati a chiavi identificative.

Nell’esempio precedente ho aggiunto un semplice valore di tipo String che rappresenta il nome di chi sta salutando…

Ora, se vi state domandando da chi verrà passato il parametro model al nostro metodo, la risposta è piuttosto semplice… da Spring. La “magia” dietro a questo meccanismo la conosciamo già: si chiama dependency injection.

Non solo Model

La classe ModelAndView costituisce un altro approccio (meno consueto, ma non per questo meno efficace) al passaggio di dati alla View.

La classe ingloba un riferimento alla pagina di destinazione e un oggetto di tipo Map che conterrà gli attributi da esporre verso la View.

Un’istanza di ModelAndView può essere utilizzata come valore di ritorno dell’handler method:

@GetMapping
public ModelAndView unSaluto() {
  return new ModelAndView("ciao-mondo", "nome", "David");
}

Costruiamo la pagina

Una volta specificati quali attributi devono essere inviati alla View, possiamo recuperarli e utilizzarli all’interno della pagina.

Il codice HTML riportato di seguito visualizzerà il nostro nuovo saluto al mondo:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Pagina dei saluti</title>
</head>
<body>
  <h1>Ciao Mondo! Sono <span th:text="${nome}"></span>...</h1>
</body>
</html>

In questo caso ho modificato il template ciao-mondo.html, aggiungendo qualche nuovo, interessante elemento:

  • il namespace xmlns:th="http://www.thymeleaf.org", definito nel tag html, consente l’utilizzo degli attributi non-standard di Thymeleaf;
  • l’attributo th:text, utilizzato nello span del nome;
  • l’espressione ${nome}, utilizzata da th:text.

Il primo punto è semplice, per utilizzare gli attributi definiti da Thymeleaf dobbiamo dichiarare il namespace corrispondente all’interno della nostra pagina. Questo permette al framework d’interpretare e risolvere correttamente gli elementi dinamici del template.

L’attributo th:text, è uno dei più semplici tra gli attributi succitati. Esso viene utilizzato per ottenere un valore testuale da un’espressione. Nel nostro caso, l’attributo valorizzerà lo span con il testo risultante dall’espressione: ${nome}.

In Thymeleaf, la sintassi ${...} viene utilizzata per accedere al valore di una variabile dichiarata, in generale, nell’application context. Nel nostro caso la variabile corrisponde ad un attributo presente nel Model e la valutazione dell’espressione ${nome} restituirà come risultato la stringa "David" (o il nome che avete scelto di usare).

Se andiamo ad eseguire il codice visto finora, otterremo qualcosa di questo tipo:

Ciao David

Fico, no? :-)

L’annotazione @ModelAttribute

Esiste un altro modo di passare oggetti alla View, in Spring MVC.

L’annotazione @ModelAttribute permette di “marcare” un metodo o un suo parametro come attributo da inserire o recuperare dal Model.

Vediamo un esempio:

@Controller
@RequestMapping("/altri-saluti")
public class AltriSalutiController {

  @GetMapping
  public String unSalutoSemplice(){
    return "ciao-mondo-alt";
  }

  @GetMapping("/verboso")
  public String unSalutoVerboso(){
    return "ciao-mondo-alt-verbose";
  }

  @ModelAttribute("nome")
  public String nome(){
    return "David";
  }
}

Ho creato un nuovo Controller per salutare il Mondo in modo leggermente diverso.

Nella classe ho definito due handler method che fanno capo a due differenti pagine. Se ci fate caso però, nessuno dei due presenta un parametro di tipo Model. Eppure, se andate a frugare nel codice delle due pagine, troverete che in entrambe ho utilizzato l’attributo nome

Quindi?

Ciao David verboso

Avrete notato che nel nuovo Controller, oltre ai due handler, ho aggiunto un altro metodo che restituisce la stringa "David" ed è annotato con @ModelAttribute. Proprio questo metodo ha il compito di aggiungere il nome agli attributi accessibili dalle View.

Questo tipo di approccio risulta molto utile per aggiungere al Model gli attributi comuni a tutti gli handler definiti in un Controller. I metodi annotati con @ModelAttribute, infatti, vengono invocati subito prima dell’handler, in corrispondenza di una qualsiasi richiesta HTTP gestita dallo stesso.

Nota L’elemento name (alias di value) dell’annotazione @ModelAttribute può essere utilizzato per indicare esplicitamente la chiave identificativa dell’attributo. Se tale chiave viene omessa, il nome associato all’attributo verrà generato automaticamente dal framework e corrisponderà al nome del tipo dell’attributo con la prima lettera minuscola (formato camel-case).

In conclusione

In questo post abbiamo valutato diversi modi di passare oggetti dai nostri Controller alle pagine, in modo da rendere dinamica la creazione delle stesse.

Come al solito, Spring fornisce differenti approcci all’implementazione di una soluzione, lasciando allo sviluppatore la libertà di decidere la strada più adatta alle sue esigenze.

Avremo modo di rincontrare l’annotazione @ModelAttribute, e la vedremo in azione in un’altra veste, quando parleremo della gestione dei form.

Per il momento mi fermo qui. Alla prossima e buon lavoro,

David