Getting and Setting a Factory value/object in AngularJS - angularjs

Part of this is includes the question of whether or not this is possible, but I am trying to make a factory value called currentUser, which will hold a single use from userService. I am trying to figure out how to make this interaction occur.
If my factories are as follows:
app.factory('currentUser', function() {
});
app.factory('userService', function() {
return {
users: [{
name: "John",
password: "12",
email: "test#example.com",
phone: "238-491-2138"
}, {
name: "Austin",
password: "potaoes",
email: "example#gmail.com",
phone: "138-490-1251"
}]
};
});
and I have a controller that does the following, is there a way to put currentuser = userService.users[i];. Or if this is a terrible way of doing it, how might I setup a way to keep track of a "current user"?
$scope.login = function() {
for (var i = 0; i < userService.users.length; i++) {
if (userService.users[i].email.toLowerCase() === $scope.credentials.email.toLowerCase()) {
if (userService.users[i].password === $scope.credentials.password) {
$scope.messageLogin = "Success!";
$timeout(function() {
$timeout(function() {
$location.path("/account");
}, 500)
$scope.loggedIn = true;
$scope.messageLogin = "Redirecting...";
// currentUser == userService.users[i];
}, 500)
} else {
$scope.messageLogin = "Incorrect login details";
}
return;
}
}
$scope.messageLogin = "Username does not exist";
};
Not sure if this is possible due to the fact that the factory seems to always have a return and never a get/set scenario. So if this is a bad use for Factory, how should I go about it?

You have a couple of options. You can make it part of the user service itself:
app.factory('userService', function() {
var currentUser;
return {
getCurrentUser: function() {
return currentUser;
},
setCurrentUser: function(user) {
currentUser = user;
},
users: [{
name: "John",
password: "12",
email: "test#example.com",
phone: "238-491-2138"
}, {
name: "Austin",
password: "potaoes",
email: "example#gmail.com",
phone: "138-490-1251"
}]
};
});
or you can store it in a separate object:
app.factory('currentUser', function() {
var currentUser;
return {
getCurrentUser: function() {
return currentUser;
},
setCurrentUser: function(user) {
currentUser = user;
}
};
});

Services/Factories in AngularJS are singletons, so you should build your application with the expectation that a service will always resolve to the same value.
That being said, your service is just a JavaScript object and its fields/properties are mutable. I don't see anything wrong with adding a field called "current" to your "userService", which is designed to contain a reference to the current user.

Related

angularfire $add not working

I'm trying to do a simple CRUD with ionic (v1) and angularfire. I can read and delete records but I need to edit and create. The actual problem is angularfire function $add`, this function doesn't do anything and doesn't return any error in the console. I have this code:
$scope.users = $firebaseArray(root.ref('/users/'));
$scope.user = {};
//Create
$scope.title = "New user"
$scope.button = "Create";
$scope.icon = "ion-person-add";
$scope.submit = function () {
var data = {
name: $scope.user.name,
username: $scope.user.username,
email: $scope.user.email,
street: $scope.user.street,
suite: $scope.user.suite,
city: $scope.user.city,
lat: $scope.user.lat,
lng: $scope.user.lng,
phone: $scope.user.phone,
website: $scope.user.website,
}
console.log(data);
$scope.users.$add(data).then(function (ref) {
console.log('contact added with Id: ' + id);;
})
Apparently the code is fine, but doesn't return console.log so maybe had some errors. Any ideas?
Well to find out if there is any error Javascript then takes to call back one for success and the other for failure something like the code below
$scope.user.$add(data).then(function (success){
return console.log(success);
}, function(error){
return console.log(error);
})
Now that's for then that way you can log returned errors
Error fixed.
$scope.users = $firebaseArray(root.ref('/users/'));
$scope.user = {};
//Create
$scope.title = "New user"
$scope.button = "Create";
$scope.icon = "ion-person-add";
$scope.submit = function () {
var data = {
name: $scope.user.name,
phone: $scope.user.phone,
email: $scope.user.email,
username: $scope.user.username,
city: $scope.user.city,
lat: $scope.user.lat,
lng: $scope.user.lng,
street: $scope.user.street,
suite: $scope.user.suite,
website: $scope.user.website,
zipcode: $scope.user.zipcode,
}
$scope.users.$add(data).then(function (response) {
if (response) {
$ionicPopup.alert({
title: '<div><i class="ion-checkmark-circled"></i></div>',
subTitle: '<h4>The user <b>' + data.name + '</b> is added.</h4>',
});
}
$scope.user=null;
With that all works properly and modal shows when we add user.

Saving simple user input in angular JS

I have built this simple todo app, however it resets the data every time the users page is reset, how can I save the data in the checklist list upon page refresh so the user can reference it later? Can this be done within the app or do I need a server database setup?
var checkList = angular.module('checkList', []);
checkList.filter('checkedItems', function () {
return function (items, showComplete) {
var resultArr = [];
angular.forEach(items, function (item) {
if (item.done == false || showComplete == true) {
resultArr.push(item);
}
});
return resultArr;
}
});
checkList.controller('CheckListCtrl', function($scope){
$scope.check = {
user: "Jim",
items: [ {action: "item1", done: false },
{action: "item2", done: false },
{action: "item3", done: false },
{action: "item4", done: false }]
};
$scope.incompleteCount = function () {
var count = 0;
angular.forEach($scope.check.items, function (item) {
if (!item.done) { count++ }
});
return count;
}
$scope.warningLevel = function () {
return $scope.incompleteCount()
< 3 ? "label-success" : "label-warning";
}
$scope.addNewItem = function (actionText) {
$scope.check.items.push({ action: actionText, done: false });
}
});
The easiest way is:
To save data in browser's local storage: localStorage.setItem('itemName', data) and then, to retrieve it back localStorage.getItem('itemName', data)

Event loop Angular. Some functions not running on app initialisation until a button is clicked? -FIREBASE

Background:
I have the following setup to authenticate retrieve my user and then retrieve his credentials. I am unclear on the event loop even after reading the documentation.
The Question:
The user is not displayed until I click a button? Every other kind of function runs on initialization like the alerts and stuff but why is my retrieve user function working until another button is pressed (pressing any button )?
Summary:
In order to retrieve the username for some reason I need to click something. I want the username to be retrieve on initialization .
crossfitApp.controller('globalIdCtrl', ["$scope",'$q','defautProfileData','$timeout', function ($scope,$q,defautProfileData,$timeout) {
$timeout(function() {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
$scope.myFbvar =null;
$scope.authenticated={
currentUser: null,
avatarUrl: "",
emailAddress: "",
settings: "",
currentUserid: null,
};
function getProfile(userID){
myprofile= new Firebase("https://glowing-fire-5401.firebaseio.com/profiles/"+userID+"/username");
myprofile.once('value', function(nameSnapshot) {
$scope.authenticated.currentUser = nameSnapshot.val();
});
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function(error, user) {
if (error) {
//Error
console.log ('error');
}
else if (user) {
//logged in
$scope.$apply(function(){getProfile(user.id);})
console.log('logged in');
$scope.authenticated.currentUserid = user.id ;//
}
else {
// user is logged out
console.log('logged out');
$scope.authenticated.currentUserid =null;
$scope.authenticated.currentUserid =null;
}
});
},100);
}]); //GlobaldCtrl
I would move most of your code to a service, and call the service from your controller, like this. I also included a deferred object in your login as I bet this is async
crossfittpApp.service('firebase',function($q) {
return {
getUser : function(authenticated) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com"),
myFbvar =null,
getProfile(userID) {
myprofile= new Firebase("https://glowing-fire-5401.firebaseio.com/profiles/"+userID+"/username");
myprofile.once('value', function(nameSnapshot) {
authenticated.currentUser = nameSnapshot.val();
});
},
deferredObj = $q.defer();
auth;
auth = new FirebaseSimpleLogin(dataRef, function(error, user) {
if (error) {
//Error
console.log ('error');
deferObj.reject();
}
else if (user) {
//logged in
getProfile(user.id);
console.log('logged in');
authenticated.currentUserid = user.id ;
deferObj.resolve(auth);
}
else {
// user is logged out
console.log('logged out');
authenticated.currentUserid =null;
deferObj.resolve();
}
}
return deferObj.promise;
}
}
});
crossfittpApp.controller('globalIdCtrl',function(firebase) {
$scope.authenticated = {
currentUser: null,
avatarUrl: "",
emailAddress: "",
settings: "",
currentUserid: null,
};
firebase.getUser(authenticated)
.then(function(_auth) {
$scope.auth = _auth;
},
function() {
//auth error here
});
});
You're not triggering Angular's HTML Compiler, so Angular doesn't know you've changed the JS variables.
Whenever you use an event like ng-click/ng-submit/etc, Angular fires $scope.$apply(), which checks for any changes to your $scope variables and applies them to the DOM, which is why it shows up after this.
You can correct this issue by alerting Angular that it needs to run $apply by using $timeout:
angular.controller('MyController', function($timeout) {
myprofile= new Firebase("https://glowing-fire-5401.firebaseio.com/profiles/"+userID+"/username");
myprofile.once('value', function(nameSnapshot) {
$timeout(function() {
authenticated.currentUser = nameSnapshot.val();
});
});
auth = new FirebaseSimpleLogin(dataRef, function(error, user) {
if (error) {
//Error
console.log ('error');
}
else if (user) {
$timeout(function() {
authenticated.currentUserid = user.id ;
});
}
else {
$timeout(function(){
authenticated.currentUserid =null;
});
}
});
});
You should utilize angularFire, which abstracts these complexities.
There are some more questions like this one here, here, and here.

Karma testing with .success() getting 'undefined' is not an object'

I'm trying to write a unit test to see if the 'getStudents()' provider function in my controller gets called if some properties are appropriately set. Notice the .success() callback:
$scope.update = function update() {
// omitted, just doing some checking...
// finally
else if (key.length === 3 || $scope.students.length === 0) {
StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success(function(data) {
$scope.students = data;
});
}
};
My karma unit test looks like this:
describe("Students: Controllers", function () {
var $scope;
var ctrl;
beforeEach(module('studentsApp'));
describe("SearchCtrl", function () {
// Mock the provider
var mockStudentsProvider = {
getStudents: function getStudents() {
return [
{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3", MobilePhone: "7777777"
}]
}
];
}
};
var StudentsProvider;
beforeEach(inject(function ($controller, $rootScope) {
$scope = $rootScope.$new();
ctrl = $controller('SearchCtrl', { $scope: $scope, StudentsProvider: mockStudentsProvider});
StudentsProvider = mockStudentsProvider;
}));
describe("Update", function () {
beforeEach(function () {
spyOn(StudentsProvider, 'getStudents');
});
it("should always call the provider with 3 letters", function () {
$scope.keyword = "axe";
$scope.update();
expect(StudentsProvider.getStudents).toHaveBeenCalled();
expect(StudentsProvider.getStudents).toHaveBeenCalledWith("axe", "");
});
});
});
});
When I run this, I get the following error:
TypeError: 'undefined' is not an object (evaluating 'StudentsProvider.getStudents($scope.keyword, $scope.selectedFilters).success')
and it's probably because I'm not mocking the .success() callback. How would I do that? Thanks in advance!
Replace this:
var mockStudentsProvider = {
getStudents: function getStudents() {
return [{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3",
MobilePhone: "7777777"
}]
}];
}
};
with this:
var mockStudentsProvider = {
getStudents: function getStudents() {
var retVal = [{
Education: [],
Person: [{
ID: 1,
Name: "Testing McTestsson",
SSN: "1234567890",
Address: "Fakestreet 3",
MobilePhone: "7777777"
}]
}];
return {
success: function(fn) {
fn(retVal)
};
}
}
};
And replace this:
spyOn(StudentsProvider, 'getStudents');
with this:
spyOn(StudentsProvider, 'getStudents').andCallThrough();
When you do not use andCallThrough() or andCallFake() jasmine prevents execution of the method and returns null. Inside your update method you are calling null.success. This will fail. (http://jasmine.github.io/1.3/introduction.html)
In your mock method you need to change the return format--the real http method returns an object where success refers to a function which takes an input a callback function.
In your case, the callback function is:
function(data) {
$scope.students = data;
}

AngularJS-MongoLab $resource update error

I am connecting AngularJS with MongoLab and trying to update the "users" collection.
AngularJS resource service:
angular.module("myApp.services", ["ngResource"])
.constant({
DB_URL: "https://api.mongolab.com/api/1/databases/mydb/collections/users/:id",
API_KEY: "[SOME_RANDOM_KEY]"
})
.factory("UsersService", ["$resource", "DB_URL", "API_KEY", function($resource, DB_URL, API_KEY) {
return $resource(DB_URL, { apiKey: API_KEY, id: "#_id.$oid" }, { update: { method: "PUT" }});
}]);
This is how I am trying to update my users collection:
angular.module("myApp", ["myApp.services"])
.controller("AppController", ["$scope", "UsersService", function($scope, UsersService) {
$scope.users = [];
$scope.getAllUsers = function() {
$scope.users = UsersService.query();
};
$scope.updateUser = function(user) { // user: firstName, lastName, email
//delete user._id;
UsersService.update({}, user, function() {
$scope.users = UsersService.query();
console.log("Users updated successfully");
}, function() {
console.log("Some problems updating the user", arguments);
});
};
}]);
When I try to update the user information, it throws an exception stating:
{ "message" : "cannot change _id of a document old:{ _id: ObjectId('[SOME_RANDOM_KEY]'), firstName: \"Anup\", lastName: \"Vasudeva\", email: \"anup.vasudeva#emal.com\" } new:{ _id: {}, firstName: \"Anup\", lastName: \"Vasudeva\", email: \"anup.vasudeva#email.com\" }"
I am new to MongoDB, so I don't understand why it is creating an empty _id object for the new user instance?
Try changing your $scope.updateUser function to the following:
$scope.updateUser = function (user) {
var userId = user._id.$oid;
user._id = undefined;
UsersService.update({id: userId}, user, function () {
$scope.users = UsersService.query();
console.log("Users updated successfully");
}, function () {
console.log("Some problems updating the user", arguments);
});
};
The update/replacement object ('user'), should not contain the _id property when being passed to UsersService.update(). Mongo will not replace the _id value, so the id will stay the same after the update.
One other thing that's changed in this function is that we're passing the user id as the first parameter of the UsersService.update() function so that mongo knows which document to update.

Resources