web-dev-qa-db-fra.com

Thymeleaf th: problème inline = "javascript"

Je ne sais pas comment résoudre le problème suivant: je voudrais laisser mon modèle générer dynamiquement du javascript réel basé sur une logique de modèle.

Ce dernier morceau de code javascript devrait alors être ajouté à l'intérieur de la partie $ (document) .ready {} de ma page html.

La chose est: si j'utilise inline = "javascript", le code est cité car mon getter est une chaîne (c'est ainsi qu'il est mentionné dans le document Thymeleaf mais ce n'est pas ce dont j'ai besoin ;-)

Si j'utilise inline = "text" in n'est pas cité mais toutes les citations sont échappées à la place ;-) - aussi bien mais inutilisable 8)

Si j'essaie inline = "none", rien ne se passe.

Voici les exemples

Mon getter modèle a créé le code Javascript suivant.

classe PageHelper

public String documentReady() {

// do some database operations to get the numbers 8,5,3,2
return "PhotoGallery.load(8,5,3,2).loadTheme(name='basic')";

}

Donc, si j'essaye maintenant inline = "javascript"

<script th:inline="javascript">
/*<![CDATA[*/
    jQuery().ready(function(){
        /*[[${pageHelper.documentReady}]]*/
    });
/*]]>*/
</script>

il sera rendu à

<script>
/*<![CDATA[*/
    jQuery().ready(function(){
        'PhotoGallery.load(8,5,3,2).loadTheme(name=\'basic\')'
    });
/*]]>*/
</script>

Ce qui n'aide pas car c'est un littéral String, rien de plus (c'est ainsi que Thymeleaf le gère).

Donc, si j'essaye inline = "text" à la place

<script>
/*<![CDATA[*/
    jQuery().ready(function(){
        PhotoGallery.load(8,5,3,2).loadTheme(name=&#39;basic&#39;)
    });
/*]]>*/
</script>

Ce qui échappe aux citations.

inline = "none" Je ne comprends pas vraiment, car cela ne fait rien

<script>
/*<![CDATA[*/
    jQuery().ready(function(){
        [[${pageHelper.documentReady}]]
    });
/*]]>*/
</script>

Pour être honnête, je ne sais pas comment résoudre ce problème et j'espère que quiconque sait comment y faire face.

Un grand merci d'avance Cheers John

11
John B.

Je changerais d'approche. Thymeleaf vous permet facilement d'ajouter des variables de modèle dans vos modèles à utiliser en Javascript. Dans mes implémentations, je place généralement ces variables quelque part avant la balise d'en-tête de fermeture; pour vous assurer qu'ils sont sur la page une fois le JS chargé. Bien sûr, je laisse le modèle décider quoi charger exactement. Si vous affichez une galerie, rendez-la comme vous le feriez et utilisez des attributs de données pour définir la galerie qui se rapporte à du code JS. Ensuite, écrivez-vous un Nice plugin jQuery pour gérer votre galerie.

Un exemple relativement basique:

Décorateur de mise en page par défaut: layout/default.html

<!doctype html>
<html xmlns:layout="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org">
<head>
  <title>My Example App</title>
  <object th:remove="tag" th:include="fragments/scripts :: header" />
</head>
<body>
  <div layout:fragment="content"></div>
  <div th:remove="tag" th:replace="fragments/scripts :: footer"></div>
  <div th:remove="tag" layout:fragment="footer-scripts"></div>
</body>
</html>

La chose à noter ici est l'inclusion des scripts de pied de page génériques et ensuite une disposition: fragment div défini. Cette div de mise en page est ce que nous allons utiliser pour inclure notre plugin jQUery nécessaire pour la galerie.

Fichier avec scripts généraux: fragments/scripts.html

<div th:fragment="header" xmlns:th="http://www.thymeleaf.org">
  <script type="text/javascript" th:inline="javascript">
    /*<![CDATA[*/
    var MY_APP = {
      contextPath: /*[[@{/}]]*/,
      defaultTheme: /*[[${theme == null} ? null : ${theme}]]*/,
      gallery: {
        theme: /*[[${gallery == null} ? null : ${gallery.theme}]]*/,
        images: /*[[${gallery == null} ? null : ${gallery.images}]]*/,
        names: /*[[${gallery == null} ? null : ${gallery.names}]]*/
      }
    };
    /*]]>*/
  </script>
</div>
<div th:fragment="footer" xmlns:th="http://www.thymeleaf.org">
  <script type="text/javascript" src="/js/jquery.js"></script>
  <script type="text/javascript" src="/js/my_app.js"></script>
</div>

Dans le fichier de scripts, il y a 2 fragments, qui sont inclus à partir du décorateur. Dans le fragment d'en-tête, un chemin de contexte utile est inclus pour la couche JS, ainsi qu'un DefaultTheme juste pour l'enfer. Un objet galerie est alors défini et attribué à partir de notre modèle. Le fragment de pied de page charge à nouveau la bibliothèque jQuery et un fichier JS du site principal pour les besoins de cet exemple.

Une page avec une galerie paresseuse: products.html

<html layout:decorator="layout/default" xmlns:layout="http://www.thymeleaf.org/" xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Products Landing Page</title>
</head>
<body>
  <div layout:fragment="content">
    <h1>Products</h1>
    <div data-gallery="lazyload"></div>
  </div>
  <div th:remove="tag" layout:fragment="footer-scripts">
    <script type="text/javascript" src="/js/my_gallery.js"></script>
  </div>
</body>
</html>

Notre page produits ne contient pas grand-chose. En utilisant le décorateur par défaut, cette page remplace le titre de la page dans l'en-tête. Notre fragment de contenu comprend un titre dans une balise h1 et un div vide avec un attribut de galerie de données. Cet attribut est ce que nous utiliserons dans notre plugin jQuery pour initialiser la galerie. La valeur est définie sur lazyload, donc notre plugin sait que nous devons trouver les ID d'image dans un ensemble de variables quelque part. Cela aurait pu être facilement vide, si la seule chose que notre plugin prend en charge est une galerie chargée par lazyl.

Ainsi, la mise en page charge certains scripts par défaut et avec une mise en page intelligemment placée: des fragments, vous autorisez certaines sections du site à charger des bibliothèques indépendamment des autres.

Voici un exemple de contrôleur Spring de base, pour travailler avec notre application: MyController.Java

@Controller
public class MyController {
  @RequestMapping("/products")
  public String products(Model model) {        
    class Gallery {
      public String theme;
      public int[] images;
      public String[] names;
      public Gallery() {
        this.theme = "basic";
        this.images = new int[] {8,5,3,2};
        this.names = new String[] {"Hey", "\"there's\"", "foo", "bar"};
      }
    }
    model.addAttribute("gallery", new Gallery());
    return "products";
  }
}

La classe Gallery a été lancée en ligne dans la méthode des produits, pour simplifier notre exemple ici. Cela pourrait facilement être un service ou un référentiel d'un certain type qui renvoie un tableau d'identifiants, ou tout ce dont vous avez besoin.

Notre plugin jQuery que nous avons créé pourrait ressembler à ceci: my_gallery.js

(function($) {
  var MyGallery = function(element) {
    this.$el = $(element);
    this.type = this.$el.data('gallery');
    if (this.type == 'lazyload') {
      this.initLazyLoadedGallery();
    }
  };

  MyGallery.prototype.initLazyLoadedGallery = function() {
    // do some gallery loading magic here
    // check the variables we loaded in our header
    if (MY_APP.gallery.images.length) {
      // we have images... sweet! let's fetch them and then do something cool.
      PhotoGallery.load(MY_APP.gallery.images).loadTheme({
        name: MY_APP.gallery.theme
      });
      // or if load() requires separate params
      var imgs = MY_APP.gallery.images;
      PhotoGallery.load(imgs[0],imgs[1],imgs[2],imgs[3]).loadTheme({
        name: MY_APP.gallery.theme
      });
    }
  };

  // the plugin definition
  $.fn.myGallery = function() {
    return this.each(function() {
      if (!$.data(this, 'myGallery')) {
        $.data(this, 'myGallery', new MyGallery(this));
      }
    });
  };

  // initialize our gallery on all elements that have that data-gallery attribute
  $('[data-gallery]').myGallery();
}(jQuery));

Le rendu final de la page des produits ressemblerait à ceci:

<!doctype html>
<html>
<head>
  <title>Products Landing Page</title>
  <script type="text/javascript">
    /*<![CDATA[*/
    var MY_APP = {
      contextPath: '/',
      defaultTheme: null,
      gallery: {
        theme: 'basic',
        images: [8,5,3,2],
        names: ['Hey','\"there\'s\"','foo','bar']
      }
    };
    /*]]>*/
  </script>
</head>
<body>
  <div>
    <h1>Products</h1>
    <div data-gallery="lazyload"></div>
  </div>
  <script type="text/javascript" src="/js/jquery.js"></script>
  <script type="text/javascript" src="/js/my_app.js"></script>
  <script type="text/javascript" src="/js/my_gallery.js"></script>
</body>
</html>

Comme vous pouvez le voir, Thymeleaf fait un très bon travail de traduction de votre modèle en JS valide et ajoute en fait les guillemets si nécessaire et les échappe également. Une fois le rendu de la page terminé, avec le plugin jQuery à la fin du fichier, tout le nécessaire pour initialiser la galerie doit être chargé et prêt à fonctionner.

Ce n'est pas un exemple parfait, mais je pense que c'est un modèle de conception assez simple pour une application web.

10
yorgo