Component property not accessible within template


#1

Hello,

happy new year! :slight_smile:

I’m trying to access a components property from its template. Howevever {{debugger}} doesn’t even list the property.

component.js:

import Ember from 'ember';

export default Ember.Component.extend({
  values: {},

  indexQty: Ember.computed('indexes', function() {
    return this.get('indexes').length;
  }),

  setup: function() {
    var indexQty = this.get('indexQty');
    var _self = this;
    
    setInterval(function() {
      var values = {};
      
      for (var i = 0; i < indexQty; i++) {
        values[i] = Math.random();
      }
      
      _self.set('values', values);
    },500);

  }.on('didInsertElement'),
});

template.hbs:

{{#each indexes as |index delta|}}
  <div class="bs-qty-{{indexQty}} bs-element-{{delta}}  ">
    <span class="value">{{get values delta}}</span><span class="unit">{{unit}}</span>
  </div>
{{/each}}

while {{get values delta}} is empty and ‘values’ not visible when inspecting with {{debugger}}.

DEBUG: -------------------------------
DEBUG: Ember      : 2.2.0
DEBUG: Ember Data : 2.2.1
DEBUG: jQuery     : 1.11.3
DEBUG: -------------------------------

Any ideas?


#2

I don’t really know why, but it works when I use a custom helper:

// arrayelement.js
import Ember from 'ember';

export function arrayelement(params) {
  // First parameter is an array/object. Second is the key.
  return params[0][params[1]];
}

export default Ember.Helper.helper(arrayelement);


// template.hbs
{{#each indexes as |index delta|}}
  <div class="bs-qty-{{indexQty}} bs-element-{{delta}}  ">
    <span class="value">{{arrayelement values delta}}</span><span class="unit">{{unit}}</span>
  </div>
{{/each}}

#3

Hello @haggis, happy new year you too. :slightly_smiling: Could you please write a context, what would you like to achieve exactly? maybe there could be a simpler way to implement in ember.


#4

Hi @zoltan,

it’s a component which receives:

  • An object with thousands of properties (int) each representing a measurement value of some channel
  • An array of indexes that tells the component which properties/channels of the object are relevant

The component will read the relevant properties each 500ms and output them via its template. Just imagine a component which always shows the current temperature and barometric pressure out of a soup of thousands other measurements.

The line values[i] = Math.random(); from above will be replaced by something like

values[i] = _self.get('dataObject')[_self.get('indexes')[i]];

The interval just picks the right values from the object and pushes them into the ‘values’ array. The template can then iterate over that array and print its values.


#5

@haggis I created an Ember-Twiddle. Hope I implemented your problem properly.

https://ember-twiddle.com/c09876b5ddcad47e6007?numColumns=1&openFiles=channel-picker.template.hbs%2Cchannel-picker.template.hbs

There is a sample data set in the application controller:

import Ember from 'ember';

export default Ember.Controller.extend({
    values: Ember.Object.create({
    0: 10,
    1: 15,
    2: 7,
    3: 19,
    4: 78,
    5: 3
  }),

  indexes: ['2','4','1','3','5','2','0','1','0','3']
});

values can work without Ember.Object.create(), so it could be a plain javascript object.

However indexes items have to be strings, because this is a limitation of get helper. http://emberjs.com/api/classes/Ember.Templates.helpers.html#method_get

I called the component channel-picker and inserted this line in the application/template.hbs: {{channel-picker values=values indexes=indexes}}

The component javascript is quite simple. I just created an alias, which is not really necessary, because a simple {{indexes.length}} can work in the template.

import Ember from 'ember';

export default Ember.Component.extend({
  
  indexesLength: Ember.computed.alias('indexes.length')
  
});

You approach was quite close. Only difference in the each helper. |delta index| means, that the first value is the content of the given array and the second value is the index.

The content in the delta has to be a string.

Indexes length: {{indexesLength}}

{{#each indexes as |delta index|}}
  <div class="bs-qty-{{indexesLength}} bs-element-{{index}}  ">
    <span class="value">{{get values delta}}</span><span class="unit">{{unit}}</span>
  </div>
{{/each}}

#6

If you really want random data in the values, you can update the application controller with the following code.

import Ember from 'ember';

export default Ember.Controller.extend({

  values: {
    0: 10,
    1: 15,
    2: 7,
    3: 19,
    4: 78,
    5: 3
  },

  indexes: ['2','4','1','3','5','2','0','1','0','3'],

  init() {
    this._super(...arguments);
    Ember.run.later(this, this.generateNewValues, 1000);
  },

  generateNewValues() {
    let values = {};

    for (let i=0; i<10; i++) {
      values[i] = Math.random();
    }

    this.set('values', values);

    Ember.run.later(this, this.generateNewValues, 1000);
  }
});

In Ember.js, instead of setInterval, the Ember.run.later() is the preferred call. You can find more about this function in the API: http://emberjs.com/api/classes/Ember.run.html#method_later

Updated twiddle: https://ember-twiddle.com/c09876b5ddcad47e6007?numColumns=1&openFiles=application.controller.js%2Cchannel-picker.template.hbs


#7

Thanks @zoltan for your effort! It’s working now - and I switched to Ember.run.later and Ember.run.cancel on willDestroyElement.