16

I am using backbone.js and underscore.js to build an javascript application. Since hours of reading and trying to run a template within a template like below, it is getting more and more frustrating.

My template using the build in underscore.js template engine:

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

I would like to render this template for every child element ( render_this_template_recursively(child) ).

How can I do this?

Thanks

5 Answers5

35

I've not personally tried this but _.template returns a function (I've named it templateFn to emphasize that), so you could pass it into the template like this:

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

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

Notice that i'm passing in the whole model (assuming that your model has a children property which is itself a collection of backbone models) and your template would be changed to:

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

Good luck. I hope this works for you

Zain Shaikh
  • 6,013
  • 6
  • 41
  • 66
timDunham
  • 3,318
  • 23
  • 24
  • @timDunham what should I do if i am using text plugin for loading templates. should i do templateFn = _.template($('#navigation_template').html()); – Priya Mar 26 '15 at 04:03
  • `<%= templateFn(child, templateFn) %>` not working for me. But works perfect `<%= templateFn({model: child, templateFn: templateFn}) %>` – Londeren Sep 10 '15 at 08:02
8

I just successfully tried this. I tested it with only pure UnderscoreJS, no BackboneJS but functionally it shouldn't matter.

Here is the code:

<!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>
Hanna
  • 10,315
  • 11
  • 56
  • 89
Ashish Datta
  • 371
  • 1
  • 4
  • 12
  • @Ashish Datta is it possible to use this approach with viewmodel kind of thing. like data processing should be done in viewmodel and then pass it to template for each recursion – Priya Mar 26 '15 at 12:43
  • @JSBin not sure I follow 100% but you could definitely build up the HTML chunks in your controller and then just pass all of that into your template? – Ashish Datta Mar 26 '15 at 17:25
6

I've tried to use example presented by timDunham and Ates Goral, but it did not work for me, so I've made a little upgrade for it. Find it below.

view:

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

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

and 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>

And it works pretty good for me. The main difference, as you can see, is the passing configuration object into templateFn, instead of arguments. Hope you'll find it useful.

Oleksandr Tkalenko
  • 1,098
  • 11
  • 23
  • is it possible to use this approach with viewmodel kind of thing. like data processing should be done in viewmodel and then pass it to template for each recursion – Priya Mar 26 '15 at 12:42
1

A recursive template can look something like this:

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

First, pre-compile your template:

entriesTmpl = _.template(entriesTmpl);

Then call it while passing both your data and the template itself:

$el.html(entriesTmpl({
    entries: entryTree,
    tmpl: entriesTmpl
});
Ates Goral
  • 137,716
  • 26
  • 137
  • 190
1

I implemented this recently using backbone-relational. I created a fiddle that I thought might be helpful if you want to see a working solution: http://jsfiddle.net/blaisco/ADKrK/

I've posted the relevant bits below.

The view:

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

The html/template:

<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>
  • is it possible to use this approach with viewmodel kind of thing. like data processing should be done in viewmodel and then pass it to template for each recursion – Priya Mar 26 '15 at 12:42