Distribute labels in columns instead of rows

I have this table below and I would like my labels in the left to be distributed in every column instead of every row that are like now.
53

Instead I would like this:
27
Without the values next to each label.

This is my HTML for the table:

   <table id="matrix-input" class="responsive-table-input-matrix">
		    <thead>
            <tr>
                <th></th>
                {{#each field in listParams}}
			          <th>{{field}}</th>
                {{/each}}
            </tr>
		    </thead> 
        <tbody>
            {{#each item in dataItems}}
		        <tr>
                <th></th>
                <th>pt</th>
                <th>eta</th>
                <th>phi</th>
                <th>charge</th>
                {{#each field in listParams}}
                <td>{{item[field]}} <input class="{{field}}" type="radio" name="{{field}}" data-item="{{item._id}}" data-field="{{field}}" value={{item[field]}}></td>
                {{/each}}
            </tr>
            {{/each}}
        </tbody>
	  </table>

If the field variable corresponds to the label reliably, just make a small dictionary lookup type object, with the field as the key, and the label string as the value. Just have one column for the label. You could also use index and/or modulus if that is more reliable and consistent than the field variable.

this isnā€™t right, you need a helper or dictionary object etc where a row would know itā€™s identity, (which label to use, pt/eta/phi)? You could arrive at that using modulus/index or fix the data to include that info so you can reference it directly. Use either an if helper, or more performant, if possible keep it outside of the nested each.



<table id="matrix-input" class="responsive-table-input-matrix">
  <thead>
    <tr>
      <th></th>
      {{#each field in listParams}}
        <th>{{field}}</th>
      {{/each}}
    </tr>
  </thead> 
  <tbody>
    {{#each item in dataItems}}
	  <tr>
	    
      	{{#each field in listParams}}
          <!-- create a helper or confirm syntax. I don't use blaze now.  -->
          {{#if @index === 0}} 
              <th>{{fieldLabelHelper}}</th>
          {{/if}}
        	<td>{{item[field]}} <input class="{{field}}" type="radio" name="{{field}}" data-item="{{item._id}}" data-field="{{field}}" value={{item[field]}}></td>
      	{{/each}}
      </tr>
    {{/each}}
  </tbody>
</table>

I am not sure this is working, I get this ā†’ While processing files with templating-compiler (for target web.browser):
client/ipsosboard.html:33: Expected identifier, number, string, boolean, null, or a sub expression enclosed in ā€œ(ā€, ā€œ)ā€

But I think this is the least of my problem as fiddling around and making it work I could see my table turning to this: 32

My keys/labels are here: [ā€˜ptā€™, ā€˜etaā€™, ā€˜phiā€™, ā€˜chargeā€™] Even trying to hardcode them it will put all array in every row and iterate it in every collumn, instead of having each one consecutively.

labels in the left to be distributed in every column
Without the values next to each label

I am not sure to clearly understand what you really want to achieve but, is that what you want to do ?
a

Yes. It looks like what I am after.

What is missing for you ?

I was not sure what to do with the values. Do you want these to be displayed in each cell like your previous image ?

Yes exactly. It basically reads a json file with key:value pairs.

I see now. You can use #let tag with Blaze to capture the index of the current label.

a

<template name="table">
    <table>
      <thead>
        <tr>
          <th> - </th>
          {{ #each field in listParams }}
            <th>{{ field }}</th>
          {{ /each }}
        </tr>
      </thead>
      
      <tbody>
        {{ #each labels }}
        <tr>

          <td>{{ getLabel }}</td>

          {{ #let labelIndex=@index }}
            {{ #each field in listParams }}
              <td>{{ getLabelValue labelIndex }} </td>
            {{ /each }}
          {{ /let }}

        </tr>
        {{ /each }}
      </tbody>
    </table>
</template>
Template.table.onCreated(function () {

  this.listParams = [
    'attack',    'decay',       'sustain',
    'release',   'detune',      'voice_one',
    'voice_two', 'voice_three', 'voice_four',
  ];

  this.labels = [
    { title: 'pt',  value: 1, },
    { title: 'phi', value: 2, },
    { title: 'eta', value: 3, },
    { title: 'pt',  value: 4, },
    { title: 'phi', value: 5, },
    { title: 'eta', value: 6, },
  ];

  this.currentLabelIndex = 0;
});


Template.table.helpers({

  listParams: () => Template.instance().listParams,

  labels: () => Template.instance().labels,

  getLabel() {
    const template = Template.instance();
    const index    = template.currentLabelIndex;
    const title    = template.labels[ index ].title;

    ++template.currentLabelIndex;

    return title;
  },

  getLabelValue: ( index ) => Template.instance().labels[ index ].value,

});

I am missing the radio buttons, do I put it after {{ /let }}
{{item[field]}} <input class="{{field}}" type="radio" name="{{field}}" data-item="{{item._id}}" data-field="{{field}}" value={{item[field]}}

Why would you put <input> outside the {{ #let }} ? By doing that, it wonā€™t be in the same cell as the value. Just add it next to the label value ?

For the data-item attribute, in order to get unique values for each <input>, you could (for example) use a helper (that will return the ID of the current label) + the current field.

Okay, I will study it, lots of stuff for me donā€™t make sense for me as this is my first project using Meteor JS HTML and whatnot. BTW, here is how my data look like:

muons:[{"pt":1.5,"eta":-1.5,"phi":3.7,"charge":-1},{"pt":0.2,"eta":-2.3,"phi":-1.3,"charge":1}]

So I need to adapt the helpers for that I guess, right?

I am learning Meteor too, I donā€™t have a lot of experience with it but as I really like it I try to help people (like you) as much as I can, it helps me to better understand it and to improve my skills with it :slight_smile: .

By seeing your data, I think the easiest way is to simply transform it into a easier format for Blaze. In my opinion it simplified the helpers.

I created other variables with #let to avoid having duplicated code. But overall, the code remains almost the same as before.

What do you think ?
a

<template name="table">
    <table>
      <thead>
        <tr>
          <th> - </th>
          {{ #each field in listParams }}
            <th>{{ field }}</th>
          {{ /each }}
        </tr>
      </thead>

      <tbody>
        {{ #each tupleMuon in allMuons }}
          <tr>
            {{ #let
              indexTuple=@index
              muonLabel=(getMuonLabel tupleMuon)
              muonValue=(getMuonValue tupleMuon)
            }}

              <td><b>{{ muonLabel }}</b></td>

              {{ #each field in listParams }}
                <td>
                  {{ muonValue }}
                  <input type="radio" name="{{ field }}" class="{{ field }}"
                    data-item="{{ muonLabel }}-{{ field }}-{{ indexTuple }}"  data-field="{{ field }}" value="{{ muonValue }}" />
                </td>
              {{ /each }}

            {{ /let }}
          </tr>

        {{ /each }}
      </tbody>
    </table>
</template>
Template.table.onCreated(function () {

  this.listParams = [
    'attack',    'decay',       'sustain',
    'release',   'detune',      'voice_one',
    'voice_two', 'voice_three', 'voice_four',
  ];

  // get your data
  const muons_origine = [
    { "pt": 1.5, "eta": -1.5, "phi": 3.7, "charge": -1 },
    { "pt": 0.2, "eta": -2.3, "phi": -1.3, "charge": 1 },
  ];

  // transform your data by creating tuples for each label/value
  // [ [ 'labelA', labelA_value ], [ 'labelB', labelB_value ], ... ]
  this.muons     = [];
  const template = this;

  muons_origine.forEach(function ( obj ) {
    Object.keys( obj ).forEach(function ( key ) {
      template.muons.push( [ key, obj[ key ] ] );
    });
  });
});

Template.table.helpers({

  listParams: () => Template.instance().listParams,

  allMuons: () => Template.instance().muons,

  getMuonLabel: ( tuple ) => tuple[ 0 ],
  getMuonValue: ( tuple ) => tuple[ 1 ],

});
1 Like

That looks really niceā€¦ :slight_smile: Is it possible also to do this, as I am loading different muon data (with same structure) interactively so I thought this will work.
Template.ipsosboard.onCreated(function () {

   this.listParams = [
            'attack',    'decay',       'sustain',
            'release',   'detune',      'voice_one',
            'voice_two', 'voice_three', 'voice_four',
        ];

               // transform your data by creating tuples for each label/value
        // [ [ 'labelA', labelA_value ], [ 'labelB', labelB_value ], ... ]
        this.muons     = [];
        const template = this;

        function selectEvent(event) {
        events[event].muons.forEach(function ( obj ) {
            Object.keys( obj ).forEach(function ( key ) {
                template.muons.push( [ key, obj[ key ] ] );
            });
        });
        };
        Template.instance().selectEvent = 'event_one'; // as a default.
    });

And then loading different value data using this:

 Template.ipsosboard.events({
        "change #event-select": function(event, template) {
            var selectedEvent = $( event.currentTarget ).val();
            Template.instance().selectEvent = selectedEvent; /*not working, many another way to call my selectEvent function*/
        }

Thanks a lot, if this is fixed then you saved me lots of timeā€¦

Yes you can :slight_smile: , you just need to have a reactive data source for building your ā€œtuples of muonsā€ and update this data source each time a new option is selected in the <select>.

Then, a small update of the helper returning the data to the HTML will be enough. (as helpers are reactive too, each time activeEvent will be changed, the helper will rerun)

  <!-- Just after the tag </table> -->
  <select id="event-select">
    <option value="event_one" selected>Load data #1</option>
    <option value="event_two">Load data #2</option>
    <option value="event_three">Load data #3</option>
  </select>
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';

import './main.html';


const EVENTS = {
  event_one  : [
    { "pt": 11.1, "eta": 11.2, "phi": 11.3, "charge": 11.4 },
    { "pt": 11.5, "eta": 11.6, "phi": 11.7, "charge": 11.8 },
  ],
  event_two  : [
    { "pt": 22.1, "eta": 22.2, "phi": 22.3, "charge": 22.4 },
    { "pt": 22.5, "eta": 22.6, "phi": 22.7, "charge": 22.8 },
  ],
  event_three: [
    { "pt": 33.1, "eta": 33.2, "phi": 33.3, "charge": 33.4 },
    { "pt": 33.5, "eta": 33.6, "phi": 33.7, "charge": 33.8 },
  ],
};


const selectEvent = function selectEvent( eventName ) {

  const eventMuons = EVENTS[ eventName ] || [];
  const muons = [];

  eventMuons.forEach(function ( obj ) {
    Object.keys( obj ).forEach(function ( key ) {
      muons.push( [ key, obj[ key ] ] );
    });
  });

  return muons;
};


Template.table.onCreated(function () {

  this.listParams = [
    'attack',    'decay',       'sustain',
    'release',   'detune',      'voice_one',
    'voice_two', 'voice_three', 'voice_four',
  ];

  // transform your data by creating tuples for each label/value
  // [ [ 'labelA', labelA_value ], [ 'labelB', labelB_value ], ... ]
  this.muons     = [];
  const template = this;

  this.activeEvent = new ReactiveVar( 'event_one' );
});

Template.table.helpers({

  listParams: () => Template.instance().listParams,

  allMuons() {
    const event = Template.instance().activeEvent.get();
    return selectEvent( event );
  },

  getMuonLabel: ( tuple ) => tuple[ 0 ],
  getMuonValue: ( tuple ) => tuple[ 1 ],
});

Template.table.events({

  'change #event-select'( event, tplInstance ) {
    const selectedElem  = event.currentTarget.selectedOptions[ 0 ];

    tplInstance.activeEvent.set( selectedElem.value );
  },

});
1 Like

Thanks a lot @borntodev this works great. One thing I am not sure as this is new to me, assuming there are other properties in the file I am getting the data like time and date, how can I retrieve it dynamically too?
Sorry, I didnā€™t mention this in my previous example of my data I was focusing on the muon values. I would like to print dynamically everytime I change the data the date and time and the event number on my HTML. Here is how my file looks like. BTW, on its current state of the program, what is the best way to share the selected data within the system, say I want to visualise the data from the table, do I need to do another reactiveVar or I can use the current one?

event_one: {
    event:123456,
    date_time:"some string",
        muons:[{"pt":0,"eta":1,"phi":2,"charge":3},{"pt":4,"eta":5,"phi":6,"charge":7}]

so it does works for you konvas? thanks, by the way, for sharing the injectable steroids part.

Sure. You could just have to change the way selectEvent() returns the data (due to the new structure) and create two new helpers returning the data you want in the same way as allMuons().

But I was thinking about another way to structure all of this. Save the new ā€œtransformed dataā€ in the template, and then make the ā€œcurrent eventā€ as a ReactiveVar too to simplify helpers and to get rid off selectEvent() and some duplicated code.
I also made the generation of <option> dynamic, depending on the events data.

I prefer this version. Tell me what do you think :slight_smile: :

<template name="table">
  <table>
    <thead>
      <tr>
        <th> - </th>
        {{ #each field in listParams }}
          <th>{{ field }}</th>
        {{ /each }}
      </tr>
    </thead>

    <tbody>
      {{ #each tupleMuon in getEventMuons }}
      <tr>
        {{ #let
          indexTuple=@index
          muonLabel=(getMuonLabel tupleMuon)
          muonValue=(getMuonValue tupleMuon)
        }}

          <td><b>{{ muonLabel }}</b></td>

          {{ #each field in listParams }}
          <td>
            {{ muonValue }}
            <input type="radio" name="{{ field }}" class="{{ field }}" value="{{ muonValue }}"
              data-item="{{ muonLabel }}-{{ field }}-{{ indexTuple }}" data-field="{{ field }}" />
          </td>
          {{ /each }}

        {{ /let }}
      </tr>
      {{ /each }}
    </tbody>
  </table>

  <div>Event number : {{ getEventNumber }}</div>
  <div>Event date : {{ getEventData }}</div>

  <select id="event-select">
    {{ #each event in allEvents }}
      <option value="{{ event.name }}">Load data for event #{{ event.number }}</option>
    {{ /each }}
  </select>
</template>
const EVENTS = {
  event_one: {
    number   : 1111,
    date_time: 'data time event #1',
    muons    : [
      { "pt": 11.1, "eta": 11.2, "phi": 11.3, "charge": 11.4 },
      { "pt": 11.5, "eta": 11.6, "phi": 11.7, "charge": 11.8 },
    ],
  },
  event_two: {
    number   : 2222,
    date_time: 'data time event #2',
    muons    : [
      { "pt": 22.1, "eta": 22.2, "phi": 22.3, "charge": 22.4 },
      { "pt": 22.5, "eta": 22.6, "phi": 22.7, "charge": 22.8 },
    ],
  },
  event_three: {
    number   : 3333,
    date_time: 'data time event #3',
    muons    : [
      { "pt": 33.1, "eta": 33.2, "phi": 33.3, "charge": 33.4 },
      { "pt": 33.5, "eta": 33.6, "phi": 33.7, "charge": 33.8 },
    ],
  },
};


const transformData = function transformData( events ) {

  const newDataObject = {};

  for (let eventName in events ) {

    const currentEvent = events[ eventName ];

    // transform muons by creating tuples
    const transformedMuons = [];

    currentEvent.muons.forEach(function ( obj ) {
      Object.keys( obj ).forEach(function ( key ) {
        transformedMuons.push( [ key, obj[ key ] ] );
      });
    });

    // copy event object into a new object,
    // and override 'muons' key with our transformed muons data
    newDataObject[ eventName ] = Object.assign( {}, currentEvent, {
      muons: transformedMuons,
    });
  }

  return newDataObject;
};


Template.table.onCreated(function () {

  this.listParams = [
    'attack',    'decay',       'sustain',
    'release',   'detune',      'voice_one',
    'voice_two', 'voice_three', 'voice_four',
  ];

  // transform your data by creating tuples for each label/value
  // [ [ 'labelA', labelA_value ], [ 'labelB', labelB_value ], ... ]
  this.events = transformData( EVENTS );

  // default event name not hard-coded
  const defaultEventName = Object.keys( this.events )[ 0 ];
  this.activeEventName   = new ReactiveVar( defaultEventName );
  this.activeEvent       = new ReactiveVar();

  this.autorun(() => {
    // this will rerun each time its dependencies are changed (the ReactiveVar)
    const eventName   = this.activeEventName.get();
    const linkedEvent = this.events[ eventName ];
    this.activeEvent.set( linkedEvent );
  });
});

Template.table.helpers({

  listParams: () => Template.instance().listParams,

  getEventMuons : () => Template.instance().activeEvent.get().muons,
  getEventNumber: () => Template.instance().activeEvent.get().number,
  getEventData  : () => Template.instance().activeEvent.get().date_time,

  getMuonLabel: ( tuple ) => tuple[ 0 ],
  getMuonValue: ( tuple ) => tuple[ 1 ],

  /**
   * {{ #each }} can't loop over an Object
   * http://blazejs.org/api/spacebars.html#Each
   *
   * @returns {Array}
   */
  allEvents() {
    const eventsObj = Template.instance().events;
    const eventsArr = [];

    for (let eventName in eventsObj) {
      eventsArr.push({
        name  : eventName,
        number: eventsObj[ eventName ].number,
      });
    }

    return eventsArr;
  },

});

Template.table.events({

  'change #event-select'( event, tplInstance ) {
    const selectedElem = event.currentTarget.selectedOptions[ 0 ];

    tplInstance.activeEventName.set( selectedElem.value );
  },

});

I think it looks great, I will have a thorough look on it, and come back if I have any other question, if thatā€™s alright with you. Thanks a lotā€¦

You are welcome. It was easier to help you here compared to the Facebook Group :wink: (yeah I remember you on ā€œLearn MeteorJS properlyā€).

Yeah, I remember now. Just a quickie in the previous version the js file was calling the HTML, havenā€™t seen this before, could you maybe tell me more about this?