persist data in a DB issue, receiving NULL on console - javascript

I am new to NodeJS and I am having an issue trying to persist/save some data in a DB.
let's start from the beginning so you can understand easier. I have a list of sports with an option to checked or unchecked, that's what I need to persist, that checked.
FRONT END:
controller.js
$scope.toggleSportSelection = function(sport) {
var params = {};
params.user = $scope.customer.customer;
sport.checked = !sport.checked;
SportsFactory.setSportChecked(params);
};
service.js
setSportChecked: function(params) {
var defer = $q.defer();
$http.post(CONSTANT_VARS.BACKEND_URL + '/sports/checked', params)
.success(function(sportChecked) {
LocalForageFactory.remove(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED, params);
defer.resolve(sportChecked);
})
.error(function(err) {
console.log(err);
defer.reject(err);
});
return defer.promise;
}
I've been debugging this front end part and everything seems to be OK...
Now BACK END:
setSportCtrl.js
module.exports = {
setCheck: function(req, res) {
var checkedSportParams = req.body;
SportSelectionService.sportChecked(checkedSportParams).then(function() {
res.json(200, {msg: 'OK'});
}, function(err) {
res.json(400, err);
});
}
}
SportSelection.js (model)
module.exports = {
connection: 'RedisServer',
attributes: {
sport: {
type: 'array',
required: false
},
user: {
type: 'string',
required: true
}
}
};
in this part I can see how that console are print in the terminal, but if I do console.log(sportChecked) or console.log(newSport) all I get is an array which says null everywhere...
SportSelectionService.js
module.exports = {
sportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
console.time('sportChecked_findOne');
SportSelection.findOne({
user: params.user
}).exec(function(err, sportChecked) {
console.timeEnd('sportChecked_findOne');
var newSport;
if (err) {
reject(new Error('Error finding user'));
console.error(err);
}else if (sportChecked) {
newSport = sportChecked.sport;
console.time('sportChecked_update');
SportSelection.update({
user: params.user
},
{
sport: newSport
}).exec(function(err, sportCheckedUpdated) {
console.timeEnd('sportChecked_update');
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(sportCheckedUpdated);
}
});
if (sportChecked.sport) {
sportChecked.sport.push(params.sport);
console.log('New sport added');
}else {
sportChecked.sport = [params.sport];
}
}else {
console.time('sportChecked_create');
SportSelection.create({
sport: [params.sport],
user: params.user
}).exec(function(err, created) {
console.timeEnd('sportChecked_create');
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
}
});
}
});
});
}
So what do you think is my issue here ? what am I doing wrong ?

here is the way I did it, I will teach how from the beginning to the end
starting from the Node.js part, I am using Sails.js and lodash
SetSportsController.js
'use strict';
module.exports = {
setCheck: function(req, res) {
var checkedSportParams = req.body;
SportSelectionService.sportChecked(checkedSportParams).then(function() {
res.json(200, {msg: 'OK'});
}, function(err) {
res.json(400, err);
});
},
retrieveSetCheck: function(req, res) {
if (req.params) {
SportSelectionService.getSportChecked(req.params).then(function(sportChecked) {
res.json(200, sportChecked);
}, function(err) {
res.json(400, err);
});
}else {
res.json(400, {error: 'Error retrieving Sports'});
}
}
};
than we go with SportSelectionService.js
'use strict';
var _ = require('lodash');
module.exports = {
sportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
SportSelection.findOne({
user: params.user
}).exec(function(err, sportChecked) {//this array comes with duplicates
var newSport,
sportCheckedUniq = _.uniq(sportChecked.sport);//prevents duplicates
if (err) {
reject(new Error('Error finding user'));
console.error(err);
}else if (sportChecked) {
newSport = sportCheckedUniq || [];
if (_.includes(sportCheckedUniq, params.sport)) {
sportCheckedUniq = _.pull(newSport, params.sport);
sportCheckedUniq = _.difference(newSport, params.sport);
}else {
newSport.push(params.sport);
sportCheckedUniq = newSport;
}
SportSelection.update({
user: params.user
},
{
sport: newSport
}).exec(function(err, sportCheckedUpdated) {
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(sportCheckedUpdated);
}
});
if (sportCheckedUniq) {
sportCheckedUniq.push(params.sport);
}else {
sportCheckedUniq = [params.sport];
}
}else {
SportSelection.create({
sport: [params.sport],
user: params.user
}).exec(function(err, created) {
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
}
});
}
});
});
},
getSportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
console.time('sportChecked_findOne');
SportSelection.findOne({
user: params.user
}).exec(function(err, sportChecked) {
console.timeEnd('sportChecked_findOne');
if (err) {
reject(new Error('Error finding sportChecked'));
console.error(err);
}else {
if (sportChecked) {
fullfill(sportChecked);
}else {
SportSelection.create({
// 10 is the ID for soccer, which must unchecked by default on every single user.
sport: [10],
user: params.user
}).exec(function(err, created) {
console.log(err);
console.log(created);
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
}
});
}
}
});
});
}
};
as you can see here we have only 2 methods, the first
sportChecked() is the one which fires up when the user checked or unchecked any of the items.
and then we have getSportChecked() which is the method called everytime that the user logs in again.
I do not have any delete method because we are not deleting anything, we are just watching for a change of statement.
Also I am working with a Redis Server
do not forget to create the model, I gave'em a name SportSelection.js
'use strict';
module.exports = {
connection: 'RedisServer',
attributes: {
sport: {
type: 'array',
required: false
},
user: {
type: 'string',
required: true
}
}
};
also, in the config folder we have policies.js, I can't tell you how to work with this because is your configuration, but mine is:
SetSportsController: {
setCheck: ['jwtAuth', 'sanitizerPolicy', 'headersPolicy'],
retrieveSetCheck: ['jwtAuth', 'sanitizerPolicy']
},...
then, we go to the Front End Part (remember: AngularJS)
I have a controller, controller.js
$scope.toggleSportSelection = function(sport) {
SportsFactory.setSportChecked({
user: $scope.customer.customer,
sport: sport.id
}).then(function() {
sport.checked = !sport.checked;
$ionicScrollDelegate.resize();
}, function() {
$ionicScrollDelegate.resize();
});
};
which is working along this template
<ion-item ng-repeat="sport in sportsFilter track by $index"
ng-click="toggleSportSelection(sport)">
{{:: sport.name}}
</ion-item>
then, service.js
be aware of AngularJS
here is where I make the post and get, look
.factory('SportsFactory', function($http, $q, AuthFactory, LocalForageFactory,
LeaguesFactory, ImageFactory, CONSTANT_VARS) {
getSports: function(customer) {
var defer = $q.defer(),
_this = this;
LocalForageFactory.retrieve(CONSTANT_VARS.LOCALFORAGE_SPORTS)
.then(function(sports) {
if (!_.isNull(sports)) {
defer.resolve(sports);
}else {
$http.get(CONSTANT_VARS.BACKEND_URL + '/lines/sports/' + customer.agent)
.success(function(sports) {
sports = _.sortBy(sports, function(sport) {
return sport.priority;
});
_this.getSportChecked(customer).then(function(sportChecked) {
var sportIds = _.pluck(sports, 'id'),
intersectedSports = _.intersection(sportIds, sportChecked.sport);
if (sports.length) {
sports = _.map(sports, function(sport) {
sport.checked = !_.includes(intersectedSports, sport.id);
return sport;
});
}else {
AuthFactory.logout();
}
});
_.each(sports, function(sport) {
var sportImg = ImageFactory.sportImages(sport);
if (sportImg.length) {
sport.img = sportImg[0];
}else {
sport.img = 'https://placehold.it/40x40';
}
});
defer.resolve(sports);
})
.error(function(err) {
defer.reject(err);
});
}
});
return defer.promise;
},
setSportChecked: function(params) {
var defer = $q.defer();
$http.post(CONSTANT_VARS.BACKEND_URL + '/sports/checked', params)
.success(function(sportChecked) {
LocalForageFactory.remove(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED, params);
defer.resolve(sportChecked);
})
.error(function(err) {
console.log(err);
defer.reject(err);
});
return defer.promise;
},
getSportChecked: function(customer) {
var defer = $q.defer(),
user,
rejection = function(err) {
defer.reject(err);
};
LocalForageFactory.retrieve(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED)
.then(function(sportChecked) {
user = customer.customer;
if (!_.isNull(sportChecked)) {
defer.resolve(sportChecked);
}else {
$http.get(CONSTANT_VARS.BACKEND_URL + '/sports/getChecked/' + user)
.success(function(sportChecked) {
LocalForageFactory.set(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED, sportChecked);
defer.resolve(sportChecked);
})
.error(rejection);
}
}, rejection);
return defer.promise;
}
});
first, center your attention to setSportChecked() and getSportChecked(), there is where the magic happen in this service, then the function getSports() make a called to getSportChecked() which looks like this
_this.getSportChecked(customer).then(function(sportChecked) {
var sportIds = _.pluck(sports, 'id'),
intersectedSports = _.intersection(sportIds, sportChecked.sport);
if (sports.length) {
sports = _.map(sports, function(sport) {
sport.checked = !_.includes(intersectedSports, sport.id);
return sport;
});
}else {
AuthFactory.logout();
}
});
so, this is the end version if this long project, you have to touch lots of file to get with this, to save/persist data in a DB, so, see this code because this is how I have it so far and is working great and is fast, I have no errors yet, parting from here, ask the questions you need to know, I will be answering during the day. Hope this helps

IF you are using mongooseJS (it appears you are) AND the purpose of the the service is to add the sport to the sports array then you can use the findOneAndUpdate method (which will return a promise with the exec method) and significantly reduce the service to:
module.exports = {
sportChecked: function(params) {
return SportSelection.findOneAndUpdate(
{user: params.user},
{$addToSet: {sports: params.sport}}
).exec();
}
};
$addToSet will only add the value if it is not already in the array. If duplicates are acceptable you can use $push
As pointed out in the comments you are likely using waterline. If so, it appears the update method behaves similarly to findOneAndUpdate. So maybe this might work (I didn't check if you need to call exec or not):
module.exports = {
sportChecked: function(params) {
return SportSelection.update(
{user: params.user},
{$addToSet: {sports: params.sport}}
).exec(); // Not sure if this is necessary
}
};

I think this could be done far more simply - rather than updating it like that, just track all your checkboxes client-side and update the whole thing when it changes.
I think you are missing out on some stuff that angular can do for you really easily, and in this case you can push all the workload off on the client rather than the server.
Firstly, make your HTML something like this:
<form name="myForm">
Basketball <input type="checkbox" ng-change="updateRecord()" ng-model="sport.basketball"><br />
Baseball <input type="checkbox" ng-change="updateRecord()" ng-model="sport.baseball"><br />
Football <input type="checkbox" ng-change="updateRecord()" ng-model="sport.football"><br />
Soccer <input type="checkbox" ng-change="updateRecord()" ng-model="sport.soccer"><br />
Golf <input type="checkbox" ng-change="updateRecord()" ng-model="sport.golf"><br />
<br />{{sport}}
<br /><span ng-show="loading">Updating Redis</span>
</form>
Plunker
Angular will create a sport object for you that will track whatever is or isn't checked. Rather than trying to manage that inside the database, just let angular take care of it, and whenever it changes, overwrite the whole record.
You can use your toggle function in the ng-change of your checkboxes:
$scope.toggleSportSelection = function(sport) {
var params = {};
params.user = $scope.customer.customer;
params.sport = sport
SportsFactory.setSportChecked(params);
};
On the backend, you can do similarly to what was suggested in other answers:
module.exports = {
sportChecked: function(params) {
return SportSelection.update(
{user: params.user},
{sports: params.sport}}
).exec();
}
};
This way you have way less code, it's easier to read, and you remove a LOT of logic from the server.
Unless there is some reason I am missing, you don't need to use $q in your ajax call. You can just use $http built in success and error to handle the promises.
You will need to change your model, by the way, to hold the sport object rather than an array.
Also, if you are in fact using sails, just use the blueprint PUT. You don't even need any logic - sails has already generated it when you generate your api.
When you load your page, just get the record back, and push data.sport into $scope.sport and your checkboxes should update.

Related

use of then / catch in $http call in angular

I my Node backend have the following end-point:
usersRoute.get('/get', function(req, res) {
//If no date was passed in - just use todays date
var date = req.query.date || dateFormat(new Date(), 'yyyy-mm-dd'),
search = req.query.search;
users.getAllUsers(date, search)
.then(function(results) {
res.json(results);
}, function(err) {
res.status(500).json({
success: false,
message: 'Server error.',
data: []
});
});
});
I have changed my sql table name to something else to trigger the function(err){} part
When I use this in my service it looks like this:
function getUsers(date, search) {
return $http.get('/api/users/get', {
params: {
date: UtilsService.formatDate(date),
search: search
}
})
.then(getData)
.catch(handleErr);
function getData(response) {
return response.data;
}
function handleErr(err) {
LoggerService.error('Could not retrieve users.', err ,'Ooops');
}
}
Knowing the server will return an http status code 500, I thought it would go right to the catch block. But it also returns the data /which is undefined in the then block
I use my service in my controller like this:
function getUsers(date, search) {
isAdmin();
vm.loading = true;
vm.filteredUsers = [];
return UsersService.getUsers(date, search).then(function(data) {
vm.loading = false;
allUsers = data || [];
vm.filteredUsers = allUsers.slice(0, 50);
vm.distribution = UsersService.getDistribution(allUsers);
return vm.filteredUsers;
});
}
My problem is, since the then part is triggered in my service. I'm trying to slice undefined
My question is: What are som best practices when it comes to this sort of pattern.
The problem is that your catching the error from your API and then returning the promise created by .catch.
Quick example
promise.then(function(data) {
throw 'Some error';
}).catch(function (err) {
console.log(err) // will output 'Some error'
}).then(function () {
// This will run even though we have a catch before
});
So how can we prevent the .then it's easy we throw an error inside the .catch
promise.then(function(data) {
throw 'Some error';
}).catch(function (err) {
console.log(err) // will output 'Some error'
throw 'You shall not pass'
}).then(function () {
// This will not run
});
So in your case you have two options, one throw an error as I said or two inject the $q service into your service:
function getUsers(date, search) {
return $http.get('/api/users/get', {
params: {
date: UtilsService.formatDate(date),
search: search
}
})
.then(getData)
.catch(handleErr);
function getData(response) {
return response.data;
}
function handleErr(err) {
LoggerService.error('Could not retrieve users.', err ,'Ooops');
return $q.reject(err);
}
}
You could do something like that
function getUsers(date, search, cb) {
return $http.get('/api/users/get', {
params: {
date: UtilsService.formatDate(date),
search: search
}
})
.then(cb)
.catch(handleErr);
function handleErr(err) {
LoggerService.error('Could not retrieve users.', err ,'Ooops');
}
}
And then in your controller
UsersService.getUsers(date, search, function(data) {
vm.loading = false;
allUsers = data || [];
vm.filteredUsers = allUsers.slice(0, 50);
vm.distribution = UsersService.getDistribution(allUsers);
});

How to return an unique checked = false item from the DB?

I have an array/list which all the items are set to checked=true by default, but I have an special case, I want to set to checked=false the id:25 by default, got it ? all the items set to checked=true except the id:25.
So how can I do that from the Nodejs part ?
here I have the code which is activated every time you uncheck any of the items
module.exports = {
sportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
SportSelection.findOne({
user: params.user
}).exec(function(err, sportChecked) {
var newSport;
if (err) {
reject(new Error('Error finding user'));
console.error(err);
}else if (sportChecked) {
newSport = sportChecked.sport || [];
if (_.includes(sportChecked.sport, params.sport)) {
sportChecked.sport = _.pull(newSport, params.sport);
sportChecked.sport = _.difference(newSport, params.sport);
}else {
newSport.push(params.sport);
sportChecked.sport = newSport;
}
SportSelection.update({
user: params.user
},
{
sport: newSport
}).exec(function(err, sportCheckedUpdated) {
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(sportCheckedUpdated);
}
});
if (sportChecked.sport) {
sportChecked.sport.push(params.sport);
}else {
sportChecked.sport = [params.sport];
}
}else {
SportSelection.create({
sport: [params.sport],
user: params.user
}).exec(function(err, created) {
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
}
});
}
});
});
},
...
};
the sportChecked param returns the ids of the sports checked to false. So what I need is to include id:10 in that param.
so what are your suggestions here ?

getting an error once trying to retrieve data with NODEJS

I have a little issue trying to perform a get in my app.
first this is the error in the browser console
GET http://localhost:1337/sports/getChecked/12787fb1e00a01337f0508ca47223d15
401 (Unauthorized)
code.min.js:875 Object {err: "JSON request needed"}
code.min.js:543 Error {stack: (...)}
code.min.js:967 ["3042", "59321H", "5932"]
code.min.js:690 ["3042", "59321H", "5932"]
I am doing a post and in the post everything is fine so far, the issue is in the get, I am working with Json web tokens (JWT)...
these are the routes for post and get
module.exports.routes = {
'post /sports/checked': 'SetSportsController.setCheck',
'get /sports/getChecked/:user': 'SetSportsController.retrieveSetCheck'
};
here the policies
module.exports.policies = {
setCheck: ['jwtAuth', 'sanitizerPolicy', 'headersPolicy', 'sessionKiller'],
retrieveSetCheck: ['jwtAuth', 'sanitizerPolicy', 'sessionKiller']
};
get on SetSportController.js
retrieveSetCheck: function(req, res) {
if (req.params) {
SportSelectionService.getSportChecked(req.params).then(function(sportChecked) {
console.log(sportChecked.sport);
res.json(200, sportChecked);
}, function(err) {
res.json(400, err);
});
}else {
res.json(400, {error: 'Error retrieving Sports'});
}
}
SportSelectionService.js
getSportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
console.time('sportChecked_findOne');
SportSelection.find({
user: params.user
}).exec(function(err, sportChecked) {
console.timeEnd('sportChecked_findOne');
if (err) {
reject(new Error('Error finding favorite leagues'));
console.error(err);
}else {
if (sportChecked) {
fullfill(sportChecked.sport);
}else {
console.time('sportChecked_create');
SportSelection.create({
sport: [],
user: params.user
}).exec(function(err, created) {
console.timeEnd('sportChecked_create');
console.log(err);
console.log(created);
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
fullfill(created.sport);
}
});
}
}
});
});
}
this is the FRONT-END part
<ion-item ng-click="toggleSportSelection(sport)">
{{:: sport.name}}
</ion-item>
controller.js
SportsFactory.getSportChecked(customer).then(function(sportChecked) {
console.log(sportChecked);
_.each(sports, function(sport) {
var sportIds = _.pluck(sport, 'id'),
intersectedSports = _.intersection(sportIds, sportChecked),
checkedSportObjects = _.filter(sport, function(sportObj) {
return _.includes(intersectedSports, sportObj.sportIds);
});
_.each(checkedSportObjects, function(sport) {
$scope.sportObj.push(sport);
});
});
});
$scope.toggleSportSelection = function(sport) {
var params = {};
params.user = $scope.customer.customer;
params.sport = sport.id;
sport.checked = !sport.checked;
SportsFactory.setSportChecked(params);
};
service.js
getSportChecked: function(customer) {
var defer = $q.defer(),
user,
rejection = function(err) {
console.log(err);
defer.reject(err);
};
LocalForageFactory.retrieve(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED)
.then(function(sportChecked) {
user = customer.customer;
if (!_.isNull(sportChecked)) {
defer.resolve(sportChecked);
}else {
$http.get(CONSTANT_VARS.BACKEND_URL + '/sports/getChecked/' + user)
.success(function(sportChecked) {
LocalForageFactory.set(CONSTANT_VARS.LOCALFORAGE_FAVORITE_LEAGUES, sportChecked);
defer.resolve(sportChecked);
})
.error(rejection);
}
}, rejection);
return defer.promise;
}
looking on the files project I got this file named headersPolicy.js which looks like this
module.exports = function(req, res, next) {
var _ = require('lodash');
if (!_.isNull(req.headers)) {
/* This API only accepts JSON, we could send 406 response, but we don't want
* to give any kind of clues ;)
*/
if (!req.is('json')) {
return res.json(401, {err: 'JSON request needed'});
}
next();
}else {
/*
* No headers, wrong request
* */
return res.json(401, {err: 'Headers not present'});
}
};
so where is this error coming from ? what am I doing wrong ?
The middleware below is checking (presumably) whether the Content-Type header is set to 'application/json'. Since you're sending a GET and not a POST request, that header is most likely not application/json.
if (!req.is('json')) {
return res.json(401, {err: 'JSON request needed'});
}

how can I put an item with checked = true depending on the info in the DB?

I have a list of items with the option to checked or unchecked them. I am doing a post request in order to save the unchecked items, and that post request works great, but I am having an issue with the get request, the front-end retrieves fine the info from the back-end, but I have this function which is putting on checked = true the items without taking care of the unchecked items saved already
if (sports.length) {
$scope.sports = _.map(sports, function(sport) {
sport.checked = true;
return sport;
});
};
so, if the user refreshes the page, all items get back to checked = true due to the function above. So how can I do to persist the items with checked or unchecked ? what do I have to change in that function in order to persist that data ?
this is my code regarding the get request of that data
I am using lodash, just in case...
front-end
controller.js
please read the note in this code
.controller('SportsController', function($scope, SportsFactory, AuthFactory) {
$scope.sports = [];
SportsFactory.getSportChecked(customer).then(function(sportChecked) {
_.each(sports, function(sport) {
var sportIds = _.pluck(sports, 'id'),
intersectedSports = _.intersection(sportIds, sportChecked),
checkedSportObjects = _.filter(sport, function(sportObj) {
return _.includes(intersectedSports, sportObj);
});
_.each(checkedSportObjects, function(sport) {
$scope.sports.push(sport);
});
});
}
//this is the function putting checked = true with out taking care
//of what the back-end returns
if (sports.length) {
$scope.sports = _.map(sports, function(sport) {
sport.checked = true;
return sport;
});
}
$scope.toggleSportSelection = function(sport) {
var params = {};
params.user = $scope.customer.customer;
params.sport = sport.id;
sport.checked = !sport.checked;
SportsFactory.setSportChecked(params);
};
});
service.js
.factory('SportsFactory', function($http, $q, AuthFactory,
LocalForageFactory, CONSTANT_VARS) {
return {
getSportChecked: function(customer) {
var defer = $q.defer(),
user,
rejection = function(err) {
console.log(err);
defer.reject(err);
};
LocalForageFactory.retrieve(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED)
.then(function(sportChecked) {
user = customer.customer;
if (!_.isNull(sportChecked)) {
defer.resolve(sportChecked);
}else {
$http.get(CONSTANT_VARS.BACKEND_URL + '/sports/getChecked/' + user)
.success(function(sportChecked) {
LocalForageFactory.set(CONSTANT_VARS.LOCALFORAGE_SPORTS_CHECKED, sportChecked);
defer.resolve(sportChecked);
})
.error(rejection);
}
}, rejection);
return defer.promise;
}
}
});
BACK-END - node.js/sails.js
SetSportsController
module.exports = {
retrieveSetCheck: function(req, res) {
if (req.params) {
SportSelectionService.getSportChecked(req.params).then(function(sportChecked) {
res.json(200, sportChecked);
}, function(err) {
res.json(400, err);
});
}else {
res.json(400, {error: 'Error retrieving Sports'});
}
}
}
SportSelectionService
module.exports = {
getSportChecked: function(params) {
var Promise = require('bluebird');
return new Promise(function(fullfill, reject) {
console.time('sportChecked_findOne');
SportSelection.find({
user: params.user
}).exec(function(err, sportChecked) {
console.timeEnd('sportChecked_findOne');
if (err) {
reject(new Error('Error finding sportChecked'));
console.error(err);
}else {
if (sportChecked) {
fullfill(sportChecked);
}else {
console.time('sportChecked_create');
SportSelection.create({
sport: [],
user: params.user
}).exec(function(err, created) {
console.timeEnd('sportChecked_create');
console.log(err);
console.log(created);
if (err) {
reject(new Error('Error on sportChecked'));
}else {
fullfill(created);
}
});
}
}
});
});
}
}

Async waterfall equivalent with Q

I've got a single page which is an account settings page. In it, I allow my users to update their avatar (if they've attached an image), change their email (if it has been changed from the original), and change their name and password.
Right now, I'm using async's waterfall method, but am swapping out async for Q since I prefer the syntax (and api). I'm wondering if this is the way that I should be using Q in replacement of async's waterfall.
I'm doing something like this:
exports.settingsAccountPOST = function(req, res) {
var doesEmailExist = function() {
var deferred = Q.defer();
User.findByEmail({
email: req.body.email
}, function(err, user) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(user);
}
});
return deferred.promise;
};
var updateEmail = function(email) {
var deferred = Q.defer();
User.updateEmail({
userId : req.session.user.id,
email : req.body.email
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
};
var updateName = function() {
var deferred = Q.defer();
if (req.body.name) {
User.updateName({
userId: req.session.user.id,
name: req.body.name
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
}
};
doesEmailExist().then(function(email) {
if (!email) {
return(updateEmail(email));
}
}).then(function() {
return(updateName())
}).then(function() {
res.redirect('/account')
});
};
Say that there is an error with the email address being used. Is there a way to "pass" it to the final call? Use case: Updated password properly, but email update didn't work, so I want to show a session flash to the user telling them they updated their password properly, but there was an issue with updating their email.
I was looking in the docs and it seems I may need to use:
.fin(function () {
});
Is this correct? If so, what should I be passing into that? Just push to an object the error that occurred within the chain and then loop through all errors and display them to the user? Or just return immediately and display the error?
If you are using Q.defer you are generally doing something wrong.
var findByEmail = Q.nbind(User.findByEmail, User);
var updateEmail = Q.nbind(User.updateEmail, User);
var updateName = Q.nbind(User.updateName, User);
//later on...
exports.settingsAccountPOST = function (req, res) {
findByEmail({
email: req.body.email
})
.then(function (user) {
if (!user) {
return updateEmail({
userId: req.session.user.id,
email: req.body.email
});
}
})
.then(function () {
return updateName({
userId: req.session.user.id,
name: req.body.name
})
})
.then(function () {
res.redirect("/account");
})
.catch(function(e){
//Handle any error
});
};

Categories