问题 Thymeleaf th:inline =“javascript”问题


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

然后应该在里面添加最后一段javascript代码 $(document).ready {} 我的HTML页面的一部分。

问题是:如果我使用inline =“javascript”,代码会被引用,因为我的getter是一个字符串(这是在Thymeleaf doc中提到的,但它不是我需要的东西;-)

如果我使用inline =“text”in没有引用但是所有引号都被转义;-) - 也很好但是无法使用8)

如果我尝试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')";

}

所以,如果我现在尝试 直列= “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处理它的方式)。

所以,如果我尝试 内嵌=“文本” 代替

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

哪个逃脱了报价。

直列=“无” 我真的不明白,因为它什么也没做

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

说实话,我不知道如何解决这个问题,希望那里的任何人都知道如何处理这个问题。

提前谢谢了 干杯 约翰


3663
2017-09-15 22:16


起源

那么你可以随时使用 eval()虽然它绝对不是最漂亮的解决方案。 - Kejml
嗨Kejml。谢谢你的快速帮助。确实eval()有效;-)多么糟糕的黑客。不得不谷歌eval(),因为我绝对不能真正记住,但最后,我的 inline="javascript" 和这个结合 eval([[${pageHelper.documentReady}]]) 真的很好。虽然我想暂时保留这个问题,但是如果有一种“官方”的Thymeleaf方式可以做到这一点。无论如何。非常感谢。干杯。约翰。 - John B.
当然,我不认为这是正确的做法,这就是为什么我把它作为评论发布,而不是作为答案,但它至少可以让你去。希望有人能得到正确答案。干杯:) - Kejml
我认为问题在于您定义脚本的方式。如果将实际的方法调用放在脚本中并在服务器sid中生成输入,那会好得多。像这样:PhotoGallery.load(8,5,3,2).loadTheme(/ * [[$ {pageHelper.inputs}]] * /) - Hossein
那是因为Thymeleaf以你使用它的方式为javascript生成值。所以要么使用eval函数,要么改变你的approche就像我说的那样 - Hossein


答案:


我会改变方法。 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>

这里要注意的是包含通用页脚脚本,然后是布局:片段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>

在脚本文件中,有2个片段,它们包含在装饰器中。在标题片段中,JS层包含一个有用的上下文路径,以及一个仅用于它的默认主题。然后定义一个gallery对象并从我们的模型中分配。为了这个例子的目的,页脚片段加载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标签中的标题和带有数据库属性的空div。我们将在jQuery插件中使用此属性来初始化库。该值设置为lazyload,因此我们的插件知道我们需要在某处找到某个变量集中的图像ID。如果插件支持的唯一内容是延迟加载的库,那么这很容易变空。

因此,布局加载了一些默认脚本,并且使用巧妙放置的布局:片段,您允许站点的某些部分独立于其余部分加载库。

这是一个基本的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类在产品方法中内联,以简化我们的示例。这可以很容易地成为某种类型的服务或存储库,它返回一系列标识符,或者您需要的任何内容。

我们创建的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应用程序设计模式。


10
2017-09-17 21:14



嗨Yorgo,谢谢你的解释。看来我真的必须像你提议的那样重构我的方法。在Kejml和Hossein表示之后我已经怀疑它了。所以感谢大家。干杯约翰 - John B.
我原以为html中的双引号之前没有反斜杠。为什么Thymeleaf在没有真正需要时会逃脱双重报价。 - T3rm1
好问题T3rm1。我不记得我是如何得到最终输出的...很可能是一个视图源,复制/粘贴。我确实让它们在我包含的样本控制器中逃脱了,所以我认为它只是Thymeleaf从原始字符串中保留它。 - yorgo