0
votes

I have an app that displays videos and allows users to rate the videos. An average rating and the number of times the video has been rated are displayed beneath it. To calculate these I have added computed properties to each model. The average property relies on the sum and length computed properties.

/*global Ember */
import DS from 'ember-data';

export default DS.Model.extend({
  title: DS.attr('string'),
  url: DS.attr('string'),
  ratings: DS.hasMany('userrating'),
  users: DS.hasMany('user'),
  rated: DS.attr('boolean'),

  // maps through ratings objects and pulls out the rating property
  // returns an array of integers that represent all of one videos ratings
  numRatings: Ember.computed.mapBy('ratings', 'rating'),

  // returns the sum of a videos ratings
  sum: Ember.computed.sum('numRatings'),

  // returns the number of ratings a video has
  length: Ember.computed('numRatings', function () {
    return this.get('numRatings').length;
  }),

  // returns true if the video has only been rated once
  // this is used to determine if '1 user' or '2 users' etc.
  // should be displayed
  one: Ember.computed('length', function () {
    if (this.get('length') === 1) {
      return true;
    }
  }),

  // returns the average rating of a video
  avg: Ember.computed('length', 'sum', function () {
    return Math.round(this.get('sum') / this.get('length'));
  })
});

I have noticed that sometimes the sum is displayed in place of the average. This usually happens only for a second and then the average correctly displays, but every once in a while the average does not display. Today, all videos except for one were displaying correctly, but that one video was displaying ’33/5’ as the average rating.

Why is this happening? Should I not build Ember computed properties to rely on each other? Is this just a problem with the browser being slow? I am loading a bunch of videos and images.

I am pretty new to web development in general and this is my first Ember app.

Thanks!

2
Some improvements: length: computed.readOnly('numRatings.length'), one: computed.equal('length', 1)nem035

2 Answers

0
votes

It's difficult to really know where there may be performance issues without seeing your entire architecture, but I can see the following:

  • You have a relationship with userrating and user models associated with your video model
  • Both your sum and length computed properties are based off of another computed value, which itself is performing a map to pull out rating values out of ratings objects
  • You have another computed watching length, that is purely determining the plurality of the word "user" somewhere else in the code
  • Lastly, you have an avg computed that is watching those other two computed properties as well

Now, again, I can't really provide an exact answer why, but here are a few suggestions to (maybe) lighten to load on your model here.

  1. Eliminate the one computed altogether. You can perform this calculation on the component/controller side if you really want to know if a single one is selected, but you can do something else like Ember.computed.equal('numRatings', 1)
  2. length property can be eliminated in favour of this.get('numRatings.length')
  3. Have avg watch just numRatings so that it will only recalculate if that particular number changes, because you already know that sum will update as well anyway, so might as well reduce the number of observed properties

That said, if it's still acting wonky, you may want to make sure the data found in userrating entries are correct. If it still feels slow or takes its time calculating, you can also try changing mapBy into a plain vanilla JS for loop, as those are significantly faster (albeit less readable) than using Array methods.

Good luck!

0
votes

length: Ember.computed('numRatings', needs to be length: Ember.computed('numRatings.[]', -- you need to observe the length of the array, not the array itself (which will only raise a flag if the value changes as a whole) you can also use the alias property -- Ember.computed.alias('numRatings.length')