{{break}} syntax for {{each}} example ... #741

Closed
BrewDawg opened this Issue Feb 16, 2014 · 1 comment

Comments

Projects
None yet
2 participants

I'm always adding functionality to handlebars for My2ndGeneration and I thought I would share this one here, it comes in real handy. Sometimes in an each loop I want to exit it, so I created the {{break}} expression.

Example usage:

{{#each PrimaryKeys}}

    {{Table.Alias}}.{{Alias}}

    {{#if IsAutoKey}}
        {{break}}
    {{/if}}

{{/each}}

The {{break}} statement above causes the {{#each}} logic to exit the loop.

Here is my implementation (tweaked from my code as I always use x for my extensions so users know, thus it's xEach and xBreak but here it is with just each/break ...

NOTE: It's written in a way that it works as expected no matter how many {{#each}} statements are nested.

var eachExit = [];

instance.registerHelper('break', function (context, options) {
    eachExit.push(true);
});

instance.registerHelper('each', function(context, options) {
  var fn = options.fn, inverse = options.inverse;
  var i = 0, ret = "", data;

  // Push false in because we don't want to exit 
  // unless break is called
  eachExit.push(false);

  if (isFunction(context)) { context = context.call(this); }

  if (options.data) {
    data = createFrame(options.data);
  }

  if(context && typeof context === 'object') {
    if (isArray(context)) {
      for(var j = context.length; i<j; i++) {
        if (data) {
          data.index = i;
          data.first = (i === 0);
          data.last  = (i === (context.length-1));
        }
        ret = ret + fn(context[i], { data: data });

        if (eachExit[eachExit.length - 1] === true) {
            eachExit.pop();  // the break true push
            eachExit.pop();  // our default false push
            return ret;
        }       
      }
    } else {
      for(var key in context) {
        if(context.hasOwnProperty(key)) {
          if(data) { 
            data.key = key; 
            data.index = i;
            data.first = (i === 0);
          }
          ret = ret + fn(context[key], {data: data});
          i++;

          if (eachExit[eachExit.length - 1] === true) {
            eachExit.pop();  // the break true push
            eachExit.pop();  // our default false push
            return ret;
          }       
        }
      }
    }
  }

  if(i === 0){
    ret = inverse(this);
  }

  // The original 'false' value
  eachExit.pop();  

  return ret;
});
Collaborator

kpdecker commented Mar 6, 2014

I don't think that this is something that we're going to implement in the core library as it seems like it could lead to unexpected behavior for users as it does not terminate execution of the current loop as I suspect many users would expect. I.e.

{{each foo}}
  {{break}}
  Why is this printing?
{{/each}}

Glad to link back to a library implementing this on the in the wild section though!

kpdecker closed this Mar 6, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment