web-dev-qa-db-fra.com

Fragments Ajax Thymeleaf Spring MVC 3.2

Je crée une application avec Spring MVC 3.2 et le moteur de template Thymeleaf. Je suis débutant à Thymeleaf.

J'ai tout fonctionne, y compris Thymeleaf, mais je me demandais si quelqu'un connaissait un total simple et clair sur la façon de faire une simple demande Ajax au contrôleur et, en conséquence, de ne rendre qu'une partie d'un modèle (fragment).

Mon application a tout configuré (Spring 3.2, spring-security, thymeleaf, ...) et fonctionne comme prévu. Maintenant, je voudrais faire une requête Ajax (assez simple avec jQuery mais je ne veux pas l'utiliser, car Thymeleaf dans son tutoriel, le chapitre 11: Rendu des fragments de modèle ( link ) mentionne qu'il peut être fait avec des fragments.

Actuellement, j'ai dans mon contrôleur

@RequestMapping("/dimensionMenuList")
public String showDimensionMenuList(Model model) {

    Collection<ArticleDimensionVO> articleDimensions;
    try {
        articleDimensions = articleService.getArticleDimension(ArticleTypeVO.ARTICLE_TYPE);
    } catch (DataAccessException e) {
        // TODO: return ERROR
        throw new RuntimeException();
    }

    model.addAttribute("dimensions", articleDimensions);

    return "/admin/index :: dimensionMenuList";
}

la partie de la vue où je voudrais remplacer <ul></ul> éléments de menu:

<ul th:fragment="dimensionMenuList" class="dropdown-menu">
    <li th:unless="${#lists.isEmpty(dimensions)}" th:each="dimension : ${dimensions}">
        <a href="#" th:text="${dimension.dimension}"></a>
    </li>
</ul>

Tout indice est grandement apprécié. Surtout si je n'ai plus à inclure de frameworks. C'est déjà trop pour Java application web telle quelle.

45
y0gie007

Voici une approche que j'ai rencontrée dans un article de blog :

Je ne voulais pas utiliser ces frameworks donc dans cette section j'utilise jQuery pour envoyer une demande AJAX au serveur, attendre la réponse et mettre à jour partiellement la vue (rendu de fragment).

Le formulaire

<form>
    <span class="subtitle">Guest list form</span>
    <div class="listBlock">
        <div class="search-block">
            <input type="text" id="searchSurname" name="searchSurname"/>
            <br />
            <label for="searchSurname" th:text="#{search.label}">Search label:</label>
            <button id="searchButton" name="searchButton" onclick="retrieveGuests()" type="button" 
                    th:text="#{search.button}">Search button</button>
        </div>

        <!-- Results block -->
        <div id="resultsBlock">

        </div>
    </div>
</form>

Ce formulaire contient un texte d'entrée avec une chaîne de recherche (searchSurname) qui sera envoyé au serveur. Il y a aussi une région (resultsBlock div) qui sera mise à jour avec la réponse reçue du serveur.

Lorsque l'utilisateur clique sur le bouton, la fonction retrieveGuests () sera invoquée.

function retrieveGuests() {
    var url = '/th-spring-integration/spring/guests';

    if ($('#searchSurname').val() != '') {
        url = url + '/' + $('#searchSurname').val();
    }

    $("#resultsBlock").load(url);
}

La fonction de chargement jQuery envoie une requête au serveur à l'URL spécifiée et place le code HTML renvoyé dans l'élément spécifié (div de resultsBlock).

Si l'utilisateur entre une chaîne de recherche, il recherchera tous les invités avec le nom de famille spécifié. Sinon, il renverra la liste complète des invités. Ces deux demandes atteindront les mappages de demandes de contrôleur suivants:

@RequestMapping(value = "/guests/{surname}", method = RequestMethod.GET)
public String showGuestList(Model model, @PathVariable("surname") String surname) {
    model.addAttribute("guests", hotelService.getGuestsList(surname));

    return "results :: resultsList";
}

@RequestMapping(value = "/guests", method = RequestMethod.GET)
public String showGuestList(Model model) {
    model.addAttribute("guests", hotelService.getGuestsList());

    return "results :: resultsList";
}

Puisque Spring est intégré à Thymeleaf, il pourra désormais renvoyer des fragments de HTML. Dans l'exemple ci-dessus, la chaîne de retour "results :: resultsList" fait référence à un fragment nommé resultsList qui se trouve dans la page de résultats. Jetons un œil à cette page de résultats:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:th="http://www.thymeleaf.org" lang="en">

<head>
</head>

<body>
    <div th:fragment="resultsList" th:unless="${#lists.isEmpty(guests)}" id="results-block">
        <table>
            <thead>
                <tr>
                    <th th:text="#{results.guest.id}">Id</th>
                    <th th:text="#{results.guest.surname}">Surname</th>
                    <th th:text="#{results.guest.name}">Name</th>
                    <th th:text="#{results.guest.country}">Country</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="guest : ${guests}">
                    <td th:text="${guest.id}">id</td>
                    <td th:text="${guest.surname}">surname</td>
                    <td th:text="${guest.name}">name</td>
                    <td th:text="${guest.country}">country</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

Le fragment, qui est une table avec des invités enregistrés, sera inséré dans le bloc de résultats.

71
Sohail

Rendu uniquement Thymeleaf fragments fonctionne également bien avec ModelAndView.

Votre contrôleur

@RequestMapping(value = "/feeds", method = RequestMethod.GET)
public ModelAndView getFeeds() {
    LOGGER.debug("Feeds method called..");
    return new ModelAndView("feeds :: resultsList");
}

Ta vue

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">

<head></head>
<body>
    <div th:fragment="resultsList" id="results-block">
        <div>A test fragment</div>
        <div>A test fragment</div>
    </div>
</body>
</html>

Qu'est-ce qui est réellement rendu

<div id="results-block">
    <div>A test fragment</div>
    <div>A test fragment
    </div>
</div>
7
Abdullah Khan

En tant que version alternative à la grande réponse de Sohail, je veux donner une version qui en utilisant javascript peut envoyer l'intégralité de l'objet th: au contrôleur, en intégrant Thymeleaf dans vos formulaires, sans avoir à utiliser @PathVariable qui devient désordonné ou inutilisable du tout lorsque vous avez des formulaires avec de nombreux champs.

Pour le formulaire (en utilisant un exemple qui renvoie un objet qui a un identifiant et un nom Strings, et alimente une zone de liste déroulante avec une carte qui a certains de ces objets en tant que valeurs), nous avons:

<form method="post" th:action="@{/yourMapping}" th:object="${object}" id="yourFormId">
    <select th:field="*{mapOfObjects}">
       <option 
          th:each="entry: ${mapOfObjects}"
          th:value="${entry.value.id}"
          th:text="${entry.value.name}" >
       </option>
    </select>

    <p>Name: 
       <input type="text" th:field="*{name}" />
    </p>
</form>

Lorsque ce formulaire est soumis (en utilisant un bouton de type soumettre par exemple) le document entier sera remplacé. Cependant, nous pouvons intercepter cette soumission avec javascript et le faire de la manière ajax. Pour ce faire, nous allons ajouter un intercepteur à notre formulaire à l'aide d'une fonction. Appelez d'abord la fonction qui ajoute l'intercepteur juste après le formulaire:

<script>formInterceptor("yourFormId");</script>

Et la fonction ressemble à ceci (placez-la dans la tête du document ou à l'endroit qui vous convient):

<script>
function formInterceptor(formName) {
    var $form = $("#" + formName);

    $form.on('submit', function(e) {
        e.preventDefault();
        $.ajax({
            url : $form.attr('action'),
            type : 'post',
            data : $form.serialize(),
            success : function(response) {
                if ($(response).find('.has-error').length) {
                    $form.replaceWith(response);
                }
                else{
                    $("#ajaxLoadedContent").replaceWith(response);
                }
            }
        });
    });
};
</script>

Maintenant, chaque fois que le formulaire est soumis, cette fonction se déclenchera et elle:

  • Empêcher la soumission du formulaire d'origine
  • Faire un appel ajax en utilisant l'url définie dans l'action th:
  • Sérialiser les données du formulaire. Votre contrôleur pourra le recevoir dans un objet
  • Remplacez la partie de votre code html par le fragment retourné

La pièce remplacée devrait ressembler à ceci

<div id="ajaxLoadedContent"></div>

Et le contrôleur peut recevoir l'objet th: dans le formulaire, avec ses valeurs remplies, comme ceci (Remplacer l'objet par le type de votre objet et "objet" par un nom propre):

@PostMapping(value = /yourMapping)
public String ajaxCtrlExample(@ModelAttribute("object") Object object, Model model) {
    return yourFragmentPath;
}

Et c'est tout. Appelez la fonction qui ajoute l'intercepteur après chaque formulaire dont vous avez besoin dans ajax-version.

1
Jorge.V