Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

persist data in a DB issue, receiving NULL on console

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 ?

like image 555
Non Avatar asked Mar 06 '15 17:03

Non


2 Answers

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

like image 184
Non Avatar answered Oct 04 '22 20:10

Non


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
  }
};
like image 41
Jason Cust Avatar answered Oct 04 '22 18:10

Jason Cust