Computed property to disable/enable button


#1

I’d like to enable or disable a button depending on the number of displayed items.

Here is the route:

# routes/shop_languages.js
model() {
    return RSVP.hash({
      shopLanguages: this.store.query('shop-language', { shop_id: this.get('currentShop.shop').get('id')}),
      languages: this.store.findAll('language')
    });
  },

So in a template I display the list of languages associated to the shop:

# templates/shop_langauges.hbs

<ul class="list-group list-group-flush">
      {{#each model.shopLanguages as |lang|}}
        <li class="list-group-item">
          {{lang.tag}}
          <button type="button" class="btn btn-danger btn-sm" disabled={{isDeleteButtonDisabled}} {{action "deleteLanguage" lang}}>
            <span class="oi oi-trash"></span>
          </button>
        </li>
      {{/each}}
    </ul>

I hoped to be able to call the computed property defined s follows in shop_languages.js controller:

# controllers/shop_languages.js

  isDeleteButtonDisabled: computed('model.shopLanguages', function() {
    let langs = this.get('model.shopLanguages');
    console.log('langs: ' + langs.get('length'));
    return langs.get('length') < 2;
  }),

The console displays correctly the number of languages. But it does not work:

  • the delete button is always displayed
  • when I change for another shop, there is nothing displayed in the console, as if the CP has never been called.

What is wrong with that ? Is the syntax is correct ? I’ve just deleted 3 extra languages (there were 4 ones), came back to the shop and, - strange, -> the button was disabled. Why so ? Caching os smth else ? Thank you.


#2

Computed property observers can be tricky, especially when dealing with arrays. What you have here:

isDeleteButtonDisabled: computed('model.shopLanguages', function() {

Is telling Ember “look at the model.shopLanguages object” which is a record array. However Ember doesn’t necessarily infer that it needs to look at the length specifically. You should have the following instead:

isDeleteButtonDisabled: computed('model.shopLanguages.length', function() {

One easy way to help think about this is that if you reference a property in the CP, it likely needs to be watched by the CP. In this case you get model.shopLanguages, but then in the next line you also get the length, so you should include the length in your observer path.


#3

Really grateful ! Thanks a lot for your response, frankly I didn’t believe that one could pass in model.shopLanguages.length as an observed stuff to the CP. Thank you!


#4

An alternate to observing length is []: https://guides.emberjs.com/v3.0.0/object-model/computed-properties-and-aggregate-data/#toc_code-code

isDeleteButtonDisabled: computed('model.shopLanguages.[]', function() {}

#5

Thank you for your response. I’ll take a note of that tip as well.


#6

Also, you should be able to use the lt computed macro: https://www.emberjs.com/api/ember/3.0/functions/@ember%2Fobject%2Fcomputed/lt

isDeleteButtonDisabled: lt('model.shopLanguages.length', 2)

#7

Wow, thank you very much, @Panman8201, I’m really glad to know that, - every time it is like a discovery :). There is still so much to know in Ember ecosystem.