客户端/服务器模板系统允许在不{{#open_tag}}和{{#close_tag}}的情况下包装内容?

时间:2012-04-19 08:41:45

标签: javascript templates mustache handlebars.js

我的Web应用程序目前正在使用Tapestry 5.2.6。我想写一个具有以下要求的新功能:

  • 用户可以点击图库中的项目,查看显示详细视图的灯箱,包括描述,评论,卖家控件(如果他们拥有正确的凭据)以及无需离开页面即可购买。
  • 应更新网址以反映其是否在图库视图或详细信息视图中。
  • 在支持HTML5 pushState的浏览器中,URL更改应该是动态的。旧版浏览器可以接受整页刷新。
  • 图库页面和详细信息页面都必须是可抓取的 - 没有Javascript的用户应该看到完全标记的页面。
  • 速度 - 需要比我所知的Tapestry快得多。

我的计划是选择并实施一种模板语言,可以在服务器或客户端上进行同等评估。对于初始页面加载,我可以在服务器上呈现模板。对于后续更新,我可以将项目的viewmodel对象作为JSON传递给客户端,并在那里评估模板。

到目前为止一切顺利。问题在于,我所看到的模板语言都没有足够强大,能够为未来的发展做好准备。作为案例研究,请考虑以下因素:

  • 霍根
  • 车把

似乎没有能力像这样进行“包装”转换:

# base template
{>widget}
    <span class="content">Hello world</span>
{/widget}

# widget template
<div class="widget">
    {>widget_body/}
</div>

# rendered output
<div class="widget">
    <span class="content">Hello world</span>
</div>

请注意,包装的内容是从基本模板中获取的,并且小部件模板的输出在两侧都包围它。我知道用上述语言实现这一目标的唯一方法就是模板:

{>open_widget/}
    {>widget_body/}
{>close_widget/}

这意味着每个组件的两个模板,一个开启器和一个更接近,都包含未关闭的标签。 (公平地说,灰尘可以使用块和内联部分来优雅地执行此操作,但由于内联部分是模板的全局部分,因此每个模板仅限一次使用窗口小部件。)

我对模板的疑问是:

  • 我知道像LinkedIn和Twitter这样的行业领导者正在使用这些技术并做得很好。我问得太多了吗?如果您使用过其中一个,那么您是如何处理“包装问题”的?
  • 我调查过的一些解决方案似乎确实支持它:jquery-tmpl,不再正式维护;下划线和ejs,这使我对其嵌入式代码的长期解决方案感到紧张;和关闭模板。令我惊讶的是,Closure看起来对我来说是最好的!如果您使用过这些中的任何一项,您的发现是什么?

3 个答案:

答案 0 :(得分:1)

我不确定那里是否有任何你正在谈论的内容。我需要类似的东西,并认为编写一个简单的文本替换脚本比比较现有的解决方案和学习使用它更快。

这个脚本不是生产就绪的(应该进行更多测试,API很奇怪),但它应该让你知道一种方法。

以下是它的设置方式:


在文档中存储模板

模板文本存储在脚本标记中,其type属性不是“text / javascript”。每个模板都有一个唯一的id属性。

<script id="some_template" type="text/plain">

    a valid template

</script>

浏览器不应该呈现这些。允许使用任何字符(</script>除外),并且不需要转义任何内容。


<强>占位符

占位符如下所示:{@some_identifier}

<script id="image_template" type="text/plain">

    <a href="{@img_url}"><img src="{@img_url}"></a>

</script>

每个占位符都将替换为:

  • 从另一个模板传入的值
  • 获取模板副本时传递给JavaScript函数的参数,或
  • 如果未找到替换值,则为空字符串。

在另一个

中包含一个模板

@@“pseudotag”包含当前模板中另一个模板的内容。

<script id="photo_template" type="text/plain">

    <@@ image_template></@@>

    <div class="photo-caption">{@caption}</div>

</script>

photo_template包括image_template。所有包含替换都在替换占位符之前发生,因此photo_template具有{@img_url}{@caption}个占位符。


包含替换占位符

这就是“包装”的来源。理想情况下,占位符几乎总是被其他模板中的内容替换,而不是在获取模板副本时传入的值。

<script id="missing_photo_template" type="text/plain">

    <@@ photo_template>
        <@ img_url>notfound.png</@>
    </@@>

</script>

missing_photo_template包含photo_template,为{@img_url}提供替换,因此missing_photo_template只有{@caption}占位符。


<强>的JavaScript

API现在很糟糕,但主要名称空间at基本上有两个函数txtnode。第一个获取模板的副本作为文本,第二个获取副本作为元素(这意味着它应该有一个根节点,与上面的一些示例不同)。

这是:

/**

    Atrocious Templates

*/
var at = (function(){

  var rTemplate = /<@@\s*(.*?)>((?:[\w\W](?!<@@))*?)<\/@@>/gm,
      rOption = /<@\s*(.*?)>([\w\W]*?)<\/@>/gm,
      rValue = /\{@(.*?)\}/g,
      rTag = /<(\w+)/i,
      rSpace = /\s+/,
      templates = {},
      doc = document.implementation.createHTMLDocument('');

  /** 

      Inlcude inner templates.

      @private

      @param {string} m0
          The full inclusion text.

      @param {string} m1
          The ID of the included template.

      @param {string} m2
          Values passed to included template.

      @return {string} 

  */
  function includeTemplates(m0, m1, m2) {
    var opts = {};
    m2.replace(rOption, function(m0, m1, m2) { opts[m1] = m2; });
    return txt(m1, opts, true);
  }

  /** 

      Get text contents of a template.

      @private

      @param {string} id
          The ID of the template.

      @return {string} 

  */
  function get(id) {
    if (templates[id]) return templates[id];
    var last, t = document.getElementById(id).innerHTML;
    while (last != t && (last = t)) t = t.replace(rTemplate, includeTemplates);
    return (templates[id] = t);
  }

  /** 

      Get a text copy of a template.

      @param {string} id
          The ID of the template.

      @param {Object.<string|function ():string>} options
          Properties of this object will replace placeholder tokens.
          Each property can be either a string, or a function which 
          returns a string.

      @param {boolean=} noStrip
          By default, placeholders for which no replacement text is 
          found are removed. Setting this to `true` overrides that
          behavior, leaving non-replaced placeholders intact. 

      @return {string} 

  */
  function txt(id, options, noStrip) {
    if (!options) options = {};
    return get(id).replace(rValue, function(m0, m1) {
      var v = options[m1];
      return noStrip && !v && m0 || v && (v.call ? v() : v) || '';
    });
  }

  /** 

      Get a node copy of a template.

      @param {string} id
          The ID of the template.

      @param {Object.<string|function ():string>} options
          Properties of this object will replace placeholder tokens.

      @return {string} 

  */
  function node(id, options) {
    var text = txt(id, options),
        root = text.match(rTag)[1];
    doc.open; doc.write(text); doc.close();
    return doc.getElementsByTagName(root)[0];
  }

  // exports

  return { txt: txt, node: node };

}());

同样,我不建议你在生产中使用它,因为它没有经过多少测试(虽然看起来工作正常),但希望这会给你一些关于你想要的东西的想法。< / p>

答案 1 :(得分:0)

尝试查看handlebarjs.com - 我在服务器和客户端都使用它。

答案 2 :(得分:-1)

也许这个问题已经过时了,但是Mustache不支持 partials 吗?

http://mustache.github.io/mustache.5.html

  

...通过这种方式,您可能希望将partials视为包含或模板   扩张,即使它不是真的。

     

例如,此模板和部分:

     

<强> base.mustache

<h2>Names</h2> {{#names}}   {{> user}} {{/names}}
     

<强> user.mustache

<strong>{{name}}</strong>
     

可以被认为是一个单一的扩展模板:

<h2>Names</h2> {{#names}}   <strong>{{name}}</strong> {{/names}}