Underscore.js 嵌套模板

47

是否可以从Underscore模板中获取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>

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

我是否可以这样做:

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

顺带一提,我是使用jQuery的$.get方法从外部文件加载模板的。这样我将得到整个document-template字符串。然后,我只需要执行documentTemplate = _.template(loadedString);

现在,如果我可以从字符串中提取#summary元素,那么它应该可以工作。但是当我尝试将字符串转换为DOM元素(var domElement = $(loadedString))时,就无法再识别<%= %>标记了。


我强烈建议将下划线转换为pure - PDA
3
安东尼,为什么会这样? - Hanna
5个回答

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

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});

6

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)。但是这种方法有效,谢谢。 - Emmanual

2

不要一次性将所有内容作为一个模板加载,然后尝试稍后再次加载其中的一部分,而是将它们分成多个模板。

有一个主文档模板和一个摘要模板。初始加载将文档模板拉入,然后将摘要模板插入到第一个模板编译的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中后,通过underscore加载第二个模板,如下所示:

<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")。

您可以使用相同的策略来处理items,只需确保使用正确的jQuery选择器/方法来覆盖正确顺序中的现有divs,因为append仅适用于将内容添加到元素末尾。


1
我知道我晚了3年才加入这个对话,但是这里的任何答案都无法适用于我最新项目中遇到的情况。由于最初想法最终可行,我认为我应该发布它,以防其他人想要嵌套underscore.js模板,正如这个问题的标题所述。
我的要求基本上是:
1) 如果模板中有动态部分,则应能够单独重新呈现它们--而不需要使用更新后的数据重新呈现整个模板。 2) 动态部分不应该被分解成与父模板同级的自己的模板;相反,应该有一种模板层次结构--一个声明其所有子模板的模板。
Underscore.js中嵌套模板的概念与PHP(例如)中的相同。以下代码是“echoing 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)把嵌套的 Underscore 语句转换成字符串。

2)插入该字符串 (<%= "string here" %>)。

3)转义嵌套 Underscore 语句中的闭合标签 (\>)。这样可以防止 Underscore 的正则表达式将其与步骤 2 中的开放标签 (<%=) 匹配。当这个字符串被回显时,转义字符将被移除,留下%>,准备在下一个步骤中由 Underscore 解释。

在我们的示例中,第二个 NOTE 中执行相同的操作可以防止浏览器结束第一个 script 标签。嵌套的 script 标签在技术上不允许,因此浏览器会查找第一个字符序列:</script> 然后结束脚本执行。这将导致浏览器无法识别此序列。

有关更详细的示例,请参见 JSFiddle


免责声明

我不认为这是做这种事情的最佳方式。毕竟,这非常hacky。虽然@John z的答案违反了我的第一个要求,但对我来说并不是一个选择,将动态部分在模板中分离成它们自己的模板可能更容易处理,只是不够有组织。

在DOM中所有这些嵌套的


0

@bowheart

你可以使用模板字面量,并将它们导出为模块,就像常规的JS模块一样。

    export default const templates = {
      container: 
`<div id="document">
    <h1><%= name %></h1>
    <ul class="items">
        <% _.each(items, function(item) { %> 
            <li><%= item %></li>
        <% }); %>
    </ul>
</div>`,
      item: `<div id="summary"><p>Total items: <%= totalitems %></p></div>`,
    }


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