Dynamic computes

Is there such as thing as dynamic computed properties? I think a bit of code would make more sense.

Let’s say there is an object like this:

const fruitVendor = {
    weeks: {
        '2018-10-07': {
            available: ['apples', 'bananas', 'pears']
        },
        ...
    },
    ...
}

The data will always be in this format: fruitVendor.weeks.<YYYY-MM-DD>.available.

There could be a lot weeks, but I may only care about a computed property on this week. Right now I need to do something like this:

myComputedProperty: computed('fruitVendor.weeks', ...)

Whereas, ideally, I had something like this:

myComputedProperty: computed('fruitVendor.weeks.<getThisWeek()>.available', ...)

How do you handle something like this?

You could use another computed with a second dependent key on the week of interest?

@computed('weeks', 'selectedWeek')
get someData() {
  return this.weeks[this.selectedWeek];
}

Thanks. I will try this.

Sometimes in these cases, things get simpler if you replace some of your computed properties with helpers.

1 Like

This is quite easy to do in plain ember once you get your head around it:

There’s also an addon but I haven’t tried it:

1 Like

Thanks @alexspeller. While I didn’t do it exactly like in the linked article, I used defineProperty in init so it is something like this:

    init() {
        this._super(...arguments);
        const currentWeek = this.get('week');
        defineProperty(this, '_availableCurrentWeek', computed.oneWay(`fruitVendor.weeks.${currentWeek}.available`));
    },

I would suggest not using “defineProperty”.

In docs you can find sentence:

“NOTE: This is a low-level method used by other parts of the API. You almost never want to call this method directly.”

But you cen easily set property

init() {
        this._super(...arguments);
        const currentWeek = this.get('week');
        set(this, '_availableCurrentWeek', computed.oneWay(`fruitVendor.weeks.${currentWeek}.available`));
    },
1 Like

The use of either defineProperty or set a computed was removed. This example no longer works.

Since I already resurrected this thread I should at least followup with a nifty solution to my dilemma regarding dynamic dependencies. I was inspired by the link given by @alexspeller above.

I had a need to have a POJO passed into a component and then have the component glean dependencies from the object’s keys.

  state: reads('stateManager.state'),
  isAllSelected: equal('state', STATES.ALL),
  isNoneSelected: equal('state', STATES.NONE),
  isSomeSelected: equal('state', STATES.SOME),

  stateManager: computed('selected', function() {
    let properties = Object.keys(this.selected);
    return EmberObject.extend({
      properties,
      state: computed(`selected.{${properties.join(',')}}`, function() {
        let selections = Object.values(this.selected);
        if (selections.every(x => x)) {
          return STATES.ALL;
        } else if (selections.every(x => !x)) {
          return STATES.NONE;
        } else {
          return STATES.SOME;
        }
      })
    }).create({ selected: this.selected });
  }),

The caveat is that this will not detect new keys or deleted keys but in my case this was fine as the selected POJO would never change like that. I needed only to track the values of those keys but the keys were not known until the component was initialized. This works for that use case.