node.jsexpresshandlebars.jsexpress-handlebarshandlebarshelper

Standardize Dropdown's Selected Handlebars Helper


I'm facing the following challenge building some dropdowns with Handlebars.

Instead of modeling the dropdown's select this way:

<div class="form-floating col-3">
            <select name="sport" class="form-select" id="floatingSelect">
                <option {{selectedSport sport 'all'}} value="all">All</option>
                <option {{selectedSport sport 'Hiking'}} value="Hiking">Hiking</option>
                <option {{selectedSport sport 'Mountaineering'}} value="Mountaineering">Mountaineering</option>
                <option {{selectedSport sport 'Climbing'}} value="Climbing">Climbing</option>
                <option {{selectedSport sport 'Mountain biking'}} value="Mountain biking">Mountain biking</option>
            </select>
            <label for="floatingSelect">Sport</label>
        </div>
        <div class="form-floating col-2">
            <select name="difficulty" class="form-select" id="floatingSelect">
                <option {{selectedDifficulty difficulty 'all'}} value="all">All</option>
                <option {{selectedDifficulty difficulty 'Easy'}} value="Easy">Easy</option>
                <option {{selectedDifficulty difficulty 'Medium'}} value="Medium">Medium</option>
                <option {{selectedDifficulty difficulty 'Hard'}} value="Hard">Hard</option>
                <option {{selectedDifficulty difficulty 'Expert'}} value="Expert">Expert</option>
            </select>
            <label for="floatingSelect">Difficulty</label>
        </div>

I would like to use the same helper for both of them, but I still don't know how to do it. (btw, ignore the fact that I should be using an {{#each}} for displaying the options :P)

The controller:

module.exports.list = (req, res, next) => {
const filters = req.query;
const { location, sport, difficulty } = req.query;
const criterial = Object.keys(filters)
    .filter((key => filters[key] !== 'all'))
    .reduce((criterial, filter) => {
        if (filters[filter]) criterial[filter] = filters[filter];
        return criterial;
    }, {});

    Route.find(criterial)
    .then(routes => {
        res.render('routes/list', { routes, location, sport, difficulty })
    })
    .catch(next)
   }

The helpers:

hbs.registerHelper('selectedSport', (option, sportValue) => {
    return option === sportValue ? ' selected' : '';
})

hbs.registerHelper('selectedDifficulty', (option, difficultyValue) => {
    return option === difficultyValue ? ' selected' : '';
})

Many thanks for your help! :)


Solution

  • Finally I solved it this way:

    Instead of typing all the options directly on the HBS, I create these with a helper that renders each option.

            <div class="form-floating col-3">
                <select name="sport" class="form-select" id="floatingSelect">
                    {{#each sportOptions as |sportOption|}}
                    {{option ../sport sportOption}}
                    {{/each}}
                </select>
                <label for="floatingSelect">Sport</label>
            </div>
            <div class="form-floating col-2">
                <select name="difficulty" class="form-select" id="floatingSelect">
                    {{#each difficultyOptions as |difficultyOption|}}
                    {{option ../difficulty difficultyOption}}
                    {{/each}}
                </select>
                <label for="floatingSelect">Difficulty</label>
            </div>
    

    The helper receives 2 parameters: sport and sportOption and according to their value, returns a string cointaining the option with the selected attribute or not.

    hbs.registerHelper('option', function (selectedValue, value) {
        const selectedProperty = value == selectedValue ? 'selected' : '';
        return new hbs.SafeString(`<option value=${value} ${selectedProperty}>${value}</option>`);
      });
    

    sportOption and difficultyOption are arrays with all the possibilities inside them.

    I hope that, if someone else has the same problem, this can be of any help :)