【问题标题】:underscore.js nested templatesunderscore.js 嵌套模板
【发布时间】:2012-02-14 20:48:17
【问题描述】:

是否有可能以某种方式从下划线模板中获取 DOM 元素并将其用作另一个模板?

这个想法是我的应用程序需要呈现一个文档,该文档包含一个带有项目和摘要的循环。我需要偶尔只重新渲染摘要或几个项目,所以我不能只重新渲染整个文档。

但是,我希望应用用户可以轻松地为文档创建自己的模板,并且我认为将所有内容保存在一个文档中会更容易。

我正在尝试使用这样的东西:

<script type="text/template" id="document-template">
    <div id="document">
        <h1><%= name %></h1>
        <ul class="items">
            <% _.each(items, function(item) { %> 
                <li><%= item %></li>
            <% }); %>
        </ul>
        <div id="summary">
            <p>Total items: <%= totalitems %></p>
        </div>
    </div>
</script>

现在,我可以轻松地执行此 var documentTemplate = _.template($('#document-template').html()); 将其转换为文档模板,但我想将摘要部分转换为模板,并将列表项转换为模板。

我可以这样做吗:

var summaryTemplate = _.template($('#document-template #summary').html());
var itemTemplate = _.template($('#document-template .items li').html());

PS。实际上,我正在使用 jQuery 的 $.get 从外部文件加载模板。这样,我将在一个大字符串中获取文档模板。从那里,我可以做到documentTemplate = _.template(loadedString);

现在,如果我可以从字符串中提取#summary 元素,它应该可以工作。但是当我尝试将字符串转换为 DOM 元素时(var domElement = $(loadedString)) (所以我可以这样做:summaryTemplate = _.template($('#summary',domElement).html());,它不起作用,因为下划线将不再识别 标记。

【问题讨论】:

  • 我强烈建议将下划线转换为pure
  • 安东尼,这是为什么呢?

标签: templates underscore.js


【解决方案1】:

我知道我迟到了 3 年,但这些答案都不适用于我在最新项目中遇到的情况。而且由于我最初考虑这样做的方式最终奏效了,我想我应该发布它,以防其他人想要真正嵌套 underscore.js 模板,正如这个问题的标题所述。

我的要求基本上是:

1) 如果模板中有动态片段,它们应该能够单独重新渲染自己——而不需要使用更新的数据重新渲染整个模板。

2) 动态片段不应该被拆分成与父模板处于同一级别的自己的模板;相反,应该有一种模板层次结构——一个模板声明了它自己所需的所有子模板。

Underscore.js 中嵌套模板的概念与 PHP 中的相同(例如)。下面的代码是一个“回显PHP”的例子:

<?php echo '<?php echo $var_unknown_until_runtime; ?>'; ?>

虽然这种嵌套语法可能会变得非常复杂和令人困惑,但想法很简单:

稍后将回显值的回显代码。

这也适用于 Underscore.js 模板。


示例

代码将如下所示:

<script type="text/template" id="outer-tpl">
    <%= value %>

    <script type="text/template" id="inner-tpl">
        <%= '<%= value %\>' %> <!-- NOTE how the inner '>' is escaped ('\>') -->
    <%= "</script\>" %> <!-- NOTE same thing -->
</script>

让我们稍微分解一下。第一个NOTE 的行是我们这里的关键行。通常当Underscore 编译这个模板时,你会期望value 的值在外部和内部模板中都被打印出来。我们采取了一些措施来防止这种情况发生:

1) 将嵌套的下划线语句转为字符串。

2) 插入该字符串 (&lt;%= "string here" %&gt;)。

3) 转义嵌套下划线语句 (\&gt;) 中的结尾 &gt;。这可以防止 Underscore 的正则表达式将其与步骤 2 中的开始标记 (&lt;%=) 匹配。当这个字符串被回显时,转义字符将被删除,留下%&gt;,准备在下一次传递时被下划线解释。

在我们的示例中,在第二个NOTE 中执行相同的操作会阻止浏览器结束第一个script 标记。嵌套script 标记在技术上是不允许的,因此浏览器会查找第一个字符序列:&lt;/script&gt;,然后结束脚本执行。这会阻止浏览器识别此序列。

有关更深入的示例,请参阅JSFiddle


免责声明

我不认为这是做这类事情的最佳方式。毕竟,它非常hacky。虽然@John z 的回答打破了我的第一个要求,这对我来说不是一个选择,但将模板中的动态部分拆分成自己的模板的另一种选择可能更容易处理。它只是不太有条理。

DOM 中所有这些嵌套的script 标签的位置很难跟踪(因为无论何时何地插入外部模板,嵌套的script 标签都会动态注入到DOM 中)。重复的风险很高,而且维护起来很烦人。

嵌套模板的难度随着嵌套的每一层而急剧增加(要将模板嵌套在嵌套模板中,您必须再次转义所有内容,等等...)。小提琴包括一个这样的例子。

【讨论】:

    【解决方案2】:

    Playground

    /////// TEMPLATES //////
    
        var mainTemplate = "<ul> \
                              <% _.each(items, function(item) { %> \
                                   <%= listItem({item:item}) %> \
                              <% }); %> \
                            <ul>";
        
        var subTemplate = '<li><%=item %></li>';
    
    
    /////// MODEL (our data) //////
    
        var model = {
            items : [1,2,3,4,5]
        }
    
        
    /////// COMPILE //////
    
        // add the subTemplate to the model data, to be passed to the mainTemplate
        model.listItem = _.template(subTemplate);
        
        // Render main template to the DOM
        document.body.innerHTML = _.template(mainTemplate, model);
    

    【讨论】:

    • 对于下划线版本 1.8.3,我们必须再次传递模型。即:modelssdd(model)。但是这个方法有效,谢谢。
    【解决方案3】:

    您可以将嵌套模板作为模板赋值中的变量传递给主模板,例如:

    HTML:

    <script type="text/template" id="sub_template">
      <article>
        <h1>id: <%= id %><h1>
      </article>
    </script>
    
    <script type="text/template" id="main_template">
      <% for (var i = 0; i < num; i++) { %>
        <%= renderSub({id:i}) %>
      <% } %>
    </script>
    

    JS:

     var renderSub = _.template( $('#sub_template').remove().text() ),
         renderMain = _.template( $('#main_template').remove().text() );
    
      renderMain({num:5, renderSub:renderSub});
    

    【讨论】:

      【解决方案4】:

      与其将它们作为一个模板一次性加载,然后尝试稍后再次加载其中的一部分,不如将它们分开为多个模板。

      拥有您的主要文档模板和摘要模板。初始加载拉入文档模板,然后拉入摘要模板并将其插入到从第一个模板编译的 DOM 中。然后您可以随时重新加载第二个模板。请参阅以下步骤:

      1) 加载初始模板,如下图:

      <script type="text/template" id="document-template">
          <div id="document">
              <h1><%= name %></h1>
              <ul class="items">
                  <% _.each(items, function(item) { %> 
                      <li><%= item %></li>
                  <% }); %>
              </ul>
          </div>
      </script>
      

      2) 编译到 DOM 后,通过下划线加载第二个模板,如下所示:

      <script type="text/template" id="summary-template">
          <div id="summary">
              <p>Total items: <%= totalitems %></p>
          </div>
      </script>
      

      3) 调用 $("#document").append(summaryTemplate) 或任何保存已编译模板的变量。

      4) 任何时候需要重新加载摘要时都重复第 2 步和第 3 步,除了在再次追加之前先删除现有的 $("#summary")

      您也可以对项目使用相同的策略,只需确保使用正确的 jQuery 选择器/方法,以便以正确的顺序覆盖现有的 div,因为 append 仅适用于向元素的结尾。

      【讨论】:

        猜你喜欢
        • 2014-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-20
        相关资源
        最近更新 更多