How to force Apollo Client to use cached data - apollo

We have an application that initially load a list of widgets:
query Widgets() {
widgets() {
...Widgets
}
}
fragment Widgets on Widgets {
name
description
rootWidget
widgets {
...WidgetInterface
}
}
fragment WidgetInterface on WidgetInterface {
id
name
description
type
}
later on I render this widgets, where every react component is wrapped with another graphql call to get the data for a single widget. As we fetch this data initially I would expect apollo get the data from local store, but it always make the server call
#import '../fragments/WidgetInterface.graphql'
query Widget($id: ID!) {
widgetDetails(id: $id) {
...WidgetInterface
}
}
So is there away to check why apollo not uses the cached ones?

Apollo caches your query results by query. The reason it's grabbing the data from the server instead of the cache is that the first time you render your component, you've never made a widgetDetails query, only a widgets one.
If you want to avoid making the widgetDetails query, you can set up your component to use the widgets query instead and just filter the results by id yourself. Something like:
graphql(WIDGETS_QUERY, {
props: ({data, ownProps}) => ({ widget: data.widgets.filter(w => w === ownProps.widgetId) })
})(MyComponent)

Related

Continuous data update via pouchdb query

i have a pouchdb database with a number of views setup that i query whenever i need data. I am using observables to handle the querying. However i have to refresh the interface to view any data changes in the database. Is there any way i can have these data changes read directly by the observable ? My code is as:-
home.ts
this.postsService.getPosts().subscribe((posts) => {
this.posts = posts.rows.map(row => {
console.log(row.value);
return row.value;
});
});
posts.ts
getPosts(): Observable<any> {
return Observable.fromPromise(this.db.query('app/inputs'));
}
You can use use db.changes with your view, so that you'll only get events for view related changes:
db.changes({
filter: '_view',
view: 'app/inputs',
live: true,
since: 'now',
include_docs:true
}).on('change', (change) => { this.handleChange(change); });
See the filtered changes section on PouchDB docs for a more detailed explanation on this.

How do I refetch a query outside of its wrapped component?

I have two screens:
Screen1: Results
Screen2: Edit Filters
When I edit the filters on Screen2 and press back, I would like to refetch the query on Screen1 (with the newly built filter string variable). Editing the filters doesn't use a mutation or fire any Redux actions (I'm storing the users search filters/preferences in localStorage/AsyncStorage instead of a database, so no mutation). I'm merely changing the local state of the form and use that to build a filter string that I want to pass to a certain query on Screen1. I have access to the filter string on both screens if that helps.
It seems like refetch() is limited to the component its query wraps http://dev.apollodata.com/react/receiving-updates.html#Refetch so how would I re-run the query from a different screen?
I tried putting the same query on both Screen1 and Screen2, then calling the refetch on Screen2, and although the query works and gets the new data on Screen2, the same name query doesn't update on Screen1 where I actually need it. Isn't it supposed to if they have the same name? (but the filters variable changed)
If I am just designing this incorrectly and there is an easier way to do it, please let me know. I expect that if I have 2 screens, put the same query on both of them, and refetch one of the queries with a new filters variable, then the refetch should happen in both places, but it's currently treating them individually.
I did the same thing here. The scenario:
- I choose a peer to filter some messages.
- I keep the peerId into redux
- I make both components (the filter and the list) dependent on that redux value.
Like this:
1 - To put that filter value on redux (and to grab it back):
import { compose, graphql } from 'react-apollo'
import { connect } from 'react-redux';
...
export default compose(
connect(
(state,ownProps) => ({
selectedMessages: state.messages.selectedMessages,
peerId: state.messages.peerId
}),
(dispatch) => ({
clearSelection: () => dispatch(clearSelection()),
setPeer: (peerId) => dispatch(setPeer(peerId))
})
),
graphql(
PEERS_QUERY,
...
when you call connect first (using compose), before you call a graphql wrapper, or outside that wrapper, you will have peerId available as a prop on your graphql wrapper, so you can use it to filter your query:
export default compose(
connect(
(state,ownProps) => {
return {
peerId: state.messages.peerId,
selectedMessages: state.messages.selectedMessages
}
},
(dispatch) => ({
toggleMessage(messageId) {
dispatch(toggleMessage(messageId));
}
})
),
graphql( // peerId is available here because this is wrapped by connect
MESSAGES_QUERY,
{
options: ({peerId}) => ({variables:{peerId:peerId}}),
skip: (ownProps) => ownProps.peerId === '',
props: ({
...
...
...
)(MessageList);

Ember computed dynamic property path

Ember CLI + Ember 2.1
I have a child component that is observing a key/value hash on a parent component that keeps track of "required" objects.
requiredObjects: {
object1_id: true,
object2_id: false,
...
}
These objects are records retrieved from Ember-Data. Whether or not an object is required is a transient property relevant only to the component, which is why I don't use a 'required' property on the object itself.
There are several child components that display one or more of these objects using different data visualizations, and each of these has yet another child component with controls for requiring the object.
The control itself sends actions up to the parent controller, and receives the requiredList from the parent controller. What I'd like to do is this...
isRequired: Ember.computed('player', `requiredList.${this.get('player.id')}`, {
get(key) {
return this.get(`requiredList.${this.get('player.id')}`);
},
set(key, value) {
this.set(`requiredList.${this.get('player.id')}`, value);
return value;
}
}),
However, that doesn't work. Essentially, I need to observe when a key in requiredList that matches player.id is updated, but I don't know the id until the component is initialized and 'player' is set. Is there a way to do this?
I thought about using an array instead of a key/value hash, but that requires every control to loop over the array each time membership changes and see whether its player is in the requiredList. That seems pretty inefficient, given that there are usually a couple hundred players in the list, each with 2 or 3 controls currently rendered in different parts of the page.
Update
So moving it into init() fixes the problem, though I have no idea why.
init() {
this._super(...arguments);
this.set('isRequired', Ember.computed('player', `requiredList.${this.get('player.id')}`, {
get(key) {
return this.get(`requiredList.${this.get('player.id')}`);
},
set(key, value) {
this.set(`requiredList.${this.get('player.id')}`, value);
return value;
}
}));
},
The templated string evaluates with the current context.
In the first case, the context is actually the module itself, which by the spec is undefined, so requiredList.${this.get('player.id')} => requiredList.undefined.
In the second case you're inside the init() function, so this is the object.

Ember: model.set vs store.update

I'm curious as to how model.set('propertyName', propertyValue) differs from store.update('modelType', {id: 1, propertyName: propertyValue})
Some context: In my app you click a button to run a background task, once that is completed my app will use ajax to fetch the data from the database. Once the data is updated, the component that displays the data will update and show the new data.
I tried doing the following in my controller when the background task is complete:
var self = this;
return Ember.$.get('someURL').done(function(data) {
// This deserializes the data so all attribute names are camelCase
data = deserializeData(data)
// At this point, data = {id: 1, attrOne: "valueOne", attrTwo: "valueTwo"}
self.store.update('modelName', data);
});
Unfortunately, this doesnt trigger any updates in the view, so my component doesn't reflect the new data. In fact when I try doing this.get('model.attrOne'), it returns the old data for the model.
Then I tried the following:
var self = this;
this.store.findById('modelName', modelId).then(function(model) {
return Ember.$.get('someURL').done(function(data) {
// This deserializes the data so all attribute names are camelCase
data = deserializeData(data)
// At this point, data = {id: 1, attrOne: "valueOne", attrTwo: "valueTwo"}
model.setProperties(data)
});
});
Now when I do this, everything works as expected, the view updates with the new data properly.
What I don't understand is why does the latter work instead and not the former?

How do I observe *all* property changes on a model object?

I have a model built from a JSON object.
// extend the json model to get all props
App.Model = Ember.Object.extend(window.jsonModel);
I want to automatically save the model when anything is updated. Is there any way I can add an observer to the whole model?
EDIT: // adding the solution I currently go
For now I do:
// XXX Can't be right
for (var prop in window.jsonModel) {
if (window.jsonModel.hasOwnProperty(prop)) {
App.model.addObserver(prop, scheduleSave);
}
}
This is a large form, which means I'm adding tons of observers – it seems so inefficient.
A firebug breakpoint at Ember.sendEvent() reveals that there are events called App.model.lastName:change being sent. I could hack in an intercept there, but was hoping for an official way.
You can bind to isDirty property of subclass of DS.Model. The isDirty changes from false to true when one of model properties changes. It will not serve well for all cases because it changes only once until reset or committed, but for your case -
I want to automatically save the model when anything is updated. Is there any way I can add an observer to the whole model?
it may work fine.
From the article:
autosave: function(){
this.save();
}.observes('attributes'),
save: function(){
var self = this,
url = this.get('isNew') ? '/todos.json' : '/todos/'+this.get('id')+'.json',
method = this.get('isNew') ? 'POST' : 'PUT';
$.ajax(url, {
type: 'POST',
// _method is used by Rails to spoof HTTP methods not supported by all browsers
data: { todo: this.get('attributes'), _method: method },
// Sometimes Rails returns an empty string that blows up as JSON
dataType: 'text',
success: function(data, response) {
data = $.trim(data);
if (data) { data = JSON.parse(data); }
if (self.get('isNew')) { self.set('id', data['todo']['id']); }
}
});
},
isNew: function(){
return !this.get('id');
}.property('id').cacheable(),
I had the same requirement, and not finding a suitable answer, I implemented one.
Try this: https://gist.github.com/4279559
Essentially, the object you want to observe all the properties of MUST be a mixed of Ember.Stalkable. You can observe the properties of that object as 'item.#properties' (or, if you bake observers directly on the Stalkable, '#properties' alone works. "#ownProperties", "#initProperties" and "#prototypeProperties" also work, and refer to (properties that are unique to an instance and not defined on any prototype), (properties that are defined as part of the create() invocation), and (properties that are defined as part of the class definition).
In your observers, if you want to know what properties changed and invoked the handler, the property "modifiedProperties", an array, will be available with the names of the changed properties.
I created a virtual property _anyProperty that can be used as a dependent key:
import Ember from 'ember';
Ember.Object.reopen({
// Virtual property for dependencies on any property changing
_anyPropertyName: '_anyProperty',
_anyProperty: null,
propertyWillChange(keyName) {
if (keyName !== this._anyPropertyName) {
this._super(this._anyPropertyName);
}
return this._super(keyName);
},
propertyDidChange(keyName) {
if (keyName !== this._anyPropertyName) {
this._super(this._anyPropertyName);
}
return this._super(keyName);
}
});