Skip to content
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 · 15 comments
Closed

{{#switch}} helper #927

stevenvachon opened this issue Dec 22, 2014 · 15 comments

Comments

@stevenvachon
Copy link

@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
Copy link
Collaborator

@kpdecker 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
Copy link

@michaelhogg 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
Copy link
Author

@stevenvachon 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-
Copy link

@Billy- 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
Copy link

@jimkoul jimkoul commented Feb 14, 2017

@Billy- Where should we include this?

@Billy-
Copy link

@Billy- 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
Copy link

@poorpaddy 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
Copy link

@poorpaddy 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
Copy link

@a-le 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
Copy link

@infinityplusone 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
Copy link

@infinityplusone 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-
Copy link

@Billy- 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
Copy link

@a-le 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
Copy link

@hoon720823 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 );
@skout90
Copy link

@skout90 skout90 commented Aug 21, 2019

How about this?
just if you want to convert key to value.

module.exports = function(input, cases, values) {
  const caseArray = cases.split(',')
  const valueArray = values.split(',')
  const index = caseArray.indexOf(input)
  
  return valueArray[index]
};
{{switch "Y" "Y,N,D", "YES,NO,DELETED"}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
10 participants