SailsJs call function into the same controller - javascript

I want to call a function in the same controller but I mostly have an error like: "ReferenceError: myFunctionB is not defined"
module.exports = {
myfunctionA: function(req, res){
var theword;
theword = myFunctionB({id:26})
return res.json({say:theword})
},
myfunctionB: function(req, res){
var id = req.param('id');
niceword.get({
id:id
}, function(err, word){
if(err){return res.negotiate(err);}
if(!word){return res.json({err:"no word found"})}
return res.json({word:word});
})
}
}
I also tryed by to put myFunctionB into a service but, as I use many other controller etc I have no response.. Any idea?

There are a few ways to do this. The proper way, as yBrodsky says, is to create a service that runs a callback or returns a promise:
myServiceFunctionB: function(params, cb) {
var id = params.id;
niceword.get({
id:id
}, function(err, word){
return cb({ say: { word: word });
});
}
And then in your controller, just use:
return MyServiceName.myServiceFunctionB({id: 26}, res.json);
You can also pass in your req and res to continue using those:
myServiceFunctionB: function(req, res) { ...
Alternatively, you can use the this keyword in your controller:
myfunctionA: function(req, res){
req.params.id = 26;
return this.myfunctionB(req, res);
}
If you'll be doing more complicated logic where context gets lost, just set a new var at the start using this:
myfunctionA: function(req, res){
var self = this;
req.params.id = 26;
//...lots of nested promises or callbacks...
return self.myfunctionB(req, res);
}

Thank you yBrodsky and wwwslinge, I finaly get what I need thank of you.
I had to make a little change because I still need to use the data after they pass into the function.
Controller
MyServiceName.functionB({id:1}, function(data){
// doing some stuff with the data and then
return res.json({data: data});
});
Service
functionB: function(params, cb){
var id = params.id;
Niceword.getbyid({
id:id
}, function(err, word){
if(err){return err}
return cb({word:word});
})
}

Related

Querying mongoose to perform a join

I have 2 collections setup as below, Dates and Streets.
What I would like to achieve is to, query Streets by a param StreetName and look that up to find it's unique ID and then query the other collection by that ID to pull back all the dates that match.
My route is set up to /wasteDate/:StreetName. Here's what I have:
model.js
var DateSchema = new Schema({
date: {
type: Date
},
street_id: {
type: String,
}
});
var StreetSchema = new Schema({
name: {
type: String
}
});
routes.js
module.exports = function(app) {
var wasteCollections = require('../controllers/wasteController');
app.route('/wasteDate/:streetName')
.get(wasteCollections.get_dates_by_street_name);
};
controller.js
var mongoose = require('mongoose'),
ColDate = mongoose.model('Dates'),
that = this,
Street = mongoose.model('Streets');
(...)
exports.manual_get_dates_by_street = function (id) {
var wasteDates = ColDate.find({ street_id: id }).lean();
return wasteDates;
};
exports.get_dates_by_street_name = function (req, res) {
Street.find({
name: req.params.streetName
}, function(err, street) {
var query;
var theStreetId = street[0].id;
if (err) res.send(err);
query = that.manual_get_dates_by_street(theStreetId);
res.json(query);
});
};
at the moment i'm getting a circular reference error on the JSON.
I don't think I'm doing it the right way and think I may need to amend my schema?
Any help appreciated
You can either use (1) find twice or (2) aggregation.
Here's the first way:
exports.manual_get_dates_by_street = function (id, callback) {
// you are dealing with asynchronous operations, so you have to wait for the callback
// to execute before you can get the data
ColDate.find({ street_id: id }).lean().exec(callback);
};
exports.get_dates_by_street_name = function (req, res) {
// you are expecting one result, so use findOne instead of find
Street.findOne({ name: req.params.streetName }, function (err, street) {
// make sure you handle errors properly such as stopping execution of
// the next lines or else you may get unexpected errors
if (err)
return res.send(err);
// we pass a callback that will be executed once results (or an error) are found
that.manual_get_dates_by_street(street._id, function (err, dates) {
res.json({ dates: dates });
});
});
};
I never used it but I think mongoose-models may resolve your problem. https://github.com/SportZing/mongoose-models
Another possible approach is to put the second query function as a callback of the first.

Mongoose findOne inside loop

I have a document Users with storage property defined as an array of ObjectId referenced to other document called Storage. I'm trying to get a specific user and then return the storage information inside an array.
This is my code:
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = [];
data.storages.forEach(function(record){
Storage.findOne({_id: record}, function(err, storage){
storageArray.push(storage);
});
});
return Promise.all(storageArray);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
}
But after execution, storageList is an empty array.
I'm a newbie to node.js and please let me know if I need to provide more information.
So the main point here is that you need to .exec() in order to return the Promise. This is why your attempt failed. But there are better approaches syntactically as well.
Instead use .map() and call .exec() to return the Promise
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = data.storages.map(function(id) {
return Storage.findOne({_id: id }).exec()
});
return Promise.all(storageArray);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
Or instead use $in with .find() where the method returns an array and the operator allows you to specify an array of things to match:
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
return Storage.find({ "_id": { "$in": data.storages } }).exec();
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
It also basically looks like you could instead simply use .populate()
User.findOne({'userId': req.user.userId}, 'storages')
.populate('storages')
.then(function(data) {
res.render('storage_template', {
storage: data.storages
});
console.log(data.storages);
});
But it's not clear in your question if these are actually defined as references to the Storage model or not.
Also see Queries are not Promises in the mongoose documentation.
If you are using mongoose then you can use .populate() to get your storages information details from Storage collection.
can try this one
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages')
.populate('storages')
.exec(function (err, storageList) {
if (err) return handleError(err);
console.log(storageList);
res.render('storage_template', {
storage: storageList
});
});
}
for using this .populate() ensure first in your User model you added ref filed for storages
like:
storages: [{ type: Schema.Types.ObjectId, ref: 'Storage' }]
//do npm install lodash
const _ = require('lodash');
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = [];
let queries = _.map(data.storages, (record)=>{
return Storage.findOne({_id: record}, function(err, storage){
storageArray.push(storage);
});
})
return Promise.all(queries);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
}

Syntax Error: unexpected Token {

Here is my Model:
var mongoose = require('mongoose');
var partySchema = new mongoose.Schema({
partyCode: Number,
partyName: String,
mobileNo: String
});
var Party = module.exports = mongoose.model('Party', partySchema);
module.exports.getAllParties = function(callback){
Party.find().lean().exec(function(err, parties){
if (err) return callback(err, null);
callback(null, parties);
});
};
Here is the Route:
router.get('/', function(req, res, next){
//retrieve all parties from Party model
//mongoose.model('Party').find({}, function (err, parties) {
Party.getAllParties(err, parties){
if (err) {
return console.error(err);
} else {
//respond to both HTML and JSON. JSON responses require 'Accept: application/json;' in the Request Header
res.format({
//response in dust or jade files
html: function(){
res.render('Party', {
title: 'Party',
"parties" : parties
});
},
//JSON response will show all parties in JSON format
json: function(){
res.json(parties);
}
});
}
};
});
At line no 9 in Route.js (Here in above code line no.4) I get an error:
Party.getAllParties(err, parties){
Syntax error: {unexpected token
Why is it unexpected? Can't I use a function's body here???
You need to pass in a function instead. A block statement like that outside unfortunately won't work.
This is most likely what you need:
Party.getAllParties(function (err, parties) {
// rest of your logic here
});
You can't place a block statement these when you're calling a function.
It looks like you want something like
Party.getAllParties(function() {
// ...
})
Where you pass an anonomous callback function

$http.delete callback not firing in basic CRUD app

I have a basic CRUD app, my main problem is with the delete function.
The angular controller calls the $http action
'use strict';
(function(){
var app = angular.module('JournalApp', [])
.controller('JournalController', function($scope, $http){
var loadEntries = function(){
$http.get('/api/entry')
.then(function(response){
$scope.entries = response.data;
if(response.data.noEntries){
$scope.entries.noEntries = true;
};
});
};
loadEntries();
//Create new Journal Entry
$scope.addEntry = function(entry){
var data = ({
hours: entry.hours,
notes: entry.notes
});
$http.post('/api/entry', data);
loadEntries();
};
//Delete Journal Entry
$scope.delEntry = function(item){
$http({
method: 'DELETE',
url: '/api/entry',
// data: item,
data: {date: item.date},
headers: {'Content-Type': 'application/json;charset=utf8'}
}, loadEntries());
};
});
})();
which is routed to my server side handler
//GET Request
this.getEntries = function(req, res){
journal.find().sort({date: -1}).toArray(function(err, doc){
if(err) throw err;
if(doc.length){
console.log('found entries');
res.json(doc);
} else {
console.log('Returning no Entries.')
res.json(({ "noEntries": true }));
}
})
};
//Post Request
this.addEntry = function(req, res){
console.log('Adding new entry...');
var time = new Date();
var notation = req.body.notes || 'n/a'
journal.insert({ hours: req.body.hours, notes: notation, date: new Date()})
};
//Delete Request
this.deleteEntry = function(req, res){
journal.findOne({"date": new Date(req.body.date)}, function(err, data){
if(err) throw err;
console.log('Removing item _id: ' + data._id);
journal.remove({"date": data.date});
});
};
}
module.exports = entryHandler;
I can verify through mongo (and the console.log) that the entry was deleted and everything works as its supposed to. However the $http callback does not fire. The callback function (loadEntries) im using to refresh the journal entires on the page.
I can get the callback to fire when I write the code out as
$http.delete('/api/entry', item).then( function(){ loadEntries() } );
However when I use this method the server side handler doesnt work properly.
Is there a better way to go about this without a ton of extra modules? I'd preffer to learn the basics before I start adding packages all over the place doing the work for me.
EDIT:
I've tried adding .then(function(){ loadEntries() }); to my angular code, and it does not work.
You need to call .then on your $http call to execute the callback function:
$http({
method: 'DELETE',
url: '/api/entry',
// data: item,
data: {date: item.date},
headers: {'Content-Type': 'application/json;charset=utf8'}
}).then(function() {
loadEntries()
});
I was unable to successfully call my callback no matter what I tried. However my goal was to remove div's from my app, so I just used the angular ng-show/hide directive to accomplish this.
Apart from .then(), It looks like server code is not returning any call completion status such as success/failure similar to - Ok/BadRequest (IHTTPctionResult type) in C#

How do I return data from a function in node?

I'm using Express and trying to teach myself node/javascript callbacks and I've stumbled across something.
I have a route that looks like this:
var express = require('express');
var router = express.Router();
var api = require('../api');
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.methodname(res);
});
module.exports = router;
And then the module that is being called above looks like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function(res) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
res.setHeader("Content-Type","application/json");
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
res.send(returnInstances);
})
.on('error', function(resp){
console.log(resp);
})
}
};
module.exports = modulename;
As you can see I'm passing through the response parameter through to my module, but I'd rather pass back instances and then in the route return api.modulename.instances, like this:
var library = require('library');
var instances = {};
var modulename = {
getAllInstances: function() {
var request = new library.asyncMethod();
request.on('success', function(resp) {
var returnData = resp.data;
instances = {
id: returnData[0].InstanceId,
state: {name: returnData[0].State.Name, code: returnData[0].State.Code}
};
})
.on('error', function(resp){
console.log(resp);
})
.send();
}
};
module.exports = modulename;
However, when I do, it's coming through as the default value {} but if I run it as above, I do get output so I know that there should be data in there.
Let me know if I have misunderstood your issue. If you are saying you want to pass back objects from getAllInstances then you pass in a callback and call it from the event handler like this-
router.get('/',function(req, res, next){
var modulename = api.modulename;
modulename.getAllInstances(res, function(err, instances){
if(err){ ... }
else{
res.send(instances); //or however you want to use instances
}
});
});
and in getInstances
var modulename = {
getAllInstances: function(res, cb) {
var request = new library.asyncMethod();
request.on('success', function(resp) {
instances = resp.data;
var returnInstances = {
id: instances[0].InstanceId,
state: {name: instances[0].State.Name, code: instances[0].State.Code}
};
cb(null, instances);
})
.on('error', function(err){
cb(err, null));
});
//.send(); not sure what this is it seems to be request.send() ??
}
};
The problem here lies with when the response from the API call is available. The event loop in Node means code won't block until the API replies with a response. Hence a callback is needed to handle that response when it becomes available. You probably want to use the API response in your Express router response so there's a chain of dependency.
One strategy here would be to use promises and not callbacks, it would alleviate some of the pain you're experiencing with async response from the API call.
In your routes:
router.get('/',function(req, res, next){
var instances = [];
// The function below could be refactored into a library to minimise controller code.
var resolver = function (response) {
var data = JSON.parse(response);
instances.push({
name: data[0].State.Name,
code: data[0].State.Code
});
res.render('instances'. {instances : instances});
};
modulename.methodname(resolver);
});
And in your module:
var rp = require('request-promise'); // Also see q-io/http as an alternate lib.
var modulename = {
methodname: function (resolver) {
rp('http://the-inter.net')
.then(resolver)
.catch(console.error);
}
};
This might not cut-n-paste work but have a look at the request-promise examples for further clarification.

Categories