I'm trying to use Ember observers with EmberJS Octane latest version (4.1.0), but it does not seem to work.
Here is what I'm trying to achieve :
export default class SummaryService extends Service {
@service store;
@service authentication;
@reads('authentication.userId') userId;
@tracked weekPosts;
@tracked monthPosts;
@observer('userId')
loadPosts() {
this._loadPosts(this.userId);
}
_loadPosts(userId) {
this.store.query('posts', { filter: { 'current-week': 1, 'user-id': userId } })
.then((posts) => {
this.set('weekPosts', posts);
});
this.store.query('posts', { filter: { 'current-month': 1, 'user-id': userId } })
.then((posts) => {
this.set('monthPosts', posts);
});
}
}
=> The syntax is invalid.
I also tried :
@observer('userId', function() {
this._loadPosts();
});
=> The observer is indeed called, but this is undefined.
I also tried :
init() {
super.init(...arguments);
this.addObserver('currentUserId', this, '_loadPosts');
}
=> But this one does not call any method (even with inline method definition).
Finally, my last attempt was to use @computed properties for weekPosts and monthPosts instead, like this :
export default class SummaryService extends Service {
/* ... */
@computed('userId')
get weekPosts() {
return this.store.query('posts', { filter: { 'current-week': 1 } })
.then((posts) => { return posts; });
}
}
=> But it always returns a Promise, so I can't call .reduce on it from a computed property used by a Component :
export default class SummaryComponent extends Component {
@computed('weekPosts')
get weekPostsViewsCount() {
return this.weekPosts.reduce((sum, post) => { return sum + post.viewCount });
}
}
I finally got something working pretty ugly using an ArrayProxy.extend(PromiseProxyMixin) returned by the weekPosts computed property, but I'm definitely not happy with this for the following reasons :
- So much code for such a simple thing
- Everything (component, template) which uses the
weekPostshas to make sure the promise is fulfilled before working with it - The promise is an implementation detail of the service and should not be visible in any way out of it
Thanks !
Observers won't work for what you want to do -- since it looks like you want to reactively re-fetch data (using ember-data) based on when
userIdchanges, I have a library suggestion:With this library, we can replace most of your service with this:
The advantage here is that you also have the ability to manage error/loading/etc states.
This uses a technique / pattern called "Derived state", where instead of performing actions, or reacting to changes, or interacting withe lifecycles, you instead define how data is derived from other data.
In this case, we have known data, the
userId, and we want to derive queries, usingqueryfrom ember-data-resources, also uses derived state to provide the following api:this._weekPosts.records.error.isLoading.isSuccess.isError.hasRunWhich then allows you to define other
getters which derive data,weekPosts,isLoading, etc.Derived state is much easier to debug than observer code -- and it's lazy, so if you don't access data/getters/etc, that data is not calculated.