Ember.js returning to computed property within a callback

I have a computed property that checks if a user has already liked a post:

  likeable: Ember.computed(function () {
    const currentUser = this.get('auth.credentials.id');
    const post = this.get('post.id');

    // Get likes and filter by post and current user

    this.get('store').query('like', {
      filter: {
         user_id: currentUser,
         post_id: post
       }
    }).then(function(like) {

      // Check if any likes were returned

      if (like.get('length') != 0) {
        console.log('length is not 0')
        return false
      } else if (like.get('length') === 0) {
        console.log('length is 0')
        return true
      }
    })
  })

Its being called in my template file like this:

{{#if likeable}}
  <button class="like-button" {{action 'addLike' post}}>Like This Post</button>
{{/if}}

I'm able to see the console.logs and I know the correct records are being retrieved. However, the issue is that the value of likeable is not being updated and the button is never being displayed. I'm pretty sure this is because my conditional is within a callback, and not returning true to the actual computed property. Is there a way around this?

6 Answers

  1. Alan- Reply

    2019-11-13

    As alizahid already ponted out, your computed property has no return value, the return statements in your internal promise only return to the promise chain and don't reach the outside of your property.

    Try to return the promise result from your property with return:

      likeable: Ember.computed(function () {
        const currentUser = this.get('auth.credentials.id');
        const post = this.get('post.id');
    
        // Get likes and filter by post and current user
    
        return this.get('store').query('like', {
          filter: {
             user_id: currentUser,
             post_id: post
           }
        }).then(function(like) {
    
          // Check if any likes were returned
    
          if (like.get('length') != 0) {
            console.log('length is not 0')
            return false
          } else if (like.get('length') === 0) {
            console.log('length is 0')
            return true
          }
        })
      })
    

    If this doesn't help, you may have to wrap the promise chain into a DS.PromiseObject (see docs), this is how ember-data handles promise returns to templates.

      likeable: Ember.computed(function () {
        const currentUser = this.get('auth.credentials.id');
        const post = this.get('post.id');
    
        // Get likes and filter by post and current user
    
        return DS.PromiseObject.create({
          promise: this.get('store').query('like', {
            filter: {
              user_id: currentUser,
              post_id: post
            }
          }).then(function (like) {
    
            // Check if any likes were returned
    
            if (like.get('length') != 0) {
              console.log('length is not 0')
              return false
            } else if (like.get('length') === 0) {
              console.log('length is 0')
              return true
            }
          })
        });
      })
    
  2. Albert- Reply

    2019-11-13

    Have you tried console.logging likeable after this function has run? I wonder if it's an asynch issue. You may have to get the post again after you set likeable to true or false, or move this logic to the backend serializer and set the value of likeable there.

  3. Andrew- Reply

    2019-11-13

    The booleans you're returning are going nowhere. You need to return the promise for the boolean to resolve.

    You can also try an async version, but I haven't tested and it may not work.

    likeable: Ember.computed(async function() {
      const post_id = this.get('post.id')
      const user_id = this.get('auth.credentials.id')
    
      // get likes and filter by post and current user
      const like = await this.get('store').query('like', {
        filter: {
          post_id,
          user_id
        }
      })
    
      return like.get('length') === 0
    })
    
  4. Andy- Reply

    2019-11-13

    You should not account asyncronous behaviour in computed properties, Ember manages all the computed through a run loop, which means it will not wait a computed to resolve a promise.

    The way to handle this is making the async call and set a property has a side effect, perhaps during the didInsertElement hook:

    import Component from '@ember/component';
    export default Component.extend({
        didInsertElement() {
            this._super(...arguments);
            this.fetchLikesFromServer().then(like => {
                this.set('likeable', like.get('length') === 0);
            });
        },
    });
    

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>