Angular passing data from factory to controller - angularjs

I'm trying to store an authorized user id variable, which I can pass to controllers. I know there's an issue with how I'm trying to pass the data from inside the closure of my factory object, but I'm stuck on how to fix it.
Here is my factory:
myApp.factory('Authentication', function($firebase,
$firebaseAuth, FIREBASE_URL, $location) {
var ref = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseAuth(ref);
var authorized;
var myObject = {
login : function() {
return simpleLogin.$authAnonymously().then(function(authData) {
authorized = authData.uid;
console.log("Logged in as:", authData.uid);
}).catch(function(error) {
console.error("Authentication failed:", error);
});
},
auth : authorized
} //myObject
return myObject;
});
Here is my controller:
myApp.controller('MeetingsController',
function($scope, $firebase, Authentication) {
var ref = new Firebase('http://i2b2icons.firebaseio.com/');
var meetings = $firebase(ref);
$scope.authid = Authentication.auth;
$scope.meetings = meetings.$asObject();
// $scope.id = = Authentication.login.id;
$scope.addMeeting=function() {
meetings.$push({
name: $scope.meetingname,
date: Firebase.ServerValue.TIMESTAMP
}).then(function() {
$scope.meetingname = '';
});
} //addmeeting
$scope.deleteMeeting=function(key) {
meetings.$remove(key);
} //deletemeeting
}); //MeetingsController
I'm really just trying to get the $scope.authid variable to pick up the value of auuthorized from the the login function of myObject.
The login method should have been called already by logging in via this controller:
myApp.controller('RegistrationController',
function($scope, $firebaseAuth, $location, Authentication) {
$scope.login = function() {
Authentication.login();
} //login
}); //RegistrationController

You are just setting the local variable authorized in your factory, it has no relation to the Authentication.auth that you are trying to access in your controller (unless ofcourse you set the value to it while creating the factor and which is not the intention anyway). Instead return a predefined object in your factory and return that object from it. Set the property on the object reference.
myApp.factory('Authentication', function($firebase,
$firebaseAuth, FIREBASE_URL, $location) {
var ref = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseAuth(ref);
//Predefine the factory
var factory = {
login: login,
authorized: null
};
function login() {
return simpleLogin.$authAnonymously().then(function(authData) {
factory.authorized = authData.uid; //Set the property here
}).catch(function(error) {});
}
//return it
return factory;
});
With this provided you have the reference of the factory and updates to its property will reflect (provided you invoke the method that populates the data) in your controller. Another way would be to use a getter function in your factory to return the auth object, or you can as well cache the promise returned by login function and return it and invalidate it when an event occurs that log the user out.

As others have already pointed out you only update the variable authorized, not the property auth. One rather simple solution is to change authto a getter, that always returns the current value:
var myObject = {
login : function() {
...
},
get auth() {
return authorized;
}
You don't have to change any other code.

Related

Angular : Wait for app run to get response

As of this post, I'm trying to figure out if the user is logged in (using a token based authentication).
The scheme is following :
1/ The page loads, app run is called, and authenticated is set to false as default
app.run(function($http, UserService) {
UserService.requestCurrentUser();
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
});
app.constant('AUTHENTICATED', false);
2/ UserService call for its method requestCurrentUser() in which a http get is sent to the correct url with the token in its header.
If token is correct, this sends back the user (success case, we're authenticated).
If not, I get a permission error (error case, we're not authenticated).
This updates currentUserproperty and AUTHENTICATED constant.
app.factory('UserService', function ($http, $q, $window, AUTHENTICATED) {
var _currentUser = {};
return {
getCurrentUser: function() {
return _currentUser;
},
setCurrentUser: function(user) {
_currentUser = user;
},
requestCurrentUser: function() {
return $http.get('/accounts/api/').then(
function (response) {
_currentUser = response.data;
AUTHENTICATED = true;
},
function (error) {
AUTHENTICATED = false;
}
);
},
};
});
3/ Controller is called and authenticated and currentUser scope values are updated.
app.controller('AuthCtrl', function ($scope, AuthService, UserService, AUTHENTICATED) {
$scope.authenticated = AUTHENTICATED;
$scope.currentUser = UserService.getCurrentUser();
});
Problem is that controller tries to reach the values before requestCurrentUser method (launched in app run) has received a response. So where should I launch requestCurrentUser to get the expected behavior ?
Thanks
What you could do it wrap your user state object in a parent object. For example:
var state = {
_currentUser: {}
};
return {
getUserState: function(){ return state; }
};
Then inside your controller:
$scope.state = UserService.getUserState();
This way, when your user updates (no matter when or how in your service), anything bound to the state will receive the update. So your controller will have access to state._currentUser when it is available.

How to return user data from Factory into a controller using Firebase

I have a factory which checks the authData of a user, using firebase. I wish to access the users details such as name, email etc, but I can't figure out how to get the snapshot data into an object which I can use. I'm new to Javascript.
this is my factory:
angular.module('.....')
.factory('UserDataService', function($q, $firebase, $firebaseAuth, FIREBASE_URL) {
var authData = {};
function authDataCallback(authData) {
if (authData) {
var ref = new Firebase(FIREBASE_URL + "/userProfiles/" + authData.uid);
ref.on("value", function(snapshot) {
var data = snapshot.val();
});
} else {
console.log("User is logged out");
}
}
// Register the callback to be fired every time auth state changes
var ref = new Firebase(FIREBASE_URL);
ref.onAuth(authDataCallback);
return authData;
});
2nd Attempt:
This time i am able to get the users details, but it won't save into the variable service and is returning to the controller as null. Here is my code:
var firebaseRef = new Firebase(FIREBASE_URL);
var authObj = $firebaseAuth(firebaseRef);
activate();
var service = {
userInfo: null
};
function activate() {
// Add listeners for authentication state changes
authObj.$onAuth(function(authData) {
if (authData) {
// Load the userInfo
loadUserInfo(authData);
} else {
// Destroy the userInfo Object if one exists
if (service.userInfo) {
service.userInfo.$destroy();
service.userInfo = null;
}
}
});
}
function loadUserInfo(authData) {
var userRef = firebaseRef.child('userProfiles').child(authData.uid);
var loadedInfo = $firebaseObject(userRef);
// console.log(loadedInfo);
loadedInfo.$loaded()
.then(function() {
service.userInfo = loadedInfo;
console.log(service.userInfo.name);
})
.catch(function(error) {
switch (error.code) {
case 'PERMISSION_DENIED':
alert('You don\'t have the permission to see that data.');
break;
default:
alert('Couldn\'t load the user info.');
}
});
}
return service;
You need to inject your service inside something (controller, service, filter or directive) and from the controller, call the service function.
.controller('myController', ['UserDataService', function($scope) {
$scope.userService = UserDataService;
}
Now you can call the function from your controller scope.
userService.authDataCallback()
You assign snapshot.val() to a local variable data. data is destroyed as soon as the function ends.
You need to assign it to the "outer" variable authData, which you can't because you have a function parameter with the same name.
Try it like this:
angular.module('.....')
.factory('UserDataService', function($q, $firebase, $firebaseAuth, FIREBASE_URL) {
var authData = {};
function authDataCallback(data) {
if (data) {
var ref = new Firebase(FIREBASE_URL + "/userProfiles/" + data.uid);
ref.on("value", function(snapshot) {
authData = snapshot.val();
//[see comment] var authData = snapshot.val();
});
} else {
console.log("User is logged out");
}
}
// Register the callback to be fired every time auth state changes
var ref = new Firebase(FIREBASE_URL);
ref.onAuth(authDataCallback);
return {
authData: authData
};
});
Also you should read up on the return types of service/factory in the docs. What the returned object of a factory does, is basically expose private variables/functions for other modules to access.
I suppose there is in var data an object.
You can access fields easily with dot .field. For example:
ref.once("value", function(snapshot) {
var data = snapshot.val();
// data is { "name": "Fred", "age": 53 }
// data.name === "Fred"
// data.age === 53
});
Depending on the data in a DataSnapshot, the val() method may return a
primitive (string, number, or boolean), an array, or an object. It may
also return null, indicating that the snapshot is empty and contains
no data.
Taken from:
https://www.firebase.com/docs/web/api/datasnapshot/val.html
I hope it helps.
-EDIT-
Use this format to get data in the controller:
var app = angular.module("sampleApp", ["firebase"]);
app.factory("Auth", ["$firebaseAuth",
function($firebaseAuth) {
var ref = new Firebase("https://docs-sandbox.firebaseio.com", "example3");
return $firebaseAuth(ref);
}
]);
app.controller("SampleCtrl", ["$scope", "Auth",
function($scope, Auth) {
$scope.auth = Auth;
// any time auth status updates, add the user data to scope
$scope.auth.$onAuth(function(authData) {
$scope.authData = authData;
});
}
]);
Assuming the snapshot is correct, the val() method turns it into an object.
Meaning data now contains the values you need. For example data.name.
The service should expose a function for this
function sendData() {
var deferred = $q.defer();
// Register the callback to be fired every time auth state changes
var ref = new Firebase(FIREBASE_URL);
ref.onAuth(function(snapshot) {
deferred.resolve(snapshot.val());
});
return deferred.promise;
}
Something like this
Cheers

AngularJS Data from a Service

So I have created a Notifications service in AngularJS and well I'm not getting any meaning full data back in my $scope.Notifications variable.
I can see the service is being called and running at the correct interval, and the correct data is being returned from the API:
[{"id":1,"user_id":1,"content":"You have new mail: Test","read":null,"type":"mail","deleted_at":null,"created_at":"2015-06-23 20:16:38","updated_at":"2015-06-23 20:16:38"},{"id":2,"user_id":1,"content":"You have new mail: Test","read":null,"type":"mail","deleted_at":null,"created_at":"2015-06-23 20:16:38","updated_at":"2015-06-23 20:16:38"},{"id":3,"user_id":1,"content":"You have new mail: Test","read":null,"type":"mail","deleted_at":null,"created_at":"2015-06-23 20:16:38","updated_at":"2015-06-23 20:16:38"}]
Essentially, all I need from this is a simple array of the users notifications.
Here is my service:
app.services.Notifications = ['$http', '$timeout', function($http, $timeout){
var timeoutId;
var notificationService = this;
function checkForNotifications(){
console.log('checking')
return $http.get('/api/notifications')
.then(function(res){
return res.data.filter(function(notification){
return notification.unread === true;
})
})
.then(function(unreadNotifications){
//fake for effect
notificationService.count = Math.floor(Math.random() * (100 - 1)) + 1;
//notificationService.count = unreadNotifications.length;
})
.then(waitAndCheck)
}
function waitAndCheck(){
return $timeout(function(){
checkForNotifications()
},5000);
}
return {
Notifications: waitAndCheck()
}
}];
And my controller:
app.controllers.notificationsController = ['$scope', 'Notifications', function($scope, Notifications) {
$scope.Notifications = Notifications;
}];
If I console log $scope.Notifications in the controller I being return this:
Object {Notifications: d}Notifications: d$$state: Objectstatus: 1value: undefined__proto__: Object$$timeoutId: 1__proto__: Object__proto__: Object
Set your $scope.Notifications = Notifications.Notifications;
app.controllers.notificationsController = ['$scope', 'Notifications', function($scope, Notifications) {
$scope.Notifications = Notifications.Notifications;
}];
Currently the thing which you are getting in console is nothing but a promise object which is conceptually correct. If you want the data from it then you use resolve that promise chain using .then function on Notification service.
Notifications.Notifications.then(function(data){
$scope.Notifications = data;
})

Angular : Share data returned from factory between controllers

I havea factory call HTTP end point to fetch user:
.factory('me', function($resource, VAS_API_URL, $q) {
return {
getUser: function() {
var deferred = $q.defer();
var url = VAS_API_URL + 'api/me';
$resource(url)
.get(function(user) {
deferred.resolve(user);
}, function(response) {
deferred.reject(response);
});
return deferred.promise;
}
};
})
I use this factory in many controllers and, i bind the data to the DOM successfully, but at times i want to use the data returned from the factory in my controller like to save the user name with a notification so i had to do the following:
.controller('createNotificationCtrl', function($scope, VAS_API_URL, me) {
var Burl = VAS_API_URL + 'api/notification';
$scope.profile = me.getUser();
$scope.profile.then(
function(user) {
$scope.owner = user;
$scope.item = {};
$scope.item.message = $scope.message;
$scope.item.owner = $scope.owner.displayName;
},
function(response) {
console.log(response);
}
);
})
I had to creat $scope.item = {}; in the factory call so i could get the displayName then send the object to save a notification but what if i want to save also another filed from another factory. How it could be done?
The factory should create the object and hold it as a state.
The controller's should use the reference from the factory and update it for everyone.
factory code sample:
this.stateObj = {};
....
....
this.getUser = function(){
var promise = $http.get(url);
promise.then(function(res){
// update stateObj
});
return promise;
}
ctrl code sample:
factory.getUser().then(function(res){
$scope.stateObj = factory.getStateObj();
});

How to get the authenticated user info and use in all controllers and services?

I'm using angularFireAuth and I want to retrieve the logged in user's info and use in
all the controllers or services when the app is initial.
Currently, I used this in every controller but i having some problem.
$scope.$on("angularFireAuth:login", function(evt, user){
console.log(user);
});
The callback will not call if it is not a full page load or return null when app initial.
I need some tips for how can I return the authenticated user's info so I can use when app is initial and in all the controllers and services.
Example
When in controller or services
$scope.auth.user.id will return user's ID
$scope.auth.user.name will return user's name
etc
I would start with a userService for this:
angular.module('EventBaseApp').service('userService', function userService() {
return {
isLogged: false,
username: null
}
});
And write a LoginCtrl controller:
angular.module('EventBaseApp')
.controller('LoginCtrl', function ($scope, userService, angularFireAuth) {
var url = "https://example.firebaseio.com";
angularFireAuth.initialize(url, {scope: $scope, name: "user"});
$scope.login = function() {
angularFireAuth.login("github");
};
$scope.logout = function() {
angularFireAuth.logout();
};
$scope.$on("angularFireAuth:login", function(evt, user) {
userService.username = $scope.user;
userService.isLogged = true;
});
$scope.$on("angularFireAuth:logout", function(evt) {
userService.isLogged = false;
userService.username = null;
});
});
Inject the userService anywhere you want the user.
My app that am currently working on that uses this - https://github.com/manojlds/EventBase/blob/master/app/scripts/controllers/login.js
Based on ideas presented here - http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app
i'm not sure quite what your question is. but if you are looking to authorise once rather than in each controller, you can put the code into the module instead and put it into the $rootScope.
var myapp = angular.module('myapp').run(
function ($rootScope) {
$rootScope.user = null;
$rootScope.$on("angularFireAuth:login", function (evt, user) {
$rootScope.user = user;
});
});

Resources