Search code examples
javascripthtmlarraystemplatesmeteor

Easier way to load many Meteor templates due to data hide


I have a selection box where a user can choose which inputs are applicable to him. The idea is that, depending on which options the user selects, only the input fields are loaded, which the user has selected. The aim is to hide the input fields which are useless for a specific user.

I tried something that works, but it is rather inelegant and I am pretty sure that there is a much easier and simple way of coding. However, I don´t know how to implement it.

This is my HTML where the user should type in his input values:

<h5 class="m-t-md">Please choose which Hs are applicable to you:</h5>
<select class="js-source-states-2" id="Hs" multiple="multiple" style="width: 100%">

          <option id="H1" value="H1">H1</option>
          <option id="H2" value="H2">H2</option>
          <option id="H3" value="H3">H3</option>
          […and more than 30 more options]   
                    
    </select>

    <small>Than click on <strong>chosen</strong></small>

    <button type="button" class="btn btn-sm" id="btnChosen">chosen</button>
        <p class="text-big font-light">
    
    {{#if loadH1}}   
        {{> inputH1}}
       {{/if}}
    
    {{#if loadH2}}
        {{> inputH2}}
       {{/if}}
    
    {{#if loadH3}}
        {{> inputH3}}
       {{/if}}
    
    [… and more than 30 more potential templates representing the input fields]
    
       </p>

      <small>and type in your values.</small>

This is my click event:

'click #btnChosen': function (event) {
    event.preventDefault();

//if something is chosen, save the selected options in a var

if ($('#Hs').val() != "" && $('#Hs').val() != undefined) {var Hs = $('#Hs').val()} else {var Hs = ["empty"]};

//Check with indexOf() which options are chosen and save the index in a var.

var H1 = Hs.indexOf("H1");
var H2 = Hs.indexOf("H2");
var H3 = Hs.indexOf("H3)");
[…and more than 30 more]

//In case the option is NOT chosen, a var will have the value -1. 
//So, in case the value is not -1, set the Session variable of that option to true. 
//Set it to false when the value is -1 (option not chosen)

if (H1 != -1 && H1 != undefined){Session.set('H1', true)} else {Session.set('H1', false)};
if (H2 != -1 && H2 != undefined){Session.set('H2', true)} else {Session.set('H2', false)};
if (H3 != -1 && H3 != undefined){Session.set('H3', true)} else {Session.set('H3', false)};
[…and more than 30 more]
},

Than I wrote a helper for every single input field:

  loadH1() {
    return Session.get('H1');
  },

  loadH2() {
    return Session.get('H2');
  },

  loadH3() {
    return Session.get('H3');
  },

[…and more than 30 more]

And lastly my HTML templates for every single one:

<template name="inputH1">

<input type="number" step="0.01" id="inputH1" placeholder="yourH1Value" class="form-control">
                            <p></p>
</template>

<template name="input2">

<input type="number" step="0.01" id="input2" placeholder="yourH2Value" class="form-control">
                            <p></p>
</template>

<template name="inputH3">

<input type="number" step="0.01" id="inputH3" placeholder="yourH3Value" class="form-control">
                            <p></p>
</template>

[…and more than 30 more]

Due to the fact that I would have to do this about more than five times again, I am looking for ways to do it with less code. I know that the code I wrote is extremely inefficient.


Solution

  • Honestly, you don't need reactivity here.

    I'd suggest to do it like this:

    Template HTML file:

    <h5 class="m-t-md">Please choose which Hs are applicable to you:</h5>
    
    <select class="js-source-states-2" id="Hs" multiple="multiple" style="width: 100%">
      {{#each options}}
        <option value=“H{{this}}”>H{{this}}</option>
      {{/each}}
    </select>
    
    <small>Than click on <strong>chosen</strong></small>
    
    <button type="button" class="btn btn-sm" id="btnChosen">chosen</button>
    <p class="text-big font-light” id="inputsBlock">
      {{#each options}}
        <input type="number" step="0.01"
               class="form-control hidden"
               id="inputH{{this}}"
               placeholder="yourH{{this}}Value">
        <br/>
      {{/each}}
    </p>
    
    <small>and type in your values.</small>
    

    Template JS file:

    I don't know your template's name, so I will use TPL instead of it, you should replace it with actual template name.

    // change this value
    const valuesCount = 50;
    
    Template.TPL.helpers({
      options() {
        return _.range(1, valuesCount + 1);
      }
    });
    
    Template.TPL.events({
      'click #btnChosen'(event) {
        event.preventDefault();
    
        const values = $('#Hs').val();
        const inputs = $('#inputsBlock input');
    
        // hide all inputs
        inputs.addClass('hidden');
    
        if (values == null) {
          // nothing to show, no values selected
          return;
        }
    
        const filter = _.map(values, (v) => `#inputH${v}`).join(',');
        inputs.filter(filter).removeClass('hidden');
      }
    });
    

    CSS file:

    .hidden { display: none; }
    .hidden + br { display: none; }
    

    Added: JSFiddle representing such behavior: http://jsfiddle.net/iStyx/5ws7x76n/