New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

{{#switch}} helper #927

Closed
stevenvachon opened this Issue Dec 22, 2014 · 14 comments

Comments

Projects
None yet
9 participants
@stevenvachon

stevenvachon commented Dec 22, 2014

This is very useful and I think it'd be best if it were native to the library:

{{#switch state}}
    {{#case "page1" "page2"}}toolbar{{/case}}
    {{#case "page1" break=true}}page1{{/case}}
    {{#case "page2" break=true}}page2{{/case}}
    {{#case "page3" break=true}}page3{{/case}}
    {{#default}}page0{{/default}}
{{/switch}}
@kpdecker

This comment has been minimized.

Collaborator

kpdecker commented Dec 26, 2014

This is something that can be achieved (in a hacky manner) using standard helpers. We wish to keep the builtin helpers relatively lightweight, in stead allowing 3rd parties to implement the helpers they need, in the manner that suits them best, using the helper API.

@kpdecker kpdecker closed this Dec 26, 2014

@michaelhogg

This comment has been minimized.

michaelhogg commented Mar 24, 2016

This page is currently the 3rd Google search result for "Handlebars switch helper". For the benefit of people arriving here from a search engine, here's an implementation by Chris Montrois which supports single-value case clauses such as {{#case "page1"}}:

Handlebars.registerHelper("switch", function(value, options) {
    this._switch_value_ = value;
    var html = options.fn(this); // Process the body of the switch block
    delete this._switch_value_;
    return html;
});

Handlebars.registerHelper("case", function(value, options) {
    if (value == this._switch_value_) {
        return options.fn(this);
    }
});

Here's an improved case helper which supports clauses with a variable number of values, such as {{#case "page1" "page2"}}:

Handlebars.registerHelper("case", function() {
    // Convert "arguments" to a real array - stackoverflow.com/a/4775938
    var args = Array.prototype.slice.call(arguments);

    var options    = args.pop();
    var caseValues = args;

    if (caseValues.indexOf(this._switch_value_) === -1) {
        return '';
    } else {
        return options.fn(this);
    }
});
@stevenvachon

This comment has been minimized.

stevenvachon commented Mar 24, 2016

Very nice, short and sweet. I'd come across much longer ones in the past.

A couple of things to add might be {{#default}} {{/default}} and {{#case "value" break=true}}.

@Billy-

This comment has been minimized.

Billy- commented Jul 7, 2016

@stevenvachon As requested ;)

module.exports = {
    switch: function(value, options) {
        this._switch_value_ = value;
        this._switch_break_ = false;
        var html = options.fn(this);
        delete this._switch_break_;
        delete this._switch_value_;
        return html;
    },
    case: function(value, options) {
        var args = Array.prototype.slice.call(arguments);
        var options    = args.pop();
        var caseValues = args;

        if (this._switch_break_ || caseValues.indexOf(this._switch_value_) === -1) {
            return '';
        } else {
            if (options.hash.break === true) {
                this._switch_break_ = true;
            }
            return options.fn(this);
        }
    },
    default: function(options) {
        if (!this._switch_break_) {
            return options.fn(this);
        }
    }
};
@jimkoul

This comment has been minimized.

jimkoul commented Feb 14, 2017

@Billy- Where should we include this?

@Billy-

This comment has been minimized.

Billy- commented Feb 14, 2017

@jimkoul These are helper functions so it depends on your set up.

My set up (with Gulp + gulp-hb) supports specifying a glob pattern for js files which export helper functions, as above, so it looks like this:

// ...
.pipe(handlebars({
  helpers: 'handlebars/helpers/*.js'
})
// ...

If you're still not sure do a little research into how helper functions work with handlebars, and how to implement them with whatever implementation of handlebars you are using.

@poorpaddy

This comment has been minimized.

poorpaddy commented Mar 16, 2017

It would be nice if we could pass variable thru the switch name and not have to write a complete {{assign}} thru each. That way the option becomes the variable value.

Additionally I'm also getting an undefined message even though the variable is changing correctly when the case is met.

This is what I have for the helper

"use strict";
Handlebars.registerHelper("switch", function(value, options) {
    this._switch_value_ = value;
    this._switch_break_ = false;
    var html = options.fn(this);
    delete this._switch_break_;
    delete this._switch_value_;
    return html;
});

Handlebars.registerHelper("case", function(value, options) {
    var args = Array.prototype.slice.call(arguments);
    var options    = args.pop();
    var caseValues = args;

    if (this._switch_break_ || caseValues.indexOf(this._switch_value_) === -1) {
        return '';
    } else {
        if (options.hash.break === true) {
            this._switch_break_ = true;
        }
        return options.fn(this);
    }
});

Handlebars.registerHelper("default", function(options) {
    if (!this._switch_break_) {
        return options.fn(this);
    }
});

This is what I have in my hbs file:

{{#assign "testParam"}}foo{{/assign}}
{{#switch testParam}}
    {{#case "boo"}}{{#assign "testParam"}}case1 has been met{{/assign}}{{/case}}
    {{#case "foo" break=true}}{{#assign "testParam"}}case2 has been met{{/assign}}{{/case}}
    {{#case "tried" break=true}}{{#assign "testParam"}}case3 has been met{{/assign}}{{/case}}
    {{#case "bwahahaha" break=true}}{{#assign "testParam"}}case4 has been met{{/assign}}{{/case}}
    {{#default break=true}}{{#assign "testParam"}}nothing matched{{/assign}}{{/default}}
{{/switch}}

{{#ttpartial "testSwitch.content"}}
        {{testParam}}
{{/ttpartial}}
@poorpaddy

This comment has been minimized.

poorpaddy commented Mar 17, 2017

or some reason the above makes the default always take precedence even when one of the cases above are met.

@a-le

This comment has been minimized.

a-le commented Jun 17, 2017

Here's what I use:

Handlebars.registerHelper('switch', function(name, value, options) {
    this['_switch_value_' + name] = value;
    this['_switch_break_' + name] = false;
    var html = options.fn(this);
    delete this['_switch_break_' + name];
    delete this['_switch_value_' + name];
    return html;
});
Handlebars.registerHelper('case', function(name, value, options) {
    var args = Array.prototype.slice.call(arguments);
    var options    = args.pop();
    var caseValues = args;

    if ( this['_switch_break_' + name] || caseValues.indexOf(this['_switch_value_' + name]) === -1) {
        return '';
    } else {
        this['_switch_break_' + name] = true;
        return options.fn(this);
    }
});
Handlebars.registerHelper('default', function(name, options) {
    if ( !this['_switch_break_' + name] ) {
        return options.fn(this);
    }
});

The switch are named, so it is possible to branch them (imbrication ? not sure about the word...)
Also, break is always done when a condition is met

ie:

    {{#switch "1" "aaa"}}
        {{#case "1" "aaa"}}
            {{#switch "2" "bbb"}}
                {{#case "2" "bbb"}}ok{{/case}}
            {{/switch}}
        {{/case}}
        {{#default "1"}}nok{{/default}}
    {{/switch}}
@infinityplusone

This comment has been minimized.

infinityplusone commented Jul 27, 2017

I'm sure I'm failing to understand some of how this works, so I apologize for my confusion, but... Is it possible for the switch to work within an each? I've been trying it out, but I keep getting errors.

Here's what I've got:

{{#each columns}}
    {{#switch this}}
        {{#case 'foo'}}
        {{/case}}
        {{#case 'bar'}}
        {{/case}}
    {{/switch}}
{{/each}}

But I'm getting this:

Uncaught TypeError: Cannot create property '_switch_value_' on string 'name'

It seems to me that this must be something other than what is expected, but I don't really know.

@infinityplusone

This comment has been minimized.

infinityplusone commented Jul 27, 2017

Looks like I was able to get it working by changing the switch as follows:

Handlebars.registerHelper({
    switch: function(value, options) {
        return options.fn({
            _switch_value_: value,
            _switch_break_: false
        });
    },
    //...
});

But I'm guessing I'm losing something for other cases. This said, I could always attach these values to options, and I think it would work.

@Billy- @a-le ?

@Billy-

This comment has been minimized.

Billy- commented Jul 27, 2017

@infinityplusone unfortunately I haven't used handlebars properly in years so I imagine that the helper implementation may have changed heavily since I wrote that, which is probably why it wasn't working as expected. I'm not sure though, sorry to disappoint.

@a-le

This comment has been minimized.

a-le commented Jul 28, 2017

@infinityplusone If you use my version, you have to "name" the switch.
So, with your example:

{{#each columns}}
    {{#switch "myswitch" this}}
        {{#case "myswitch" 'foo'}}
        {{/case}}
        {{#case "myswitch" 'bar'}}
        {{/case}}
    {{/switch}}
{{/each}}
@hoon720823

This comment has been minimized.

hoon720823 commented Jul 28, 2017

for nested switch block

Handlebars.__switch_stack__ = [];

Handlebars.registerHelper( "switch", function( value, options ) {
    Handlebars.__switch_stack__.push({
        switch_match : false,
        switch_value : value
    });
    var html = options.fn( this );
    Handlebars.__switch_stack__.pop();
    return html;
} );
Handlebars.registerHelper( "case", function( value, options ) {
    var args = Array.from( arguments );
    var options = args.pop();
    var caseValues = args;
    var stack = Handlebars.__switch_stack__[Handlebars.__switch_stack__.length - 1];
    
    if ( stack.switch_match || caseValues.indexOf( stack.switch_value ) === -1 ) {
        return '';
    } else {
        stack.switch_match = true;
        return options.fn( this );
    }
} );
Handlebars.registerHelper( "default", function( options ) {
    var stack = Handlebars.__switch_stack__[Handlebars.__switch_stack__.length - 1];
    if ( !stack.switch_match ) {
        return options.fn( this );
    }
} );
{{#switch state}}
    {{#case "page1" "page2"}}page 1 or 2{{/case}}
    {{#case "page3"}}page3{{/case}}
    {{#case "page4"}}page4{{/case}}
    {{#case "page5"}}
            {{#switch s}}
                {{#case "3"}}s = 3{{/case}}
                {{#case "2"}}s = 2{{/case}}
                {{#case "1"}}s = 1{{/case}}
                {{#default}}unknown{{/default}}
            {{/switch}}
    {{/case}}
    {{#default}}page0{{/default}}
{{/switch}}
var data = {
    state : 'page5',
    s : '1'
};

var html = template( data );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment