web-dev-qa-db-fra.com

Manière recommandée pour manipuler Thymeleaf Spring MVC AJAX Les formulaires et leurs messages d'erreur

Quelle est la méthode recommandée pour gérer les formulaires AJAX et leurs messages d'erreur du côté des objets Thymeleaf?

J'ai actuellement un contrôleur Spring qui renvoie une vue d'ensemble JSON des champs et leurs messages d'erreur respectifs, mais devoir recourir à l'utilisation de JQuery entièrement manuscrite (ou simplement de Javascript) se sent juste un peu faux et lent; surtout en raison du grand nombre de formulaires que je compte utiliser dans la candidature.

12
Kristof

Ce que j'aime faire, c'est remplacer tout le formulaire lorsqu'une erreur survient. Ce qui suit est un exemple super primitif. Je ne vais pas utiliser une tonne de fragments pour restituer une forme ... mais restez simple.

Ceci a été écrit dans Spring 4.2.1 et Thymeleaf 2.1.4

Une classe de base représentant un formulaire d'informations utilisateur: UserInfo.Java

package myapp.forms;

import org.hibernate.validator.constraints.Email;
import javax.validation.constraints.Size;
import lombok.Data;

@Data
public class UserInfo {
  @Email
  private String email;
  @Size(min = 1, message = "First name cannot be blank")
  private String firstName;
}

Le contrôleur: UsersAjaxController.Java

import myapp.forms.UserInfo;
import myapp.services.UserServices;
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.web.bind.annotation.*;

import javax.transaction.Transactional;

@Controller
@Transactional
@RequestMapping("/async/users")
public class UsersAjaxController {
  @Autowired
  private UserServices userServices;

  @RequestMapping(value = "/saveUserInfo", method = RequestMethod.POST)
  public String saveUserInfo(@Valid @ModelAttribute("userInfo") UserInfo userInfo,
                             BindingResult result,
                             Model model)  {
    // if any errors, re-render the user info edit form
    if (result.hasErrors()) {
        return "fragments/user :: info-form";
    }
    // let the service layer handle the saving of the validated form fields
    userServices.saveUserInfo(userInfo);
    return "fragments/user :: info-success";
  }
}

Le fichier utilisé pour rendre le formulaire et le message de réussite: fragments/user.html

<div th:fragment="info-form" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
  <form id="userInfo" name="userInfo" th:action="@{/async/users/saveUserInfo}" th:object="${userInfo}" method="post">
    <div th:classappend="${#fields.hasErrors('firstName')}?has-error">
      <label class="control-label">First Name</label>
      <input th:field="*{firstName}" type="text" />
    </div>
    <div th:classappend="${#fields.hasErrors('first')}?has-error">
      <label class="control-label">Email</label>
      <input th:field="*{email}" ftype="text" />
    </div>
    <input type="submit" value="Save" />
  </form>
</div>

<div th:fragment="info-success" xmlns:th="http://www.thymeleaf.org" th:remove="tag">
  <p>Form successfully submitted</p>
</div>

Le code JS soumettra simplement le formulaire à l'URL fournie dans l'attribut d'action de formulaire. Lorsque la réponse est renvoyée au rappel JS, recherchez les erreurs éventuelles. S'il y a des erreurs, remplacez le formulaire par celui de la réponse.

(function($){
  var $form = $('#userInfo');
  $form.on('submit', function(e) {
    e.preventDefault();
    $.ajax({
      url: $form.attr('action'),
      type: 'post',
      data: $form.serialize(),
      success: function(response) {
        // if the response contains any errors, replace the form
        if ($(response).find('.has-error').length) {
          $form.replaceWith(response);
        } else {
          // in this case we can actually replace the form
          // with the response as well, unless we want to 
          // show the success message a different way
        }
      }
  });
})
}(jQuery));

Encore une fois, ceci est juste un exemple de base. Comme mentionné dans les commentaires ci-dessus, il n'y a pas de bonne ou de mauvaise façon de procéder. Ce n’est pas exactement ma solution préférée non plus, il ya certainement quelques modifications à apporter, mais l’idée générale est là. 

NOTE: Il y a aussi une faille dans mon code JS. Si vous remplacez le formulaire par celui de la réponse, le gestionnaire d'envoi de formulaire ne sera pas appliqué au formulaire nouvellement remplacé. Vous devrez vous assurer de réinitialiser correctement le gestionnaire de formulaire après avoir remplacé le formulaire, si vous suivez cette route.

20
yorgo

Très peu de documentation à ce sujet, mais si vous connaissez déjà Web Flow, vous n’avez peut-être pas besoin de plus. Je ne suis pas sûr de savoir comment cette technique fonctionne avec la liaison de haricots normale dans Thymeleaf. J'aimerais voir une application de démonstration complète pour animaux de compagnie utiliser cette application pour que je puisse voir les contrôleurs. 

docs

0
bwfrieds

Je ne sais pas si cela pourrait être considéré comme une pratique exemplaire, mais voici ce que j'ai fait:

Map<String, String> errorMap = binding.getFieldErrors()
   .stream().collect(Collectors.toMap(
        e -> e.getField(), e -> messageSource.getMessage(e, locale)));

Ensuite, j'ai renvoyé la carte dans la réponse ajax à traiter dans la section "succès".

0
diafragment