Search code examples
javascriptjqueryjsrender

Use JsRender to create a row/column layout from simple array data


I am using jsrender to write a bootstrap based HTML template and render the HTML content using javascript. My data is as follows

var data = [
    {
        'img': 'img/books/kiterunner.jpg',
        'title': 'The Kite Runner',
        'authors': 'Khaled Hosseini'
    },
    {
        'img': 'img/books/tokillamockingbird.jpg',
        'title': 'To Kill A Mocking Bird',
        'authors': 'Harper Lee'
    },
    {
        'img': 'img/books/hungergames.jpg',
        'title': 'The Hunger Games',
        'authors': 'Suzanne Collins'
    }
    .....
]

And the corresponding HTML I want to generate is as follows

<div class="row-fluid" id="result">
    <script id="template" type="text/x-js-render">
        <div class="col-md-3 col-sm-6">
            <div class="image-tile outer-title text-center">
                <img alt="{{:title}}" src="{{:img}}" style="max-height:350px;">
                <div class="title mb16">
                    <h5 class="uppercase mb0">{{:title}}</h5>
                        <span>{{:authors}}</span>
                </div>
            </div>
       </div>
   </script>
</div>

Currently each object in data gets displayed individually but sometimes, the images are larger in size and disrupt the way the row looks. So I want to repeat the div.row-fluid creation with the content after every 4 objects from data. I currently do the following to register the template and render the data into the required HTML. I can't seem to figure out how to use {{for}} without changing the values in data.

var template = $.templates("#template");
var htmlOutput = template.render(data);
$("#result").html(htmlOutput);

Thank you.

Edit: I have managed to do this by doing explicit HTML string concatenation and then rendering them while using index%4 in the if statements to decide on whether the div.row-fluid needs to be created. However, I would really like a more elegant solution. Adding jquery tag for wider visibility.


Solution

  • You could use a custom {{range}} tag - see this sample.

    Here is an example which renders into a table with <td>s grouped by four - for a four column layout:

    <script id="myTmpl" type="text/x-jsrender">
      <table>
        {{for rows ~last=rows.length-1}}
          {{if #index%4===0 ~ind=#index}}
            <tr>
            {{range ~root.rows start=~ind end=~ind+3 max=~last}}
              <td>{{:title}}</td>
            {{/range}}
            </tr>
          {{/if}}
        {{/for}}
      </table>
    </script>
    

    using this version of a custom {{range}} tag:

    $.views.tags("range", function(array) {
      var ret = "",
        start = this.tagCtx.props.start,
        end = this.tagCtx.props.end,
        max = this.tagCtx.props.max;
    
      end = end > max ? max : end;
    
      for (var i = start; i <= end; i++) {
        // Render tag content, for this data item
        ret += this.tagCtx.render(array[i]);
      }
      return ret;
    });
    
    var data = {rows: [
        {
            'title': ...
        },
        ...
      ]};
    
      ...