Logical OR/AND Handlebars.JS Helper, Multiple Argu

2019-08-19 03:12发布

问题:

The following was proposed as a logical AND/OR multi-arg Handlebars.JS helper:

Handlebars.registerHelper({
    and: function () {
        return Array.prototype.slice.call(arguments).every(Boolean);
    },
    or: function () {
        return Array.prototype.slice.call(arguments).some(Boolean);
    }
});

Handlebars.js Else If

This doesn't work for me because I need to call it as

{{#if (or questionType 'STARTTIME' 'ENDTIME') }}

{{#if (or questionType 'STARTTIME' 'ENDTIME' 'ARGUMENT3' 'ARGUMENT4') }}

In other words,

  1. I support multiple args for my AND/OR,
  2. The first arg is always what I'm checking, e.g.

    return (questionType == arg1 || questionType == arg2 || questionType == arg3 ...)

In other words, I can't write a dumb 2-param or(..) / and(..) like this,

Handlebars.registerHelper('or', function(a, b, c) {
    if(a == b || a == c)
        return true;
    else
        return false;
});

It should be multi-argument, with the first one always being checked. Any thoughts?

回答1:

First: Your original or helper is not going to work. Handlebars passes an additional meta object as the final argument when invoking a helper. For example, using your or helper in a template as (or false false) results in the helper function being executed with the following arguments object:

{
    0: false,
    1: false,
    2: {
        "name": "or",
        "hash": {...},
        "data": {...}
    },
    length: 3
}

The existence of that Object at 3 will evaluate to true in the Boolean conversion and cause your helper to return true despite that the call site passes only false values.

To make the helper work as intended, we need to exclude the last arg when slicing our arguments object. (FYI: The purpose of the slice is to convert the array-like arguments Object to an Array, so that we may call Array.prototype methods on it, like .some.) To do this, we update our or helper to be:

return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);

Now we can turn to the problem of comparing our first argument expression to the rest. We can similarly update our .slice call to exclude the first argument as well: .slice.call(arguments, 1, -1). Then we need only to compare each item in the slice to the first argument. Our helper becomes:

return Array.prototype.slice.call(arguments, 1, -1).some(arg => arg === arguments[0]);

Our helper now works as we intend; but I would urge you to rename it as it not an "or" operation, but an "in".