Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursion with doT.js

Assuming I have a data structure like this:

{ list: [ {
         name: "1",
         children: [{
               name: "1.1",
               children: []
            },
            {
               name: "1.2",
               children: [{
                     name: "1.2.1",
                     children: []
                  }
               ]
            }
         ]
      },
      {
         name: "2",
         children: [{
               name: "2.1",
               children: [{
                     name: "2.1.1",
                     children: []
                  },           
                  {
                     name: "2.1.2",
                     children: []
                  },               
                  {
                     name: "2.1.3",
                     children: []
                  }
               ]
            },
            {
               name:  "2.2",
               children: []
            },
            {
               name: "2.3",
               children: []
            }
         ]
      },
      {
         name: "3",
         children: [{
               name: "3.1",
               children: []
            }
         ]
      }
   ]
}

How would I go about creating a template with doT.js that will recursively go through the object and build nested lists?

Building the html string directly in JS with a recursive function is straight forward enough: (http://jsfiddle.net/fergal_doyle/WN8hZ/5/)

var html = "";
function buildList(a){

    if (a.length == 0){return};

    html += "<ul>";
    for (var i = 0; i < a.length; i++)
    {
        html += "<li>" + a[i].name;
        buildList(a[i].children);
        html += "</li>";
    }
    html += "</ul>";            
}
buildList(data.list);

$("#out").html(html);


But with doT.js this is what I have and after that I'm stumped! (http://jsfiddle.net/fergal_doyle/BTZpu/4/)

EDIT: I can do it by mixing in some JS with evaluation (http://jsfiddle.net/fergal_doyle/he8AN/)

{{ function buildList(a) { }}

    {{?a.length}}
    <ul>
        {{~a :v}}
        <li>
            {{=v.name}}
            {{ buildList(v.children); }}
        </li>
        {{~}}
    </ul>
    {{?}}

{{ } }}

{{ buildList( it.list ); }}

I was trying to achieve it using partials. Defining a ul snippet then have that snippet calling itself passing in an array as a param, but I'm getting a "too much recursion" error. If there's any way to get the below working, it's a lot neater than the above I think. (http://jsfiddle.net/fergal_doyle/qazGe/4/)

{{##def.ul:a:
    <ul>
    {{~a :value}}
        <li>{{=value.name}}{{#def.ul:value.children}}</li>
    {{~}}
    </ul>
#}}

{{#def.ul:it.list}}
like image 342
Fergal Avatar asked Dec 30 '12 18:12

Fergal


1 Answers

The issue is at compile time. doT.js doesn't seem be handling recursion in partials. You recursion code {{#def.ul:value.children}} makes the doT.js library infinitely resolve/replace it with the contents of your function. One way to get around this is to use arguments.callee to refer to your partial within your partial. Here's a fork of your FIDDLE

{{##def.ul:a:
  <ul>
  {{~a :value}}
    <li>{{=value.name}}{{=arguments.callee(value.children)}}</li>
  {{~}}
  </ul>
#}}

{{#def.ul:it}}

The author(s) of the library can implement the same idea in their library in the following fashion (untested):

function resolveDefs(c, block, def) {
    return ((typeof block === 'string') ? block : block.toString())
    .replace(c.define || skip, function(m, code, assign, value) {
        if (code.indexOf('def.') === 0) {
            code = code.substring(4);
        }
        if (!(code in def)) {

            // HANDLE RECURSION START
            value = value.replace(c.use || skip, function(m, recursiveCode) {
                if (c.useParams) return recursiveCode.replace(c.useParams, function(m, s, d, param) {
                    if(d == code) {
                        var ret = s + "{{=arguments.callee(";
                        if(param)
                            ret += param;
                        return ret + ")}}";
                    }
                });

            })
            // HANDLE RECURSION END

            if (assign === ':') {
...
like image 159
cbayram Avatar answered Oct 03 '22 06:10

cbayram