orbitjs / ember-orbit

Ember.js data layer built with Orbit.js
MIT License
320 stars 42 forks source link

LiveQuery lifetimes #311

Open ef4 opened 4 years ago

ef4 commented 4 years ago

I see that LiveQuery uses the ember-destroyable-polyfill, and that all queries get destroyed when the cache itself gets destroyed.

But it seems likely that many live queries have a lifetime shorter than that, like being associated with a component. In my own experiments, I have a utility like this:

// app/lib/query.js
import { associateDestroyableChild } from '@ember/destroyable';
import { getOwner } from '@ember/application';

export function liveQuery(parent, query) {
  let records = getOwner(parent).lookup('service:store').cache.liveQuery(query);
  associateDestroyableChild(parent, records);
  return records;
}

That I use like this:

// app/components/my-component.js
import { liveQuery } from '../lib/query';

export default class extends Component {
  observations = liveQuery(this, (qb) =>
    qb.findRecords('observation').sort('-time')
  );
}

This ensures that the live query is destroyed whenever its parent component is destroyed.

Is there a better built-in solution? Should something like this utility be added to ember-orbit?

dgeb commented 4 years ago

@ef4 I definitely see where you're coming from and would be glad to integrate a similar solution into ember-orbit. I'd prefer to not hard-link the store service within a general solution, since there's often more than one store at any given time in an orbit application, especially when using forks.

We should consider incorporating the ability to create a dependent relationship in LiveQuery itself. I'm having trouble coming up with the perfect method name - perhaps associateParent(parent) or dependentUpon(parent)? Regardless, this method could return this (the LiveQuery instance) to make it seamless to use. For instance:

// app/components/my-component.js
import { liveQuery } from '../lib/query';

export default class extends Component {
  @service store;

  observations = this.store.cache.liveQuery((qb) =>
    qb.findRecords('observation').sort('-time')
  ).associateParent(this);
}
tchak commented 4 years ago

My plan is to wait for invokeHelper and then implement some form of @usable pattern. But maybe we can have utility like what you suggesting for now.

tchak commented 4 years ago

The problem I see with suggested utilities is that there no way with them to bind live query to some of component tracked properties. Another advantage of invokeHelper is that result could be of any shape unlike the current custom iterable which works only with collections.

ef4 commented 4 years ago

You can implement this with tracked property support already, without any private API. Here's a demo that does that for a remoteData property. It will correctly recompute if this.args.id changes:

https://github.com/ef4/jan2020-meetup-example/blob/ea56f69ddcb2d51638ef6ce04a4d87e6485a4cf0/app/components/use-decorator-station.js

Now that the destroyables API is shipping in the current Ember release (and polyfilled), you could combine destroyable plus this pattern to do all of this in an addon.