This question already has answers here:
Mongoose findByIdAndUpdate not returning correct model
(2 answers)
Closed 7 years ago.
Im trying to add an item to an array and get back the updated model.
I PUT my request like so:
addCarToDriver(CarToAdd) {
const self = this;
request
.put('api/drivers/' + this.state.race._id)
.send({
car: CarToAdd})
.end(function(err, res) {
console.log(res.body);
self.setState({race: res.body});
});
}
It hits the server here :
MyRace.findByIdAndUpdate(
req.params.myrace_id,
{ $push: { 'cars': req.body.car } },
function(er, model) {
if (er) {
console.log(err);
return res.send(err);
}
console.log(model);
return res.json(model);
});
Now in the request callback I would expect the new updated model to get logged, i.e with the new Car. It does not. The "old" version gets logged. However, if i refresh the page the model gets updated with the new car.
Of course I need to setState directly in the callback. Any tips on how to do this?
Mongoose 4 has changed .findAndUpdate behavior to return the old document by default, and if you want the new updated one you need to pass {new:true}, because that was the actual behavior in MongoDB
You can patch the Query to set options.new=true to have the old way back:
var __setOptions = mongoose.Query.prototype.setOptions;
mongoose.Query.prototype.setOptions = function(options, overwrite) {
__setOptions.apply(this, arguments);
this.options['new'] = true;
return this;
};
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 4 years ago.
I am trying to get one of the events outside and print it. Of course the 'answer' variable scope is only inside.
As you can see, this one gets a list of all the events stored on a specific calendar. I am able to access specific events from the array. However. I don't know how to get that one value out of this.
I am not that familiar with coding, help appreciated.
//my question is just above the last line.
let google = require('googleapis');
let privatekey = mypk.json;
// configure a JWT auth client
let jwtClient = new google.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
['https://www.googleapis.com/auth/calendar']);
//authenticate request
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("Successfully connected!");
}
});
let calendar = google.calendar('v3');
calendar.events.list({
auth: jwtClient,
calendarId: 'xxxxx#group.calendar.google.com'
}
, function (err, response, cb) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var events = response.items;
var singleEvent = events[0].summary;
return;
/* if (events.length == 0) {
console.log('No events found.');
} else {
console.log('Event from Google Calendar:');
for (let event of response.items) {
console.log('Event name: %s, Creator name: %s, Create date: %s', event.summary, event.creator.displayName, event.start.date);
}
}*/
}
);
//this is what I need to get, one event but the variable has no scope here.
console.log ('this is the ' + singleEvent);
Declare the variable singleEvent just after calendar
Like this:
let calendar = google.calendar('v3');
let singleEvent;
And inside the callback do the following:
singleEvent = events[0].summary;
Note: Understanding scopes in Javascript is probably the most important thing you need to learn. This article will probably help you a lot:
https://scotch.io/tutorials/understanding-scope-in-javascript
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I have a client sending JSON information to the server and trying to make a register. JSON includes name and password of this user. Before the server makes the register into the database it needs to make sure that the username is available. For this purpose i created a function called isRegisted(req) that makes a search on the database and if the result.length is greater than zero then set a boolean value to true and at the end of the function returns this boolean value.
The mysql search returns what it's expected in "result" but at the end the boolean value is not set and the console.log prints undefined.
Here's my code:
function register(req , res){
res.setHeader('Content-Type', 'application/json');
console.log(isRegisted(req));
}
function isRegisted(req){
var flag;
var query = conn.query("SELECT * FROM Users WHERE name=?", [ req.body.name ], function(err, result) {
if(err){
console.error(err);
return;
}
console.log(result);
if(result.length > 0)
flag = true;
else
flag = false;
});
return flag;
}
Thank you for your attention.
The problem with your code is, that you make an asynchronious request to the database. The function(err,result) will only be executed AFTER the query was done in your database. So you cannot be sure, when the function(err,result) will be called. So in mostly every case the flag is always undefined, because the database has not executed the query, which results in an undefined variable flag. The circumvent this problem, do your additional logic in the function(err, result) .
In this case, you have a async. function, so parent function doesn't wait for child function execution and returns undefined. Something like this may be useful for you:
var successFn = function() {console.log('success');},
errorFn = function() {console.log('error');};
function register(req , res){
res.setHeader('Content-Type', 'application/json');
console.log(isRegisted(req, successFn, errorFn));
}
function isRegisted(req, successFn, errorFn){
var flag;
var query = conn.query("SELECT * FROM Users WHERE name=?", [ req.body.name ], function(err, result) {
if(err){
errorFn();
}
if(result.length > 0) {
successFn();
}
});
}
I'm trying to use the Steam Community (steamcommunity) npm package along with meteorhacks:npm Meteor package to retreive a user's inventory. My code is as follows:
lib/methods.js:
Meteor.methods({
getSteamInventory: function(steamId) {
// Check arguments for validity
check(steamId, String);
// Require Steam Community module
var SteamCommunity = Meteor.npmRequire('steamcommunity');
var community = new SteamCommunity();
// Get the inventory (730 = CSGO App ID, 2 = Valve Inventory Context)
var inventory = Async.runSync(function(done) {
community.getUserInventory(steamId, 730, 2, true, function(error, inventory, currency) {
done(error, inventory);
});
});
if (inventory.error) {
throw new Meteor.Error('steam-error', inventory.error);
} else {
return inventory.results;
}
}
});
client/views/inventory.js:
Template.Trade.helpers({
inventory: function() {
if (Meteor.user() && !Meteor.loggingIn()) {
var inventory;
Meteor.call('getSteamInventory', Meteor.user().services.steam.id, function(error, result) {
if (!error) {
inventory = result;
}
});
return inventory;
}
}
});
When trying to access the results of the call, nothing is displayed on the client or through the console.
I can add console.log(inventory) inside the callback of the community.getUserInventory function and receive the results on the server.
Relevant docs:
https://github.com/meteorhacks/npm
https://github.com/DoctorMcKay/node-steamcommunity/wiki/CSteamUser#getinventoryappid-contextid-tradableonly-callback
You have to use a reactive data source inside your inventory helper. Otherwise, Meteor doesn't know when to rerun it. You could create a ReactiveVar in the template:
Template.Trade.onCreated(function() {
this.inventory = new ReactiveVar;
});
In the helper, you establish a reactive dependency by getting its value:
Template.Trade.helpers({
inventory() {
return Template.instance().inventory.get();
}
});
Setting the value happens in the Meteor.call callback. You shouldn't call the method inside the helper, by the way. See David Weldon's blog post on common mistakes for details (section Overworked Helpers).
Meteor.call('getSteamInventory', …, function(error, result) {
if (! error) {
// Set the `template` variable in the closure of this handler function.
template.inventory.set(result);
}
});
I think the issue here is you're calling an async function inside your getSteamInventory Meteor method, and thus it will always try to return the result before you actually have the result from the community.getUserInventory call. Luckily, Meteor has WrapAsync for this case, so your method then simply becomes:
Meteor.methods({
getSteamInventory: function(steamId) {
// Check arguments for validity
check(steamId, String);
var community = new SteamCommunity();
var loadInventorySync = Meteor.wrapAsync(community.getUserInventory, community);
//pass in variables to getUserInventory
return loadInventorySync(steamId,730,2, false);
}
});
Note: I moved the SteamCommunity = Npm.require('SteamCommunity') to a global var, so that I wouldn't have to declare it every method call.
You can then just call this method on the client as you have already done in the way chris has outlined.
I have node running with express as the server side framework.
I have created the following endpoint:
app.post('/post/save', auth.auth, function (req, res) {
Post.findById(req.body._id, function (err, post) {
post = post || new Post();
post.author.name = req.user.getName();
post.author.id = req.user._id;
post.title = req.body.title;
post.body = req.body.body;
post.save(function (err, object) {
err && res.send(500);
res.status(200).send({ /*id: object._id*/ });
});
});
});
When I call this the first time, it works.
When I call this the second time, it fails. The request just keeps pending, and the object returned from the save function call is undefined.
req.body._id is undefined in both the requests. I try to create 2 new posts in a row.
What I want to do is to check if a document exist, if it does, update it and then save it, or create a new document.
I know stuff like upsert exist, but I cant use it because I need the pre-save middleware to trigger, and it only triggers before .save.
Can anyone see the error?
What if you put your logic to a callback, and then - either create or find a Post based on the request query value, passing your callback function? Just dont forget to remove this assignment: post.author.id = req.user._id;
app.post('/post/save', auth.auth, function (req, res) {
var callback = function(post) {
post.author.name = req.user.getName();
post.title = req.body.title;
post.body = req.body.body;
post.save(function (err, object) {
err && res.send(500);
res.status(200).send({ /*id: object._id*/ });
});
};
if (req.body._id) {
Post.findById(req.body._id, function (err, post) {
callback(post);
});
} else {
var post = new Post();
callback(post);
}
});
My original post worked, once I removed the unique field from the model, and dropped the collections in the database.
It might have been enough to drop the indexes; see Leonid Beschastnys comment;
when you're setting a field to be unique, Mongoose creates an unique
index on this field. This index persist in MongoDB even after removing
unique: true flag. Dropping collection indexes should resolve your
problem
On my client side, I display a list of users and a small chart for each user's points stored in the DB (using jQuery plugin called sparklines).
Drawing the chart is done on Template.rendered method
// client/main.js
Template.listItem.rendered = function() {
var arr = this.data.userPoints // user points is an array of integers
$(this.find(".chart")).sparkline(arr);
}
Now I have a Meteor method on the server side, that is called on a regular basis to update the the user points.
Meteor.methods({
"getUserPoints" : function getUserPoints(id) {
// access some API and fetch the latest user points
}
});
Now I would like the chart to be automatically updated whenever Meteor method is called. I have a method on the template that goes and calls this Meteor method.
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
Meteor.call("getUserPoints", this._id);
}
});
How do I turn this code into a "reactive" one?
You need to use reactive data source ( Session, ReactiveVar ) together with Tracker.
Using ReactiveVar:
if (Meteor.isClient) {
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
var instance = Template.instance();
Meteor.call("getUserPoints", this._id, function(error, result) {
instance.userPoints.set(result)
});
}
});
Template.listItem.created = function() {
this.userPoints = new ReactiveVar([]);
};
Template.listItem.rendered = function() {
var self = this;
Tracker.autorun(function() {
var arr = self.userPoints.get();
$(self.find(".chart")).sparkline(arr);
})
}
}
Using Session:
if (Meteor.isClient) {
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
Meteor.call("getUserPoints", this._id, function(error, result) {
Session.set("userPoints", result);
});
}
});
Template.listItem.rendered = function() {
var self = this;
Tracker.autorun(function() {
var arr = Session.get("userPoints");
$(self.find(".chart")).sparkline(arr);
})
}
}
Difference between those implementation :
A ReactiveVar is similar to a Session variable, with a few
differences:
ReactiveVars don't have global names, like the "foo" in
Session.get("foo"). Instead, they may be created and used locally, for
example attached to a template instance, as in: this.foo.get().
ReactiveVars are not automatically migrated across hot code pushes,
whereas Session state is.
ReactiveVars can hold any value, while Session variables are limited
to JSON or EJSON.
Source
Deps is deprecated, but still can be used.
The most easily scalable solution is to store the data in a local collection - by passing a null name, the collection will be both local and sessional and so you can put what you want in it and still achieve all the benefits of reactivity. If you upsert the results of getUserPoints into this collection, you can just write a helper to get the appropriate value for each user and it will update automatically.
userData = new Meteor.Collection(null);
// whenever you need to call "getUserPoints" use:
Meteor.call("getUserPoints", this._id, function(err, res) {
userData.upsert({userId: this._id}, {$set: {userId: this._id, points: res}});
});
Template.listItem.helpers({
userPoints: function() {
var pointsDoc = userData.findOne({userId: this._id});
return pointsDoc && pointsDoc.points;
}
});
There is an alternative way using the Tracker package (formerly Deps), which would be quick to implement here, but fiddly to scale. Essentially, you could set up a new Tracker.Dependency to track changes in user points:
var pointsDep = new Tracker.Dependency();
// whenever you call "getUserPoints":
Meteor.call("getUserPoints", this._id, function(err, res) {
...
pointsDep.changed();
});
Then just add a dummy helper to your listItem template (i.e. a helper that doesn't return anything by design):
<template name="listItem">
...
{{pointsCheck}}
</template>
Template.listItem.helpers({
pointsCheck: function() {
pointsDep.depend();
}
});
Whilst that won't return anything, it will force the template to rerender when pointsDep.changed() is called (which will be when new user points data is received).