I have a client integration test to ensure my admin user can change user roles via the user management interface in my app. However, when I query for the user I want to change, the query comes back empty even though it has been created in the fixture.
describe('Admin users', function() {
beforeEach(function(done) {
Meteor.loginWithPassword('admin#gmail.com', '12345678', function(error) {
Router.go('/users');
Tracker.afterFlush(done);
});
});
beforeEach(waitForRouter);
afterEach(function(done) {
Meteor.logout(function() {
done();
});
});
it('should be able to change user roles', function(done) {
var changeUser = Meteor.users.findOne({ emails: { $elemMatch: { address: 'user#gmail.com' } } });
console.log('changeUser: ', changeUser);
console.log('Users: ', Meteor.users.find().fetch());
$('#user-' + changeUser._id + '-roles').val('manage-users').change();
expect(Roles.userIsInRole(changeUser, 'manage-users')).toBe(true);
expect(Roles.userIsInRole(changeUser, 'edit-any')).toBe(false);
done();
});
});
This test fails with the following error:
TypeError: Cannot read property '_id' of undefined
Here's the fixture file that creates the two users:
/* globals
resetDatabase: true,
loadDefaultFixtures: true,
*/
var Future = Npm.require('fibers/future');
resetDatabase = function () {
console.log('Resetting database');
// safety check
if (!process.env.IS_MIRROR) {
console.error('velocityReset is not allowed outside of a mirror. Something has gone wrong.');
return false;
}
var fut = new Future();
var collectionsRemoved = 0;
var db = Meteor.users.find()._mongo.db;
db.collections(function (err, collections) {
var appCollections = _.reject(collections, function (col) {
return col.collectionName.indexOf('velocity') === 0 ||
col.collectionName === 'system.indexes';
});
_.each(appCollections, function (appCollection) {
appCollection.remove(function (e) {
if (e) {
console.error('Failed removing collection', e);
fut.return('fail: ' + e);
}
collectionsRemoved++;
console.log('Removed collection');
if (appCollections.length === collectionsRemoved) {
console.log('Finished resetting database');
fut['return']('success');
}
});
});
});
return fut.wait();
};
loadDefaultFixtures = function () {
console.log('Loading default fixtures');
var adminId = Accounts.createUser({email: 'admin#gmail.com', password: '12345678'});
var standardUserId = Accounts.createUser({email: 'user#gmail.com', password: '12345678'});
console.log('Users: ', Meteor.users.find().fetch());
console.log('Finished loading default fixtures');
};
if (process.env.IS_MIRROR) {
resetDatabase();
loadDefaultFixtures();
}
I can see the output of the fixture console.log in the Jasmine logs, and it shows both users. The log from the test logs undefined for the changeUser and an array of only the current user for the full collection fetch.
The only other problems I can imagine are the publication and the subscription. I can't see anything wrong with them, but I could be missing it. Here's the publication:
Meteor.publish('allUsers', function () {
if (Roles.userIsInRole(this.userId, ['manage-users'])) {
return Meteor.users.find({}, { fields: { emails: true, roles: true, id: true}});
} else {
return this.ready();
}
});
and the subscription:
subscriptions: function() {
return [Meteor.subscribe('allUsers'), Meteor.subscribe('allRoles')];
},
It seems like the default Meteor users publication containing only the current user is being delivered for the test, but shouldn't waiting on the route and that route's user subscription mean that the entire user list is being published/subscribed?
Related
When I login to my firebase v 2.4.1 app using email / password auth with an invalid email. Firebase throws an error internally and I can't seem to find a way to catch it.
The examples here work, but they are using v.1.1.1 and when I swap the libraries in my app to 1.1.1 it seems to work also
Am I missing something or is this a known issue?
http://jsfiddle.net/firebase/a221m6pb/embedded/result,js/
(function (jQuery, Firebase, Path) {
"use strict";
// the main firebase reference
var rootRef = new Firebase('https://docs-sandbox.firebaseio.com/web/uauth');
// pair our routes to our form elements and controller
var routeMap = {
'#/': {
form: 'frmLogin',
controller: 'login'
},
'#/logout': {
form: 'frmLogout',
controller: 'logout'
},
'#/register': {
form: 'frmRegister',
controller: 'register'
},
'#/profile': {
form: 'frmProfile',
controller: 'profile',
authRequired: true // must be logged in to get here
},
};
// create the object to store our controllers
var controllers = {};
// store the active form shown on the page
var activeForm = null;
var alertBox = $('#alert');
function routeTo(route) {
window.location.href = '#/' + route;
}
// Handle third party login providers
// returns a promise
function thirdPartyLogin(provider) {
var deferred = $.Deferred();
rootRef.authWithOAuthPopup(provider, function (err, user) {
if (err) {
deferred.reject(err);
}
if (user) {
deferred.resolve(user);
}
});
return deferred.promise();
};
// Handle Email/Password login
// returns a promise
function authWithPassword(userObj) {
var deferred = $.Deferred();
console.log(userObj);
rootRef.authWithPassword(userObj, function onAuth(err, user) {
if (err) {
deferred.reject(err);
}
if (user) {
deferred.resolve(user);
}
});
return deferred.promise();
}
// create a user but not login
// returns a promsie
function createUser(userObj) {
var deferred = $.Deferred();
rootRef.createUser(userObj, function (err) {
if (!err) {
deferred.resolve();
} else {
deferred.reject(err);
}
});
return deferred.promise();
}
// Create a user and then login in
// returns a promise
function createUserAndLogin(userObj) {
return createUser(userObj)
.then(function () {
return authWithPassword(userObj);
});
}
// authenticate anonymously
// returns a promise
function authAnonymously() {
var deferred = $.Deferred();
rootRef.authAnonymously(function (err, authData) {
if (authData) {
deferred.resolve(authData);
}
if (err) {
deferred.reject(err);
}
});
return deferred.promise();
}
// route to the specified route if sucessful
// if there is an error, show the alert
function handleAuthResponse(promise, route) {
$.when(promise)
.then(function (authData) {
// route
routeTo(route);
}, function (err) {
console.log(err);
// pop up error
showAlert({
title: err.code,
detail: err.message,
className: 'alert-danger'
});
});
}
// options for showing the alert box
function showAlert(opts) {
var title = opts.title;
var detail = opts.detail;
var className = 'alert ' + opts.className;
alertBox.removeClass().addClass(className);
alertBox.children('#alert-title').text(title);
alertBox.children('#alert-detail').text(detail);
}
/// Controllers
////////////////////////////////////////
controllers.login = function (form) {
// Form submission for logging in
form.on('submit', function (e) {
var userAndPass = $(this).serializeObject();
var loginPromise = authWithPassword(userAndPass);
e.preventDefault();
handleAuthResponse(loginPromise, 'profile');
});
// Social buttons
form.children('.bt-social').on('click', function (e) {
var $currentButton = $(this);
var provider = $currentButton.data('provider');
var socialLoginPromise;
e.preventDefault();
socialLoginPromise = thirdPartyLogin(provider);
handleAuthResponse(socialLoginPromise, 'profile');
});
form.children('#btAnon').on('click', function (e) {
e.preventDefault();
handleAuthResponse(authAnonymously(), 'profilex');
});
};
// logout immediately when the controller is invoked
controllers.logout = function (form) {
rootRef.unauth();
};
controllers.register = function (form) {
// Form submission for registering
form.on('submit', function (e) {
var userAndPass = $(this).serializeObject();
var loginPromise = createUserAndLogin(userAndPass);
e.preventDefault();
handleAuthResponse(loginPromise, 'profile');
});
};
controllers.profile = function (form) {
// Check the current user
var user = rootRef.getAuth();
var userRef;
// If no current user send to register page
if (!user) {
routeTo('register');
return;
}
// Load user info
userRef = rootRef.child('users').child(user.uid);
userRef.once('value', function (snap) {
var user = snap.val();
if (!user) {
return;
}
// set the fields
form.find('#txtName').val(user.name);
form.find('#ddlDino').val(user.favoriteDinosaur);
});
// Save user's info to Firebase
form.on('submit', function (e) {
e.preventDefault();
var userInfo = $(this).serializeObject();
userRef.set(userInfo, function onComplete() {
// show the message if write is successful
showAlert({
title: 'Successfully saved!',
detail: 'You are still logged in',
className: 'alert-success'
});
});
});
};
/// Routing
////////////////////////////////////////
// Handle transitions between routes
function transitionRoute(path) {
// grab the config object to get the form element and controller
var formRoute = routeMap[path];
var currentUser = rootRef.getAuth();
// if authentication is required and there is no
// current user then go to the register page and
// stop executing
if (formRoute.authRequired && !currentUser) {
routeTo('register');
return;
}
// wrap the upcoming form in jQuery
var upcomingForm = $('#' + formRoute.form);
// if there is no active form then make the current one active
if (!activeForm) {
activeForm = upcomingForm;
}
// hide old form and show new form
activeForm.hide();
upcomingForm.show().hide().fadeIn(750);
// remove any listeners on the soon to be switched form
activeForm.off();
// set the new form as the active form
activeForm = upcomingForm;
// invoke the controller
controllers[formRoute.controller](activeForm);
}
// Set up the transitioning of the route
function prepRoute() {
transitionRoute(this.path);
}
/// Routes
/// #/ - Login
// #/logout - Logut
// #/register - Register
// #/profile - Profile
Path.map("#/").to(prepRoute);
Path.map("#/logout").to(prepRoute);
Path.map("#/register").to(prepRoute);
Path.map("#/profile").to(prepRoute);
Path.root("#/");
/// Initialize
////////////////////////////////////////
$(function () {
// Start the router
Path.listen();
// whenever authentication happens send a popup
rootRef.onAuth(function globalOnAuth(authData) {
if (authData) {
showAlert({
title: 'Logged in!',
detail: 'Using ' + authData.provider,
className: 'alert-success'
});
} else {
showAlert({
title: 'You are not logged in',
detail: '',
className: 'alert-info'
});
}
});
});
}(window.jQuery, window.Firebase, window.Path))
I removed the 1.1.1 reference from your jsFiddle and added the 2.4.1 one (https://cdn.firebase.com/js/client/2.4.1/firebase.js) and then tried it with a random email and it reacted exactly the same as with the previous version. Can you give me more details on how to reproduce it? I was using Chrome.
I would like to share a variable that is set in the client with the Meteor.onCreateUser function call on the server.
I have this code that sets some user properties before a user is created
Accounts.onCreateUser(function(options, user, err) {
if (options.profile) {
user.profile = options.profile;
// Images
var picturelrg = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=large";
var picturesm = "http://graph.facebook.com/" + user.services.facebook.id + "/picture/?type=small";
options.profile.picturelrg = picturelrg;
options.profile.picturesm = picturesm;
options.profile.upvotes = 0;
options.profile.neutralvotes = 0;
options.profile.downvotes = 0;
// ip = response.ip;
return user;
}
});
Here is the client code
if (Meteor.isClient) {
fbLogin = function() {
Meteor.loginWithFacebook({
requestPermissions: ['public_profile', 'email', 'user_location']
}, function(err) {
if (err)
// redirect to register if popup comes and user isn't on register
Session.set('errorMessage', err.reason || 'Unknown Eror');
console.log(Session.get('errorMessage'));
});
}
locate = function(){
function ipLocate(whenDone) {
var api = "http://ipinfo.io?callback=?";
$.getJSON(api, {
format: "jsonp"
})
.done(function(response) {
var result = ""
// show all the props returned
for (var prop in response) {
result += prop + ": " + response[prop] + "<br>";
}
var selectedResponse = {
city: response.city,
region: response.region,
country: response.country,
ip: response.ip,
latLng: response.loc
}
console.log(selectedResponse);
whenDone(selectedResponse);
return selectedResponse
});
}
// HACK: Async
function ipDone(selectedResponse) {
response = selectedResponse;
}
// Set response
ipLocate(ipDone);
return response
}
Template.ModalJoin.events({
'click .modJoinFB-Btn ': function() {
locate();
fbLogin();
}
});
}
On the client I have an event handler that sets some values when the user clicks the "Sign Up with Facebook" button. How can I send these values to the onCreateUser function to be accessed.
Ex: I want to store user geolocation info ( city, state) when the user registers but I don't know how this can be sent from the client to server.
I'm not sure how I would use Meteor.call() if I could
Looks like you should run a Meteor.call function inside fbLogin, passing that location data, if no error is returned. Something like this:
fbLogin = function() {
Meteor.loginWithFacebook({
requestPermissions: ['public_profile', 'email', 'user_location']
}, function(err) {
if (err) {
Session.set('errorMessage', err.reason || 'Unknown Eror');
console.log(Session.get('errorMessage'));
} else {
//if no error was returned, then Meteor.call the location
var userId = Meteor.userId(); //you should send that userId for the method.
Meteor.call('storeLocation', locationData, userId, function(err,res){
if (err) {
console.log(err);
}
});
}
});
}
And on server, you create a Method for updating that user profile data with the location. Maybe something like this:
Meteor.methods({
'storeLocation': function(locationData, userId) {
var locationData = {
// based on what you have gathered on your client location function
'city': response.city,
'region': response.region,
'country': response.country,
'ip': response.ip,
'latLng': response.loc
}
Meteor.users.update(
//I suggest placing it inside profile, but do as it is better to you
{'_id': userId},
{$addToSet: {'profile.locations': locationData }}
);
}
});
Not sure if you will store like that, but this is how I have done for myself. Let me know if any problems or doubts, we can try to solve it together.
I've been struggling with a simple test that involves mocking promises with no luck. I'm using jasmine spies. Hope somebody can help me. I've successfully mocked findByUserName method but there appears to be something wrong with the promise. The tests just fails without any error. Please see my code:
Module CertificationSettingsManager.FindUser being tested:
'use strict';
var CertificationSettingsManagerFindUser = function(usersRepository, accountsRepository, userName, next) {
if (userName) {
usersRepository.findByUserName(userName)
.then(function(user) {
if (user && user.password) {
delete user.password;
}
return user;
})
.then(function(user) {
if (user) {
accountsRepository.findAllAssignedTo(user.userId).then(function(results) {
user.hasAccountsAssigned = (results.count > 0);
user.belongsRepHierarchy = true;
user.isMemberOfReconOrPrep = true;
next(null, user);
});
}
})
.catch(function(err) {
if (err) {
next(err);
}
});
} else {
next();
}
};
module.exports = CertificationSettingsManagerFindUser;
And this is the test spec:
'use strict';
var findUserModule = require('./CertificationSettingsManager.FindUser');
var Q = require('q');
describe('CertificationSettingsManager findUser module', function() {
var userSpy;
var accounstSpy;
var nextCallbackSpy;
var inputUserTemplate;
var accountsResult;
beforeEach(function() {
inputUserTemplate = {
userId: 1234
};
accountsResult = {
count: 0
};
userSpy = jasmine.createSpyObj('UserRepository', ['findByUserName']);
accounstSpy = jasmine.createSpyObj('ProfileRepository', ['findAllAssignedTo']);
nextCallbackSpy = jasmine.createSpy('nextCallback spy');
});
it('when userName was not supplied it should call next with no parameters', function() {
findUserModule(null, null, null, nextCallbackSpy);
expect(nextCallbackSpy).toHaveBeenCalledWith();
});
//done callback passed so it can be asynchronous
it('if user has password, it should remove the password', function(done) {
inputUserTemplate.password = 'there is some password here';
accounstSpy.findAllAssignedTo.and.returnValue(Q.resolve(accountsResult));
userSpy.findByUserName.and.returnValue(Q.resolve(inputUserTemplate));
findUserModule(userSpy, accounstSpy, 'rassiel', function(){
expect(inputUserTemplate.password).toBeUndefined();
// done being called
done();
});
});
});
The second test: it('if user has password, it should remove the password' is failing, but when I try to debug the test it never hits the then inside:
usersRepository.findByUserName(userName)
.then(function(user) {
**UPDATED!!!****
After adding the done callback to the it method, the test is working now... that's what I was missing. Then done should be called after the expects statements.
Is there a better way of doing this?
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.
I was wondering if anyone could point me in the right direction and help me fix this error I'm getting when I attempt to add a user with my Ember.js model after created a user with Firebases createUser method.
To be more specific here is the error I'm getting: Uncaught TypeError: Cannot read property 'createRecord' of undefined
App.SignUpController = Ember.Controller.extend({
needs: ['sign-in'],
needs: ['application'],
userSignedIn: false,
actions: {
signMeUp: function() {
var state = false;
var controllerContext = this;
// Create firebase user
ref.createUser({
email : this.get('email'),
password : this.get('password'),
}, function(error, user) {
if (error === null) {
console.log('User created with id', user.uid);
state = true;
controllerContext.set('userSignedIn', state);
console.log("State from sign-up page: "+ state);
console.log("Testing user.uid inside: "+user.uid);
var fbid = user.id;
controllerContext.set('user id', user.uid);
var newUser = this.store.createRecord('user', {
id: fbid,
email: this.get('email'),
password: this.get('password'),
});
newUser.save();
} else {
console.log("Error creating account:", error);
}
}); // End createUser
this.transitionToRoute('letters');
}
}
});
UPDATE: Here is a (very hacky) solution I came up with after a day of JS plumbing.
App.SignUpController = Ember.Controller.extend({
needs: ['sign-in'],
needs: ['application'],
userSignedIn: false,
thisUserID: '',
actions: {
signMeUp: function() {
var state = false;
var controllerContext = this;
// Create firebase user
function authWithPassCallback(userObj, user){
console.log("authWithPassCallback user.uid is: "+user.uid);
return user.uid
}
function createUserAndLogin(userObj, callback) {
ref.createUser(userObj, function(error, user) {
if (error === null) {
console.log("User created successfully");
controllerContext.set('thisUserID', user.uid);
return callback(userObj, user);
} else {
console.log("Error creating user:", error);
}
});
}
var userAndPass = {
email: this.get('email'),
password: this.get('password')}
var fbPayload = createUserAndLogin(userAndPass, authWithPassCallback);
setTimeout(function () {
console.log("FB load: "+ controllerContext.get('thisUserID'));
var newUser = controllerContext.store.createRecord('user', {
id: controllerContext.get('thisUserID'),
email: controllerContext.get("email"),
password: controllerContext.get("password"),
});
newUser.save();
controllerContext.transitionToRoute('letters');
}, 1000);
console.log(controllerContext.get('thisUserID'));
}
}
});
I'm assuming the error is occurring at newUser = this.store.createRecord - at this point in your code this is no longer referring to the controller. You will need to use controllerContext.store.createRecord.
you probably just lost the context here. this doesn't refer to the controller, you're in the error function.
There are two ways of fixing that. First is to bind the function to the controller's this:
ref.createUser({
// ...
}, function(error, user) {
var newUser = this.store.createRecord('user', {/*...*/});
// ...
}.bind(this));
or to reuse the controllerContext variable:
ref.createUser({
// ...
}, function(error, user) {
// ...
var newUser = controllerContext.store.createRecord('user', {/*...*/});
});