archytect
archytect

Reputation: 3725

handlebars with two different sets of properties. helper or partial?

I have a json object, let's call it teamData with Teams.

I want to be able to pass a variable such as Primary or Secondary to a call and have it render a states dropdown with the State of the TeamType selected.

I've been reading many handlebars tutorials but none of them really deal with more than one property of an object or show how to link a value from one property to another.

the states dropdown is simple

 <script type="text/x-handlebars-template" id="tmpl-states">
      <select>
           {{#eachProperty States}}
                <option name="{{property}}">{{value}}</option>
           {{/eachProperty}}
      </select>
 </script>

 Handlebars.registerHelper('eachProperty', function (context, options) {
     var ret = "";
     for (var prop in context) {
          ret = ret + options.fn({ property: prop, value: context[prop] });
     }
     return ret;
 });

but what I want to do is more like (in sudo)

 renderTemplate("tmps-all", "container", "data", "variable");

 <script type="text/x-handlebars-template" id="tmps-all">
      {{#each Teams}}
           {{#if TeamType == variable}} // e.g. Primary
                var State = this.State;
           {{/if}}
      {{/each}}
      <select>
           {{#eachProperty States}}
                {{#if property == State}} // e.g NY
                <option name="{{property}}" selected>{{value}}</option>
                {{/else}}
                <option name="{{property}}">{{value}}</option>
                {{/if}}
           {{/eachProperty}}
      </select>
 </script>

var teamData = {"Teams":[{"TeamType":"Primary","State":"NY"},{"TeamType":"Secondary","State":"CA"}],"States":{"AK":"Alaska","AL":"Alabama","AR":"Arkansas","AZ":"Arizona","CA":"California","CO":"Colorado","CT":"Connecticut","DC":"District of Columbia","DE":"Delaware","FL":"Florida","GA":"Georgia","HI":"Hawaii","IA":"Iowa","ID":"Idaho","IL":"Illinois","IN":"Indiana","KS":"Kansas","KY":"Kentucky","LA":"Louisiana","MA":"Massachusetts","MD":"Maryland","ME":"Maine","MI":"Michigan","MN":"Minnesota","MO":"Missouri","MS":"Mississippi","MT":"Montana","NC":"North Carolina","ND":"North Dakota","NE":"Nebraska","NH":"New Hampshire","NJ":"New Jersey","NM":"New Mexico","NV":"Nevada","NY":"New York","OH":"Ohio","OK":"Oklahoma","OR":"Oregon","PA":"Pennsylvania","PR":"Puerto Rico","RI":"Rhode Island","SC":"South Carolina","SD":"South Dakota","TN":"Tennessee","TX":"Texas","UT":"Utah","VA":"Virginia","VT":"Vermont","WA":"Washington","WI":"Wisconsin","WV":"West Virginia","WY":"Wyoming"}};

Upvotes: 1

Views: 1768

Answers (1)

76484
76484

Reputation: 9003

There is no need for your eachProperty helper. The functionality it is giving you exists already in Handlebars. Let's remove that helper and update our template to the following (Note: I will replace the name attribute with value):

<select>
    {{#each States}}
        <option value="{{@key}}">{{this}}</option>
    {{/each}}
</select>

Now on to the task of setting the selected attributed.

You are trying too much logic into your template. It is not for the template to initialize variables. That work should be done before the template is rendered. We want our code the calls the template method to give the template all the data it needs. This would mean passing to our template a data structure like this:

[
    {
        value: 'AK',
        label: 'Alaska',
        selected: false
    },
    {
        value: 'AL',
        label: 'Alabama',
        selected: false
    },
    // and so on...
]

Our code will do the work of building this data structure:

var selected_team = teamData.Teams.find(team => team.TeamType === 'Primary');
var states = Object.keys(teamData.States).map(key => ({
    value: key,
    label: teamData.States[key],
    selected: (key === selected_team.State)
}));

Now we can modify our template to handle our new data structure:

<select>
    {{#each this}}
        <option value="{{value}}" {{#if selected}}selected{{/if}}>{{label}}</option>
    {{/each}}
</select>

When we call our template, we simply pass in our states variable:

renderTemplate(states);

However: With all of that work behind us, I want to add that I see no purpose in re-rendering this template just to reflect a changed selected option. It makes more sense to me to use the DOM to make the change. Something like the following would suffice:

document.querySelector('#Select [value="NY"]').selected = true;

See: https://stackoverflow.com/a/7373115/3397771

Upvotes: 1

Related Questions