Thymeleaf中th:inline="javascript"的问题

15

我不知道如何解决以下问题:我想让我的模型根据一些逻辑生成真正的JavaScript代码。

这段最终的JavaScript代码应该添加到我的HTML页面的$(document).ready { }部分中。

问题是:如果我使用inline="javascript",那么由于我的getter是一个字符串,所以代码会被引用(这是Thymeleaf文档中提到的,但这不是我需要的)。

如果我使用inline="text",它不会被引用,但是所有引号都被转义了(也不错,但无法使用)。

如果我尝试inline="none",什么也不会发生。

以下是示例:

我的模型getter创建了以下JavaScript代码。

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')";

}

那么如果我现在尝试使用 inline="javascript"

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

它将被呈现为

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

这并没有帮助,因为它只是一个字符串字面量而已(这是Thymeleaf的处理方式)。

所以如果我尝试使用 inline="text" 代替呢?


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

逃脱引号的内容。

inline="none" 我并不真正理解,因为它什么也不做。

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

说实话,我不知道如何解决这个问题,希望有人能够处理好它。

提前致以感谢, 问候, 约翰


1
你总是可以使用 eval(),虽然这绝对不是最好的解决方案。 - Kejml
嗨Kejml,感谢你的快速帮助。确实eval()函数起作用了;-)真是个肮脏的hack。我不得不谷歌一下eval()函数,因为我绝对记不清楚了,但最终,我的inline="javascript"eval([[${pageHelper.documentReady}]])结合使用真的很好用。虽然我想暂时保留这个问题未回答,看看是否有"官方"的Thymeleaf方法来解决这个问题。无论如何,再次感谢你。干杯。John。 - John B.
当然,我认为这不是正确的做法,这就是为什么我将其作为评论而不是答案发布的原因,但至少它可以让你开始。希望有人能提供正确的答案。干杯 :) - Kejml
我认为问题在于您定义脚本的方式。如果您将实际的方法调用放入脚本中,并在服务器端生成输入,那么效果会好得多。就像这样:PhotoGallery.load(8,5,3,2).loadTheme(/[[${pageHelper.inputs}]]/) - Hossein
这是因为Thymeleaf以你使用的方式生成javascript的值。所以要么使用eval函数,要么改变你的方法,采用类似我说的方法。 - Hossein
嗨Hossein。我和你在一起。这背后的想法有些不同。根据我数据库中的信息,我知道实际的Thymeleaf模板需要哪些JavaScript。基于此,我只想创建所需的JS代码,以保留真正运行时需要的代码。因此,整个“PhotoGallery.load(8,5,3,2).loadTheme(name ='basic')”部分是动态的。如果不需要,它就是空的。还有更多类似的部分。但我把它们省略了,以免过于复杂。它是某种动态包含。 - John B.
2个回答

11

我会改变方法。

Thymeleaf可以轻松地让您在模板中添加模型变量以供JavaScript使用。在我的实现中,我通常将这些变量放在关闭页头标签之前的某个位置,以确保它们在JS加载后出现在页面上。

当然,我会让模板决定要加载的内容。如果您要显示画廊,则按照您的方式呈现它,并使用数据属性来定义与一些JS代码相关联的画廊。然后编写一个不错的jQuery插件来处理您的画廊。

以下是一个相对基本的示例:

默认布局修饰器: 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>

需要注意的是,这里包括了通用页脚脚本以及定义了 layout:fragment 的 div。我们将使用此布局 div 来包含所需的 jQuery 插件以用于相册。

带有常规脚本的文件: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>
在脚本文件中,有两个片段是从装饰器中包含的。页头片段包括JS层的有用上下文路径以及一个defaultTheme。然后定义并从我们的模型分配了一个画廊对象。页脚片段加载jQuery库和一个主站点JS文件,为了这个示例而再次使用。
带有惰性加载画廊的页面: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>

我们的产品页面信息比较少。使用默认的装饰器,该页面会覆盖头部中的页面标题。我们的内容片段包括在一个h1标签中的标题和一个带有data-gallery属性的空的div。这个属性是我们将在jQuery插件中使用来初始化图库的属性。

该值设置为lazyload,因此我们的插件知道需要在某个地方设置的变量中查找图像ID。如果我们的插件只支持懒加载图库,则此值可以很容易地为空。

因此,布局加载一些默认脚本,并通过巧妙放置layout:fragments,允许网站的某些部分独立于其他部分加载库。

下面是一个与我们的应用程序一起使用的基本Spring控制器示例: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";
  }
}

为了简化我们的示例,Gallery类被内联到products方法中。这可以很容易地成为返回标识符数组或任何所需内容的服务或存储库。

我们创建的jQuery插件可能如下所示: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));

产品页面的最终呈现将如下所示:

<!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>

正如您所看到的,Thymeleaf 在将您的模型转换为有效的 JS 方面做得非常出色,并在需要时添加引号并进行转义。一旦页面完成渲染,并在文件末尾使用 jQuery 插件后,初始化工作就应该加载和准备就绪了。

这不是一个完美的例子,但我认为这是一个相当直观的 Web 应用程序设计模式。


嗨Yorgo,感谢你的精彩解释。看来我确实需要像你建议的那样重构我的方法。在Kejml和Hossein也指出之后,我已经怀疑了。所以感谢大家。祝好,John - John B.
我本来以为在HTML中双引号前面不需要加反斜杠。为什么Thymeleaf会转义双引号呢,这并不是必须的。 - T3rm1
很好的问题T3rm1。我不记得我是如何得到最终输出的...很可能是查看源代码,复制/粘贴。在我包含的示例控制器中,我已经对它们进行了转义,所以我认为这只是Thymeleaf从原始字符串中保留下来的。 - yorgo

-4

使用 ${pageHelper.documentReady} 而不是 ${pageHelper.documentReady}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接