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
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With