Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handlebars Block Helper : each with sort

I have an array of json objects which I output using a Handlebars template; I am currently doing using {{#each object}}...{{/each}}. I now need to sort the objects by one of the object's properties, which again is no problem using a handlebars helper & coffeescript, however, I have a problem in my template in that I cannot work out how to iterate over the sorted array using each.

My research so far indicates that I probably need to write a custom Handlebars helper which will, in effect, be:

{{#each_with_sort array}}

My existing sort helper is like this

   Handlebars.registerHelper sort_me =>
     myArray.sort (a,b)->
       return if +a.sort_index >= +b.sort_index then 1 else -1

but, I am struggling to be able to use the sorted array in the template - for example, it is not as simple as

 {{#each sort_me(myArray)}}

The data is coming from a third party API, so I have to perform sorting in handlebars/coffeescript.

like image 752
Tykus Avatar asked Jul 15 '13 19:07

Tykus


People also ask

What is helper in Handlebars?

A Handlebars helper call is a simple identifier, followed by zero or more parameters (separated by a space). Each parameter is a Handlebars expression that is evaluated exactly the same way as described above in "Basic Usage": template {{firstname}} {{loud lastname}}

How do I register Handlebars in helpers?

registerHelper("noop", function(options) { return options. fn(this); }); Handlebars always invokes helpers with the current context as this , so you can invoke the block with this to evaluate the block in the current context.

What are Handlebars options?

compile' and 'Handlebars. precompile' create a function. This function can be called as template(context, options) where context is the input object. options is an object that can have any of the following properties. data Pass in an object to define custom @variable private variables.

Which mapping uses Handlebars expressions?

Handlebars expressions can be used to: Map Export and Import application fields.


1 Answers

The easiest thing to do would be to sort the data before it gets to Handlebars, then you can use {{#each ...}} as usual and no helpers are needed. This sort of approach is quite common with Handlebars, the template is often split into two pieces: a (Java|Coffee)Script piece for data mangling/rearrangement and the template proper.

As an aside, you'll want to adjust your comparator function to behave property. From the fine manual:

If compareFunction is supplied, the array elements are sorted according to the return value of the compare function. If a and b are two elements being compared, then:

  • If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
  • If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
  • If compareFunction(a, b) is greater than 0, sort b to a lower index than a.

So you want to return 0 if a.sort_index and b.sort_index are the same, something more like this:

myArray.sort (a,b)->
  a = +a.sort_index
  b = +b.sort_index
  return  1 if a > b
  return  0 if a == b
  return -1 if a < b

If you must do the sorting inside the template then you'd need to add a custom each_with_sort helper to do both the sorting and the iteration, something like this:

# If you're always sorting by sort_index then you don't need the key argument
# and you might want to put the '+'-casting back in.
Handlebars.registerHelper('each_with_sort', (array, key, opts) ->
    data  = Handlebars.createFrame(opts.data) if(opts.data)
    array = array.sort (a, b) ->
        a = a[key]
        b = b[key]
        return  1 if a > b
        return  0 if a == b
        return -1 if a < b
    s = ''
    for e, i in array
        data.index = i if(data) # Support the usual @index.
        s += opts.fn(e, data: data)
    s
)

and your template would be like this:

{{#each_with_sort array "sort_index"}}
    ...
{{/each_with_sort}}

Demo: http://jsfiddle.net/ambiguous/zusq2tt4/

like image 75
mu is too short Avatar answered Oct 16 '22 11:10

mu is too short