user533844
user533844

Reputation: 2063

Handlebars.js custom function sort

I am using Handlebars to display comments.

{{#each COMMENTS}}
     <div class="heightfix">
          <div class="commentIcon"></div>&nbsp;&nbsp;
             <label id="commentDate1" class="bold">{{COMMENTON}}</label>:&nbsp;
             <label id="commentCreator1" class="bold">{{COMMENTBY_FIRSTNAME}} {{COMMENTBY_LASTNAME}}</label>&nbsp;&nbsp;
             <label class="commentContent" id="commenttext_{{ID}}">{{COMMENTTEXT}}</label>
     </div>                                  
{{/each}}   

In those COMMENTS, I have INDEX. I would like to display the comments as per their index. Comment 0 Comment 1 Comment 2 Comment 3 .....

How do I achieve this using custom functions ? Thanks

Upvotes: 2

Views: 7033

Answers (2)

Hilson Francis
Hilson Francis

Reputation: 29

Below am sorting items for an image gallery

Need to know

  • Function takes in 3 perimeters
    • data to sort
    • value to sort by
    • heading for sorted data
  • Function returns html with sorted data

Data

{
"imageUrl" : "img/shoes.jpg",
"category" : "shoes"
},
{
"imageUrl" : "img/hat.jpg",
"category" : "hat"
}

JS Node Sort Function server side

"use strict";
var helpers = module.exports;

helpers.sort = function(data, value, heading) {
var str = '<div class="row">'+
'<div class="container headings">'+
  '<h2 class="center">'+heading+'</h2>'+ // heading data from Markdown
  '<hr>'+
'</div>'+
'<div class="swap"><i class="material-icons">swap_horiz</i></div>'+
'<div class="carousel">';
data.forEach(function(gal){ // loop through all data
if (gal.category === value) {  // sort data by value
 str += '<a class="carousel-item" href="#!"><img src="'+gal.imageUrl+'" alt="gallery_image"></a>'; // gallery item

}
})
str += '</div>'+
        '</div>'+
        '</div>';
return str;  // return sorted html data 
};

Handlebars Markup client side

 {{#sort data 'shoes' 'Jordans'}}{{/sort}}
  • call the helper and add the 3 parameters
    • 1st parameter includes all the data to be sorted data
    • 2nd parameter is the value to sort by 'shoes'
    • 3rd parameter is the heading for the sorted data 'Jordans'

Upvotes: 0

Ethan Brown
Ethan Brown

Reputation: 27282

Method 1: Custom Accessor

If you have control over the context (and you probably do), you can implement this with property accessors. Let's say your context contains the comments in jumbled order:

var context = {
    comments: [
        { idx:6, text:'violet' },
        { idx:1, text:'orange' },
        { idx:0, text:'red' },
        { idx:5, text:'indigo' },
        { idx:3, text:'green' },
        { idx:2, text:'yellow' },
        { idx:4, text:'blue' },
    ],
};

You could just sort the comments array in place before rendering your template (note that for numeric values, you can just subtract them to get natural ordering):

context.comments.sort( function(a,b) { return a.idx - b.idx; } );

The problem with that is that will modify the context object, which you may not want to do. A solution is to provide a custom accessor that will return the comments sorted by some custom property. Here's how we can do that:

Object.defineProperty( context, 'commentsByIdx', {
    get: function() {
        // note that we call concat() to create a copy of
        // the array; otherwise we are modifying the
        // the original array, which is a side effect
        return this.comments.concat()
            .sort( function(a,b) { return a.idx - b.idx } );
    }
});

You can see this solution in action here: http://jsfiddle.net/Jammerwoch/ShZLY/

Method 2: Handlebars Block Helper

The other approach is to implement a Handlebars block helper. This approach will work better if you don't have much control over the context (or don't want to contaminate it with custom properties). Simply register a Handlebars helper:

Handlebars.registerHelper('eachByIdx', function(context,options){
    var output = '';
    var contextSorted = context.concat()
        .sort( function(a,b) { return a.idx - b.idx } );
    for(var i=0, j=contextSorted.length; i<j; i++) {
        output += options.fn(contextSorted[i]);
    }
    return output;
});

And call it like so in your template:

{{#eachByIdx comments}}
    <li>{{text}}</li>
{{/eachByIdx}}

See it in action here: http://jsfiddle.net/Jammerwoch/aeCYg/1/

All in all, I find the first method preferable, as it creates a new view of your data that's potentially useful outside of Handlebars. Furthermore, to the casual reader of Handlebars code, it will make more sense.

Upvotes: 12

Related Questions