在underscore.js模板引擎中运行模板(递归)

16

我正在使用backbone.js和underscore.js构建一个JavaScript应用程序。由于阅读和尝试在模板中嵌套另一个模板,就像下面这样的操作几个小时了,变得越来越令人沮丧。

我的模板使用内置的underscore.js模板引擎:

<script id="navigation_template" type="text/template">
  <div><%= title %>
      <% _.each(children, function(child) { %>
          <% render_this_template_recursively(child) %>
      <% }); %>
  </div>
</script>
我想要为每个子元素渲染这个模板( render_this_template_recursively(child) )。
我该如何实现?
谢谢。

1
嘿,这是一个非常有趣的问题...你尝试下面的答案了吗?;) - Anton Kraievyi
1
嗨,亚当,你解决了它吗?还是你使用了其他东西? - jamie-wilson
1
@jamie-wilson,我从Ashish Datta的回答中得到了一些启示,现在有东西可以工作了。我在下面发布了一个简约的例子。 - Ates Goral
5个回答

35

我个人没有尝试过这个方法,但是 _.template 会返回一个函数(为了强调,我把它命名为 templateFn),你可以将它作为参数传递给模板,如下所示:

var templateFn = _.template($('#navigation_template').html());

$(this.el).html(templateFn({model: this.model, templateFn: templateFn}));

注意我正在传递整个模型(假定您的模型具有一个children属性,该属性本身是backbone模型的集合),您的模板将更改为:

<script id="navigation_template" type="text/template">
  <div><%= model.escape('title') %>
      <% _.each(model.children, function(child) { %>
          <%= templateFn(child, templateFn) %>
      <% }); %>
  </div>
</script>

祝你好运。希望这对你有用。


@timDunham 如果我正在使用文本插件来加载模板,那我该怎么办?我应该这样做:templateFn = _.template($('#navigation_template').html()); - Priya
<%= templateFn(child, templateFn) %> 对我来说无法工作。但 <%= templateFn({model: child, templateFn: templateFn}) %> 完美运行。 - Londeren

8
我刚刚成功地尝试了这个方法。我使用的是纯的UnderscoreJS,没有用到BackboneJS,但实际上这不重要。
以下是代码:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript" src="underscore.js"></script>    
<style>

.container {
  position: relative;
  margin-bottom: 20px;
}

#container {
  position: relative;
  margin: auto;
}

.fib-box {
  position: absolute;
  top: 0px;
  left: 0px;
  background: rgba(0,68,242,0.15);
  border: 1px solid rgba(0,0,0,0.20); 
}

.header {
  padding-bottom: 10px;
}

</style>
  </head>
  <body> 

    <div class="header">
        <h3>Render Fibonacci With UnderscoreJS</h3>
        <form id="updateCount">
            <input type="text" value="13" id="fibSequence" />
            <input type="submit" value="Update" />
        </form>
    </div>

    <div class="container">
    <div id="container">

    </div>    
    </div>

<script type="text/template" id="template">
<% if(depth){ %>
<div class='fib-box' data-depth='<%= depth %>' style='width: <%= val %>px; height: <%= val %>px;'></div>
<% print(template(getFibObj(depth-1))) %>
<% } %>
</script>

<script type="text/javascript">

var template;

$(document).ready(function(){

    template = _.template($("#template").text());

    $("#updateCount").submit( function(){

        $("#container").html( template( getFibObj($("#fibSequence").val()) ) );

        var width = $("#container .fib-box:first").css("width");
        $("#container").css( {width: width, 'min-height': width} );

        return false;
    });

    $("#updateCount").submit();
});

function getFibObj(i){
    return {depth: i, val: fib(i)};
}

function fib(i){
    return ( i == 0 || i == 1 ) ? i : fib(i-1) + fib(i-2);
}

 </script>

  </body>
</html>

@Ashish Datta,是否可以将此方法与ViewModel之类的东西一起使用?例如,在ViewModel中进行数据处理,然后将其传递给每个递归的模板。 - Priya
@JSBin 我不确定我完全理解了,但你肯定可以在控制器中构建HTML块,然后将所有内容传递到模板中。 - Ashish Datta

6

我尝试使用timDunham和Ates Goral提供的示例,但它对我没有起作用,所以我进行了一些小的升级。请查看以下内容。

视图:

    template: _.template($("#tree").html()),

    render: function () {
        this.$el.html(this.template({
            options: this.collection.toJSON(),
            templateFn: this.template
        }));
    }

和模板:

<script type="text/template" id="tree">
<ul>
    <% _.each(options, function (node) { %>
        <li><%= node.title %></li>
        <% if (node.children) { %>
            <%= templateFn({ options: node.children, templateFn: templateFn }) %>
        <% } %>
    <% }); %>
</ul>

对我来说,它的表现相当不错。正如您所看到的那样,主要区别在于将配置对象传递到templateFn中,而不是参数。希望您会觉得它有用。


能否使用这种方法来处理视图模型这样的东西?就像数据处理应该在视图模型中完成,然后将其传递到模板中进行递归处理。 - Priya

1

递归模板可以长成这样:

<ul>
    <% entries.forEach(function (entry) { %>
    <li>
        <%= entry.title %>
        <%
        if (entry.children) {
            print(tmpl({
                entries: entry.children,
                tmpl: tmpl
            }));
        }
        %>
    </li>
    <% }); %>
</ul>

首先,预编译您的模板:

entriesTmpl = _.template(entriesTmpl);

然后在调用时同时传递数据和模板本身:

$el.html(entriesTmpl({
    entries: entryTree,
    tmpl: entriesTmpl
});

1

最近我使用backbone-relational实现了这个功能。如果您想查看工作解决方案,我创建了一个JSFiddle,可能会有所帮助:http://jsfiddle.net/blaisco/ADKrK/

下面是相关代码:

var FolderView = Backbone.View.extend({
    el: $("#main"),

    template: _.template($("#folder-tmpl").html()),

    render: function () {
        this.$el.html(this.template({
            "folder": parentFolder.toJSON(),
            "templateFn": this.template
        }));
        return this;
    }
});

这是HTML模板:

<ul id="main"></ul>

<script type="text/template" id="folder-tmpl">
    <li>
        <a href="#" class="folder"><%= folder.title %></a>
        <% if(folder.children.length) { %><ul><% } %>
        <% _.each(folder.children, function(child) { %>
            <%= templateFn({"folder": child, "templateFn": templateFn}) %>
        <% }); %>
        <% if(folder.children.length) { %></ul><% } %>
    </li>
</script>

能否使用这种方法来处理 ViewModel 类型的东西?比如说,数据处理应该在 ViewModel 中完成,然后将其传递到每个递归的模板中。 - Priya

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