i have a problem with my callbacks, i dont know how i get it back.
index: function(req, res,next) {
User.find().exec(function(err,users) {
if(err) {
//do something
}
res.locals.users = users
return true;
});
Country.find().exec(function(err,countrys) {
//save country
}
console.log(res.locals.users)
res.view({
users: res.locals.users,
country: countrys
user:res.locals.user
});
}
How can i get access to both objects?
Thank you
Your DB calls are running async, that means the view is rendered before the data can be passed to them. So you have to make them in synchronous manner. This can be achived via callback or you can chain your functions instead of callbacks, like this,
index: function(req, res,next) {
User.find().exec(function(err,users) {
if(err) {
//do something
}
else{
res.locals.users = users
Country.find().exec(function(err,countrys) {
if(err) {
//do something
}
else{
console.log(res.locals.users)
res.view({
users: res.locals.users,
country: countrys
user:res.locals.user
});
}
}
}
});
}
Other way is to use callbacks.
If your trying to make it pretty and organised when you keep adding more queries you can use the async library that comes with sails
see this answer as an example
Chaining waterline calls with Promises
Related
I'm relatively new to the loopback game. How can I get observers to work?
For example, I want something to observe whenever user information is changed or a user is created.
Thanks
//this observer will be activated whenever the user is edited or created
User.observe('after save', function(ctx, next) {
var theUserObject = ctx.instance;
if(ctx.isNewInstance){
anotherModel.create(theUserObject.name,theUserObject.ID);
}else{
anotherModel.update(theUserObject.name,theUserObject.ID);
}
next();
});
Is this the correct user of ctx? Where should this code sit? Within the User.js?
Just to put this in answer (see comments above):
In general what you are doing is mostly correct. You want to put operation hooks in the common/models/my-model.js file(s), but that context object (ctx) will change depending on the hook (read the linked documentation above).
In your case, to create a new model, you need to access the app off of the current model and then execute create(), but be sure to put your next() callback in the callback for the create call:
//this observer will be activated whenever the user is edited or created
User.observe('after save', function(ctx, next) {
var theUserObject = ctx.instance;
if(ctx.isNewInstance){
User.app.models.anotherModel.create({name: theUserObject.name, id: theUserObject.ID}, function(err, newInstance) {
next(err);
});
} else {
User.app.models.anotherModel.find({ /* some criteria */ }, function(err, instance) {
if (err) { return next(err); }
if (instance) {
instance.updateAttributes({name: theUserObject.name, id: theUserObject.ID}, function(err) {
next(err);
});
}
});
}
});
I am pretty new to Node.js or Javascript in general when it comes to serverside stuff. Currently I am tring to validate some of the user input and set default values if something is wrong. Now if I run my validation the json object appears in the database befor my validation is completed.
The way I am doing the validation isnt maybe the best right now but if someone can explain me the behavior, I am pretty sure i can understand Javascript alot better in the future.
Is there a better way of doing validation (without mongoose or other ODM modules) with callbacks, middleware or should I use some async module?
Here is my code:
module.exports = function(app, express, todoDB, listDB, statusDB) {
var moment = require('moment');
var todoRouter = express.Router();
todoRouter.post('/', function(req, res, next) {
console.log('1');
if (!(moment(req.body.createDate).isValid())) {
req.body.createDate = moment().format("DD-MM-YYYY HH:mm:ss");
}
else {
req.body.createDate = moment(req.body.createDate).format("DD-MM-YYYY HH:mm:ss");
}
console.log('2');
if (req.body.list_id == '') {
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
if (docs == null) {
listDB.insert({list: 'Neu', index: 1});
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
console.log('AnlageListID');
console.log(docs._id);
req.body.list_id = docs._id;
});
}
else {
console.log('BestehendeListID');
console.log(docs._id);
req.body.list_id = docs._id;
}
});
}
console.log('3');
if (req.body.status_id == '') {
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
if (docs == null) {
statusDB.insert({status: 'offen', index: 1});
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
console.log('AnlageStatusID');
console.log(docs._id);
req.body.status_id = docs._id;
});
}
else {
console.log('BestehendeStatusID');
console.log(docs._id)
req.body.status_id = docs._id;
}
});
}
console.log('4');
console.log('StatusID');
console.log(req.body.status_id);
console.log('ListID');
console.log(req.body.list_id);
todoDB.insert({
todo: req.body.todo,
createDate: req.body.createDate,
endDate: req.body.endDate,
discription: req.body.discription,
comment: req.body.comment,
list_id: req.body.list_id,
priority_id: req.body.priority_id,
section_id: req.body.section_id,
user_id: req.body.user_id,
status_id: req.body.status_id,
company_id: req.body.company_id
});
res.json({message: 'TODO erfolgreich hinzugefĆ¼gt!'});
});
return todoRouter;
};
... and this is the ouput:
1
2
3
4
StatusID
ListID
POST /api/todos 200 76.136 ms - 44
BestehendeListID
M3Xh46VjVjaTFoCM
BestehendeStatusID
48v80B4fbO87c8um
PS: Its a small "project" just for me learing the MEAN Stack so I am using neDB.
If I understand correctly you try to sequentially execute a number of asynchronous calls and introduce checks in the code to validate if previous asynchronous calls have completed. This is not going to work in a general case because your checks may be processed before the asynchronous call goes through. It might work now and then just by chance, but I would not expect even that.
There are standard mechanisms for that. One of them is using promises, another one using async and yet another one if stacking up all callbacks one into another. Below I will demonstrate how to address the problem using async, but the same general idea applies to using promises. Check the async project on Github then the following part-solution will become clear:
var async = require("async")
async.waterfall([
function(next) {
listDB.findOne({list: 'Neu'}, next); // quits on error
},
function(doc, next) {
if (doc) {
return next(null, doc._id);
}
statusDB.insert({status: 'offen', index: 1}, function(err) {
if (err) return next(err); // quit on error
statusDB.findOne({status: 'offen'}, function(err, doc) {
next(err, doc._id); // quits on error
});
});
},
function(id, next) {
// do next step and so on
next();
}
],
// this is the exit function: it will get called whenever an error
// is passed to any of the `next` callbacks or when the last
// function in the waterfall series calls its `next` callback (with
// or without an error)
function(err) {
console.error("Error processing:", err)
});
I am using Sequelize and ending up in callback hell:
db.Role.findAll().success(function(roles) {
db.User.findAll({where: {permission: 'coach'}}).success(function(coaches) {
res.render('users/edit_profile', {successFlash: successFlash, user: user, roles: roles, coaches: coaches});
}).error(function(errors) {
console.log("Error", errors);
res.render('dashboard', {successFlash: successFlash, errors: errors});
});
}).error(function(errors) {
console.log("Error", errors);
res.render('dashboard', {successFlash: successFlash, errors: errors});
});
I want to avoid callback hell and make the code more reusable. Is there a way to do this without using async, promises etc?
Suppose I write something like this..
get_user: function(req, res) {
var users = null;
users = func.get_users();
res.render('users/get_users', {users: users});
}
get_users: function() {
db.User.findAll().success(function(users){
return users;
}).error(function(err){
return null;
});
}
The problem with this approach is that, get_user renders the page first and then the callback from get_users is executed, giving me users as always null.
Please help me in this regard!
Thanks in advance!
Sequelize 2.0 ships with native promises, so you don't need to require a promise lib manually in your code. With 2.0 your snippet can be written as:
db.Role.findAll().bind({}).then(function(roles) {
this.roles = roles;
return db.User.findAll({where: {permission: 'coach'}});
}).then(function(coaches) {
res.render('users/edit_profile', {successFlash: successFlash, user: user, roles: this.roles, coaches: coaches});
}).catch(function(errors) {
console.log("Error", errors);
res.render('dashboard', {successFlash: successFlash, errors: errors});
});
You only need a single catch block, because any error is propagated to the top of the chain when you return a promise.
The bind({}) part makes an empty object available inside all functions in your promise chain so you can pass context along in that way. If could also do bind(this), if you want to be able to access something from the outer context, without having to do the idiomatic var self = this
Try this:
get_user: function(req, res) {
var users = null;
func.get_users(function(result){
if (result==null) //handle error
res.render('users/get_users', {users: users});
});
}
get_users: function(callback) {
db.User.findAll().success(function(users){
callback(users);
}).error(function(err){
callback(null);
});
}
given the async nature of mongoose (or sequelize, or redis) queries, what do you do when you have multiple queries you need to make before rendering the view?
For instance, you have a user_id in a session, and want to retrieve some info about that particular user via findOne. But you also want to display a list of recently logged in users.
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
})
}
// here, current_user isn't populated until the callback fires
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
});
};
So res.render is in the first query callback, fine. But what about waiting on the response from findOne to see if we know this user? It is only called conditionally, so I can't put render inside the inner callback, unless I duplicate it for either condition. Not pretty.
I can think of some workarounds -
make it really async and use AJAX on the client side to get the current user's profile. But this seems like more work than it's worth.
use Q and promises to wait on the resolution of the findOne query before rendering. But in a way, this would be like forcing blocking to make the response wait on my operation. Doesn't seem right.
use a middleware function to get the current user info. This seems cleaner, makes the query reusable. However I'm not sure how to go about it or if it would still manifest the same problem.
Of course, in a more extreme case, if you have a dozen queries to make, things might get ugly. So, what is the usual pattern given this type of requirement?
Yep, this is a particularly annoying case in async code. What you can do is to put the code you'd have to duplicate into a local function to keep it DRY:
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
function render() {
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
}
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
render();
})
} else {
render();
}
});
};
However, looking at what you're doing here, you'll probably need to look up the current player information in multiple request handlers, so in that case you're better off using middleware.
Something like:
exports.loadUser = function (req, res, next) {
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
req.player = player
}
next();
})
} else {
next();
}
}
Then you'd configure your routes to call loadUser wherever you need req.player populated and the route handler can just pull the player details right from there.
router.get("/",function(req,res){
var locals = {};
var userId = req.params.userId;
async.parallel([
//Load user Data
function(callback) {
mongoOp.User.find({},function(err,user){
if (err) return callback(err);
locals.user = user;
callback();
});
},
//Load posts Data
function(callback) {
mongoOp.Post.find({},function(err,posts){
if (err) return callback(err);
locals.posts = posts;
callback();
});
}
], function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
//Here `locals` will be an object with `user` and `posts` keys
//Example: `locals = {user: ..., posts: [...]}`
res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
});
Nowadays you can use app.param in ExpressJS to easily establish middleware that loads needed data based on the name of parameters in the request URL.
http://expressjs.com/4x/api.html#app.param
It should be very awesome to use non-blocking code but I'm running out of ideas how to acomplish this task. I have to validate a value by making few db queries like so:
validate = function() {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) valid = false
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) valid = false
}
return valid;
}
It is very good that validations can run in concurent manner, but the problem is how to return the final state. Obviously my example will not work. The function will return before db queries execution.
Welcome to the async world.
You should use something like async or fnqueue for your control flow,
then you can setup a chain of validations.
function isValid (mainCallback) {
new FnQueue({
date: function (callback) {
if (...) {
callback();
} else {
callback(new Error('what\'s happened here');
}
},
author: function (callback) {
db.collection('posts').findOne({ author: ..... }, callback);
}
},
function (err, data) {
mainCallback(Boolean(err)); //you should more than this :)
},
1 // concurrency level for serial execution
);
If you are using mongoose, then you can use the validations that are supported in the models. Take a look the validation docs for details and examples.
If you are not using mongoose, then you will need to pass a callback to your validate function, and the callback will receive the boolean. Also, you will need to handle the flow of your function so that they are run in series or parallel, depending on your needs. So if it is in in series, the following would work:
validate = function(callback) {
var valid = true;
db.collection('posts').findOne({date: ....}, function(err, post){
if (..) {
return callback(true);
}
db.collection('posts').findOne({author: .....}, function(err, post){
if (..) callback(false);
});
});
}