1

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"}};
archytect
  • 3,615
  • 5
  • 22
  • 30

1 Answers1

1

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

Community
  • 1
  • 1
76484
  • 8,498
  • 3
  • 19
  • 30
  • very nice! I haven't dabbled much with ecmascript and its new prototype functions, but your approach makes a lot more sense - the data should be restructured in the first place and handlebars should just be left to template. what's a good tutorial that has examples similar to the ones you used? particularly the predicate you're using with the find method – archytect Apr 09 '16 at 23:29
  • MDN is always good: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions – 76484 Apr 09 '16 at 23:33
  • The equivalent function expression would be: `function (team) { return team.TeamType === 'Primary'; }` – 76484 Apr 09 '16 at 23:38
  • Asking Google for "es6 arrow functions" will give you better suggestions than I can. – 76484 Apr 10 '16 at 16:43