Not all changes in subscribed docs are loaded - mongodb

I have such a method in my Meteor app:
addLocation: (location, tid) => {
location = LocationSchema.clean(location);
LocationSchema.validate(location);
var lid = Locations.insert(location);
Thoughts.update({_id: tid}, {$push: {locations: lid}});
}
I subscribe both Locations and Thoughts collections. But unfortunately after calling my method only changes in Locations collection are visible - my modified thought is still the same and I must reload page to see its changes. Is it bug in Meteor or my mistake? Do you know some ways to solve or bypass this problem?
What's more, when I push some value to locations array in some thought with Robomongo, changes are visible though. It looks like the problem is that two changes try to be seen at the same time.

Related

Avoiding repetitive calls when creating reactfire hooks

When initializing a component using reactfire, each time I add a reactfire hook (e.g. useFirestoreDocData), it triggers a re-render and therefore repeats all previous initialization. For example:
const MyComponent = props => {
console.log(1);
const firestore = useFirestore();
console.log(2);
const ref = firestore.doc('count/counter');
console.log(3);
const { value } = useFirestoreDocDataOnce(ref);
console.log(4);
...
return <span>{value}</span>;
};
will output:
1
1
2
3
1
2
3
4
This seems wasteful, is there a way to avoid this?
This is particularly problematic when I need the result of one reactfire hook to create another (e.g. retrieve data from one document to determine which other document to read) and it duplicates the server calls.
See React's documentation of Suspense.
Particulary that part: Approach 3: Render-as-You-Fetch (using Suspense)
Reactfire uses this mechanics. It is not supposed to fetch more than one time for each call even if the line is executed more than once. The mechanics behind "understand" that the fetch is already done and will start the next one.
In your case, react try to render your component, see it needs to fetch, stop rendering and show suspense's fallback while fetching. When fetch is done it retry to render your component and as the fetch is completed it will render completely.
You can confirm in your network tab that each calls is done only once.
I hope I'm clear, please don't hesitate to ask for more details if i'm not.

Saving a score to firebase with attached values

What I'm wanting to accomplish is save a score to firebase that has two values attached to it. Here's the code that writes the score to firebase.
func writeToFirebase() {
DispatchQueue.global(qos: .userInteractive).async {
self.ref = Database.database().reference()
if GameManager.instance.getTopScores().contains(GameManager.instance.getGameScore()) {
self.ref?.child("user")
.child(GameManager.instance.getUsername())
.child(String(GameManager.instance.getGameScore()))
.updateChildValues( [
"badge":GameManager.instance.getBadgeLevel(),
"vehicle": GameManager.instance.getVehicleSelected()
]
)
}
}
}
The issue I'm having is when a new score is saved with its values it sometimes overwrites the other scores. This seems to be random and its not when they're the same score or anything like that. Sometimes it will only overwrite one score and sometimes multiple. I'm watching firebase and I can see it being overwritten, it turns red and then is deleted. Sometimes the new score being added will be red and get deleted. The score doesn't need to be a child, but I don't know how to attach values to it if it's not. Any help is appreciated
This issue seems to happen occasionally so I am going to post my comment as an answer.
There are situations where an observer may be added to a node and when data changes in that node, like a write or update, it will fire that observer which may then overwrite the existing data with nil.
You can see this visually in the console as when the write occurs, you can see the data change/update, then it turns red and then mysteriously vanishes.
As suggested in my comment, add a breakpoint to the function that performs the write and run the code. See if that function is called twice (or more). If that's the case, the first write is storing the data properly but upon calling it a second time, the values being written are probably nil, which then makes the node 'go away' as Firebase nodes cannot exist without a value.
Generally speaking if you see your data turn red and vanish, it's likely caused by nil values being written to the node.

Wait for backend service response before making changes to ag-grid table

I am using ag-grid/ag-grid-angular to provide an editable grid of data backed by a database. When a user edits a cell I want to be able to post the update to the backend service and if the request is successful update the grid and if not undo the user's changes and show an error.
I have approached this problem from a couple different angles but have yet to find the solution that meets all my requirements and am also curious about what the best practice would be to implement this kind of functionality.
My first thought was to leverage the cellValueChanged event. With this approach I can see the old and new values and then make a call to my service to update the database. If the request is successful then everything is great and works as expected. However, if the request fails for some reason then I need to be able to undo the user's changes. Since I have access to the old value I can easily do something like event.node.setDataValue(event.column, event.oldValue) to revert the user's changes. However, since I am updating the grid again this actually triggers the cellValueChanged event a second time. I have no way of knowing that this is the result of undoing the user's changes so I unnecessarily make a call to my service again to update the data even though the original request was never successful in updating the data.
I have also tried using a custom cell editor to get in between when the user is finished editing a cell and when the grid is actually updated. However, it appears that there is no way to integrate an async method in any of these classes to be able to wait for a response from the server to decide whether or not to actually apply the user's changes. E.g.
isCancelBeforeStart(): boolean {
this.service.updateData(event.data).subscribe(() => {
return false;
}, error => {
return true;
});
}
does not work because this method is synchronous and I need to be able to wait for a response from my service before deciding whether to cancel the edit or not.
Is there something I am missing or not taking in to account? Or another way to approach this problem to get my intended functionality? I realize this could be handled much easier with dedicated edit/save buttons but I am ideally looking for an interactive grid that is saving the changes to the backend as the user is making changes and providing feedback in cases where something went wrong.
Any help/feedback is greatly appreciated!
I understand what you are trying to do, and I think that the best approach is going to be to use a "valueSetter" function on each of your editable columns.
With a valueSetter, the grid's value will not be directly updated - you will have to update your bound data to have it reflected in the grid.
When the valueSetter is called by the grid at the end of the edit, you'll probably want to record the original value somehow, update your bound data (so that the grid will reflect the change), and then kick off the back-end save, and return immediately from the valueSetter function.
(It's important to return immediately from the valueSetter function to keep the grid responsive. Since the valueSetter call from the grid is synchronous, if you try to wait for the server response, you're going to lock up the grid while you're waiting.)
Then, if the back-end update succeeds, there's nothing to do, and if it fails, you can update your bound data to reflect the original value.
With this method, you won't have the problem of listening for the cellValueChanged event.
The one issue that you might have to deal with is what to do if the user changes the cell value, and then changes it again before the first back-end save returns.
onCellValueChanged: (event) => {
if (event.oldValue === event.newValue) {
return;
}
try {
// apiUpdate(event.data)
}
catch {
event.node.data[event.colDef.Field] = event.oldValue;
event.node.setDataValue(event.column, event.oldValue);
}
}
By changing the value back on node.data first, when setDataValue() triggers the change event again, oldValue and newValue are actually the same now and the function returns, avoiding the rather slow infinite loop.
I think it's because you change the data behind the scenes directly without agGrid noticing with node.data = , then make a change that agGrid recognises and rerenders the cell by calling setDataValue. Thereby tricking agGrid into behaving.
I would suggest a slightly better approach than StangerString, but to credit him the idea came from his approach. Rather than using a test of the oldValue/newValue and allowing the event to be called twice, you can go around the change detection by doing the following.
event.node.data[event.colDef.field] = event.oldValue;
event.api.refreshCells({ rowNodes: [event.node], columns: [event.column.colId] });
What that does is sets the data directly in the data store used by aggrid, then you tell it to refresh that grid. That will prevent the onCellValueChanged event from having to be called again.
(if you arent using colIds you can use the field or pass the whole column, I think any of them work)

Data from Firebase undefined in React

I'm playing with Firebase as an alternative to a local Mongo store, for the time being.
I've followed various tutorials, however they are all for older versions of ES6. I've tried to tweak them to v14 and ES6 but, well, no errors but no data!
Some code:
var Rebase = require('re-base');
var base = Rebase.createClass('https://reactathon.firebaseio.com/days');
...
componentDidMount() {
console.log('ExampleComponent Mounted');
base.bindToState('days', {
context: this,
state: 'days',
asArray: true
});
console.log(this.state.days[0]);
}
The console simply logs undefined. I've tried the base URL with and without /days. I've tried getting the data as an object instead of an array. I have a feeling I'm simply pointing at the wrong thing.
Any thoughts?
Cheers.
bindToState is an asynchronous method so it's going to take some time to set up that listener. You're logging before the listener has been set up. As Jacob mentioned in his comment, move your log to your render method and then once your state is bound to Firebase your component will re render and you should see your data.

Meteor observer with two different applications

Because changed data (inserted by application A) needs to be displayed in application B in realtime we decided to go with .find().observe(...).
It does look like:
App A -> Insert -> mongodb <- observe -> publish -> Display App B
This works fine but it has a delay of about 3-5 seconds between Inserting in A and displaying in B. How can i change this?
Initially i thought, Oplog-Observe-Driver is default in Meteor > Version 1 and does react in realtime. Does it still POLL or is there some other reason for the delay????
Thanks for your expanations.
If you're using Oplog, then the changes will be immediate. If you're using poll then it'll take a few seconds as you wrote.
You need to set MONGO_OPLOG_URL correctly to make this work. (And of course your MongoDB needs to be Oplog enabled.)
Also, you don't need to use find().observe() if you're in a reactive context, find() is enough. On the server though you might need find().observe() depending on what you're doing.
Did you use DDP.connect? You also have to use the onReconnect
Remote = DDP.connect('http://yourremoteserver');
MyCollection = new Mongo.Collection('same_name', Remote);
// do whatever you need with collection
let watchCollection = function (query={}, project={}) {
return MyCollection.find(query, project).observe({
changed: function () { console.log('Something changed!') }
});
}
DDP.onReconnect(watchCollection);