Help with computed property?

Hello i have a question about computed properties and DS.hasMany relation so when you make this relation checklistItems: DS.hasMany("projects/checklist-item"), it goes directly to the backend and get requested data i wanna add something to this request to check the current project stage if [stage 1 , stage 2 …] cause each stage has specific items

it is possible to do something like this ? not working !

  checklistItems: DS.hasMany("projects/checklist-item"),

  // checklistItems: computed('currentStage.name', function(){
  //   const stage = this.get("currentStage.name");
  //   const params = stage ? {stage} : {};
  //   const stageItems = this.store.query("projects/checklist-item", params);
  //   if(stageItems != null){
  //     return stageItems
  //   }
  // }),

You can do something like that though I wouldn’t necessarily recommend it and I’ll explain why.

But before I get there, let’s get this CP cleaned up a tiny bit… this part would be slightly problematic:

 const stageItems = this.store.query("projects/checklist-item", params);
 if(stageItems != null){
   return stageItems
 }

The store.query method always returns a proxy, so it would never be null, so this if condition would always evaluate to true. It would also return the proxy from the CP immediately, before the proxy has resolved, so any code that uses this CP would have to be know how to handle that properly.

Anyway… back to why I wouldn’t necessarily recommend using a CP in place of a relationship unless you really know you want to… it will work fine and function somewhat the same way as the relationship would, but there are some subtle differences. For one you can’t arbitrarily reload the relationship. A hasMany relationship can be reloaded at any time but a computed must rely on whatever it is observing to trigger a recompute. Secondly, if the checklist items will be mutated (e.g. they’re not read only) you will probably encounter some issues with the “relationship” updating properly when you try to push/remove records. A hasMany is built to support this but a query result is not (basically: why would you mutate the results of a query? it doesn’t really make sense). If it’s read-only in that the number of checklist items doesn’t need to change maybe that would work fine.

Anyway, there are several other solutions you could explore. The first is loading all checklist items and filtering them on the client by stage. You could have the relationship as well as a CP like the one you have above, e.g.

checklistItems: DS.hasMany("projects/checklist-item"),
currentChecklistItems: computed('checklistItems.@each.stage', 'currentStage.name', function() {
  return this.checklistItems.filterBy('stage', this.currentStage.name);
})

The downside to this is the requests are sub-optimal (you’re fetching all checklist items at once).

Another option would be to override your adapter’s findHasMany method to add query params as appropriate. This would be more similar to your CP solution above, but would still use the hasMany relationship. The main thing you have to be careful of there is that you are careful to reload the relationship when the stage changes, otherwise you’ll end up with stale data. The benefit is that you get a regular hasMany relationship.

Yet another option is to have your backend provide different checklistItems (depending on how your relationships are set up in your API payloads). If you’re serializing ids that would mean simply reloading the project(?) model. If you’re using links you could update the links to something with a filter, or if you’re using “nested endpoint” links you could just swap out the response that comes from those endpoints.

The last option, off the top of my head, is just not using a relationship at all (since the relationship itself is sort of transient) and just querying for the checklistItems when and where they are needed.

There are tradeoffs with any of these solutions but hopefully that helps at least provide an overview. Let me know if you need any further clarification on anything!

1 Like