I have been implementing a login feature in my Angular app and have it working for the most part. I am trying to show the userName in the header section. But it won't update after they login until the refresh the screen. What do I need to add to refresh that data?
I am including my header bar like so:
<div data-ng-include="'app/layout/topnav.html'"></div>
Here is my top nav section that I need the two way binding for Session.
(function() {
'use strict';
angular
.module('app.layout')
.controller('TopNav', TopNav);
TopNav.$inject = ['$route', 'routehelper', 'Session', 'authentication'];
function TopNav($route, routehelper, Session, authentication) {
/*jshint validthis: true */
var vm = this;
var routes = routehelper.getRoutes();
vm.isCurrent = isCurrent;
vm.userName = Session.userName;
activate();
function activate() { getNavRoutes(); }
function getNavRoutes() {
vm.navRoutes = routes.filter(function(r) {
return r.settings && r.settings.nav;
}).sort(function(r1, r2) {
return r1.settings.nav - r2.settings.nav;
});
}
function isCurrent(route) {
if (!route.title || !$route.current || !$route.current.title) {
return '';
}
var menuName = route.title;
return $route.current.title.substr(0, menuName.length) === menuName ? 'current' : '';
}
vm.logout = function() {
vm.userName = null;
authentication.logout();
};
}
})();
Here is my Session Service:
(function() {
'use strict';
angular.module('blocks.authentication').service('Session', function() {
this.create = function(userId, userName, userRole) {
this.userId = userId;
this.userName = userName;
this.userRole = userRole;
};
this.destroy = function() {
this.userId = null;
this.userName = null;
this.userRole = null;
};
return this;
});
})();
Including the nav like this, it only runs once. So once the Session is actually set on my log in page, it does not update here. Am I missing something? What is a better way to achieve this?
There are a couple of options, the first should work.
First, instead of setting vm.userName = Session.userName you should change it to vm.userName = Session.getUserName() where Session.getUserName is like:
function getUserName() {
return this.userName;
}
This is because when the controller is instantiated it sets vm.userName to whatever the value of Session.userName is at that point in time. It never knows about the change. Setting it to a function, it should check for that during every $digest cycle.
Otherwise, you could use a good old fashioned observer pattern to solve the issue.
In the TopNav controller:
// Register the setting of vm.userName as an observer
Session.registerObserverCallback(function () {
vm.userName = Session.userName;
});
And then in Session:
this.create = function(userId, userName, userRole) {
this.userId = userId;
this.userName = userName;
this.userRole = userRole;
// Everytime this changes, notify everyone
this.notifyObservers();
};
// Since almost everything in angular is a singleton, anyone can register here
// and whenever notifyObservers() is called, everyone gets notified of the new values
var observerCallbacks = [];
var registerObserverCallback = function (callback) {
observerCallbacks.push(callback);
};
var notifyObservers = function () {
angular.forEach(observerCallbacks, function (callback) {
callback();
});
};
This is a sample Controller to display the injected Session details. I assumed, the Session object contains the data. Session is the generated service.
angular.module('myApp')
.controller('SessionController', function($scope, Session) {
$scope.userName = Session.userName;
}
Sample usage
<div>{{userName}}</div>
Related
I'm using a service in order to pass data between different instances of an AngularJS controller. I know that this is not the best way to do it but it's the way that fits my case. The problem is that I cannot get data out of that Service.
var app = angular.module('MovieApp', ['ngResource']);
app.factory('factMovies', function($resource) { //this returns some movies from MongoDB
return $resource('/movies');
});
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function() {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
return tempDataset;
}
});
};
});
app.controller('TileController', function(SnapshotService) {
this.dataset = [];
this.filters = [];
this.init = function() {
var ctrl = this;
var data = SnapshotService.init(function() {
console.log(ctrl.data); //doesn't even get to the callback function
});
};
});
I really can't figure out what I'm doing wrong..
SnapshotService.init() doesn't take any parameters - meaning the anonymous function you pass in with the SnapshotService.init() call in TileController does nothing.
What you need to do is add the parameter to the init function definition and then call it in the code:
app.service('SnapshotService', function(factMovies) {
//this is used to pass data to different instances of the same controller
//omitted getters/setters
this.snapshots = [];
this.init = function(cb) {
var ctrl = this;
var resp = factMovies.query({}, function() {
if (resp.error) {
console.log(resp.error)
} else {
tempDataset = []
//do stuff and put the results in tempDataset
ctrl.snapshots.push(tempDataset);
console.log(tempDataset); //prints fine
cb(ctrl.snapshots);
}
});
};
});
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
I'm trying to write an AngularJS library for Pusher (http://pusher.com) and have run into some problems with my understanding of the digest loop and how it works. I am writing what is essentially an Angular wrapper on top of the Pusher javascript library.
The problem I'm facing is that when a Pusher event is triggered and my app is subscribed to it, it receives the message but doesn't update the scope where the subscription was setup.
I have the following code at the moment:
angular.module('pusher-angular', [])
.provider('PusherService', function () {
var apiKey = '';
var initOptions = {};
this.setOptions = function (options) {
initOptions = options || initOptions;
return this;
};
this.setToken = function (token) {
apiKey = token || apiKey;
return this;
};
this.$get = ['$window',
function ($window) {
var pusher = new $window.Pusher(apiKey, initOptions);
return pusher;
}];
})
.factory('Pusher', ['$rootScope', '$q', 'PusherService', 'PusherEventsService',
function ($rootScope, $q, PusherService, PusherEventsService) {
var client = PusherService;
return {
subscribe: function (channelName) {
return client.subscribe(channelName);
}
}
}
]);
.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) {
$scope.items = [];
var channel = Pusher.subscribe('items')
channel.bind('new', function(item) {
console.log(item);
$scope.items.push(item);
})
}]);
and in another file that sets the app up:
angular.module('myApp', [
'pusher-angular'
]).
config(['PusherServiceProvider',
function(PusherServiceProvider) {
PusherServiceProvider
.setToken('API KEY')
.setOptions({});
}
]);
I've removed some of the code to make it more concise.
In the ItemListController the $scope.items variable doesn't update when a message is received from Pusher.
My question is how can I make it such that when a message is received from Pusher that it then triggers a digest such that the scope updates and the changes are reflected in the DOM?
Edit: I know that I can just wrap the subscribe callback in a $scope.$apply(), but I don't want to have to do that for every callback. Is there a way that I can integrate it with the service?
On the controller level:
Angular doesn't know about the channel.bind event, so you have to kick off the cycle yourself.
All you have to do is call $scope.$digest() after the $scope.items gets updated.
.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) {
$scope.items = [];
var channel = Pusher.subscribe('items')
channel.bind('new', function(item) {
console.log(item);
$scope.items.push(item);
$scope.$digest(); // <-- this should be all you need
})
Pusher Decorator Alternative:
.provider('PusherService', function () {
var apiKey = '';
var initOptions = {};
this.setOptions = function (options) {
initOptions = options || initOptions;
return this;
};
this.setToken = function (token) {
apiKey = token || apiKey;
return this;
};
this.$get = ['$window','$rootScope',
function ($window, $rootScope) {
var pusher = new $window.Pusher(apiKey, initOptions),
oldTrigger = pusher.trigger; // <-- save off the old pusher.trigger
pusher.trigger = function decoratedTrigger() {
// here we redefine the pusher.trigger to:
// 1. run the old trigger and save off the result
var result = oldTrigger.apply(pusher, arguments);
// 2. kick off the $digest cycle
$rootScope.$digest();
// 3. return the result from the the original pusher.trigger
return result;
};
return pusher;
}];
I found that I can do something like this and it works:
bind: function (eventName, callback) {
client.bind(eventName, function () {
callback.call(this, arguments[0]);
$rootScope.$apply();
});
},
channelBind: function (channelName, eventName, callback) {
var channel = client.channel(channelName);
channel.bind(eventName, function() {
callback.call(this, arguments[0]);
$rootScope.$apply();
})
},
I'm not really happy with this though, and it feels as though there must be something bigger than I'm missing that would make this better.
JavaScript and AngularJS is still new to me.
I have trouble getting variables from my services into my scope. Here is what I'm doing:
myControllers.controller('UserController', function($scope, UserService) {
clearLoginForm();
updateStuff();
$scope.notifications = UserService.notifications;
function updateStuff() {
$scope.realName = UserService.realName;
$scope.loggedIn = UserService.loggedIn;
}
function clearLoginForm() {
$scope.loginName = '';
$scope.loginPassword = '';
}
$scope.login = function() {
UserService.login($scope.loginName,$scope.loginPassword);
updateStuff();
clearLoginForm();
}
$scope.logout = function() {
UserService.logout();
updateStuff();
clearLoginForm();
}
});
the UserService should hold the information about the logged in User and the functions for login/logout and account related stuff that should be polled from the server.
myModule.factory('UserService', function($interval) {
var loggedIn = false;
var realName = "";
var notifications = {};
resetNotifications();
function resetNotifications() {
notifications.msgCount = 0;
notifications.todoCount = 0;
notifications.ergCount = 0;
}
function login(name, password) {
if (password === 'abc') {
loggedIn = true;
realName = 'John Smith';
}
};
function logout() {
loggedIn = false;
realName = '';
resetNotifications();
}
function updateNotifications() {
if (loggedIn) {
notifications.msgCount = 1;
}
else {
resetNotifications();
}
};
$interval(updateNotifications, 10000);
return {
loggedIn : loggedIn,
realName : realName,
login : login,
logout : logout,
notifications : notifications
};
});
But it's not working. So I noticed that if I change "loggedIn" in the login/logout functions to "this.loggedIn" (and same with realName) then the values get propagated to the scope. Why do I need the "this"? Aren't "loggedIn" and "realName" in my closure? But this is not a solution since this is now a different "loggedIn" than used in the updateNotifications function (and here I can't change it to "this.loggedIn").
Secondly, I don't like that I need to call the updateStuff() Function everytime a value in the service changes. But it's not working without. Is there a better way to do it?
I fear I'm doing something fundamentally wrong.
When you return loggedIn at the end of your service, what you're really returning is a copy of the variable. So when you later update it inside of UserService, the copy won't get updated. The easiest way to solve that is with the getter approach:
function getLoggedIn() { return loggedIn; }
return {
getLoggedIn: getLoggedIn
};
your answer helped. but I found a better way to do it. I just wrapped loggedIn and realName in a real object
var userStatus = {
loggedIn : false,
realName : ""
};
and using that one in the controller. That also removes the necessity of the updateStuff function. I hope that won't cause any other trouble.
I am looking for a solid way to have the 'current user id' in all my controllers available.
Using: Firebase Simple Login : Email / Password Authentication
My ida: I need a 'Factory' wich I can inject into my controllers,
to have the 'current user id' always available.
I came up with this code:
app.factory('User', ['angularFire',
//Get Current UserID
function(angularFire){
console.log ('FACTORY: User');
var currentUser = {};
var ReturnStr = '';
var ref = new Firebase("https://myFIREBASE.firebaseio.com/");
var authClient = new FirebaseAuthClient(ref, function (err, user) {
if (err) {
ReturnStr = 'FACTORY: User Error: ' + err;
console.log (ReturnStr);
//var User = ReturnStr;
} else if (user) {
console.log ('FACTORY: User: Login successfully:');
console.log (user);
currentUser = user;
} else {
//console.log ('-----------User: Logged Out ---------------');
ReturnStr = 'FACTORY: Logged out: Redirect to Login';
console.log (ReturnStr);
window.location.href = "/login.php";
}
});
return currentUser;
}
]);
My simplest Controller looks like:
function ToDoCtrl($scope, User) {
$scope.MyUser = User;
$scope.MyUser.test = 'Test';
}
In HTML (angular partials) i have:
<h2>{{MyUser.id}}</h2>
<h2>{{MyUser.email}}</h2>
<h2>{{MyUser.provider}}</h2>
<h2>{{MyUser.test}}</h2>
=> id, email, provider are 'undefined'. In console I see the 'FACTORY: User: Login successfully:' with correct user - Object.
=> Asynchronous loading of data problem?
I have also experimented (without luck):
$timeout(function () {
currentUser = user;
}
Such a FACTORY would be very useful!
Thanks for a pointing me in the right direction!
Edit 1.1: Now, with $rootscope hack
=> Same effect - mycontroller is too fast - factory to slow.
app.factory('User', ['$rootScope', '$timeout', 'angularFire',
//Aktueller Benutzer auslesen
function($rootScope, $timeout, angularFire){
console.log ('FACTORY: User');
var currentUser = {};
var ReturnStr = '';
var ref = new Firebase("https://openpsychotherapy.firebaseio.com/");
var authClient = new FirebaseAuthClient(ref, function (err, user) {
if (err) {
ReturnStr = 'FACTORY: User Error: ' + err;
console.log (ReturnStr);
//var User = ReturnStr;
} else if (user) {
console.log ('FACTORY: User: Login successfully:');
//currentUser = user;
$timeout(function () {
ReturnStr = 'FACTORY: Inside timout';
console.log (ReturnStr);
currentUser = user;
console.log (currentUser);
$rootScope.myUser = user;
$rootScope.myUserID = user.id;
$rootScope.loggedIn = true;
$rootScope.$apply();
return currentUser;
});
} else {
//console.log ('-----------User: Logged Out ---------------');
ReturnStr = 'FACTORY: Logged out: Redirect to Login';
console.log (ReturnStr);
//var User = ReturnStr;
window.location.href = "/login.php";
}
});
return currentUser;
}
]);
TAHNKS for any helpful suggestions! Wonderin how others solve this!
So here is my solution to this exact problem. I am using Firebase, FirebaseAuthClient, and angularFire for my Angular app. I ran into the same situation for my login system where you cannot inject the $scope into the factory and therefore I came up with making a controller that used a factory for it's methods to retrieve, add, update, and delete things. And in the controller, I have my firebaseAuth stuff going on, the setting of the User values, and references whick I assign to the scope of that. Once the user is logged in, they are redirected to another location, at which point the app.js file takes over with a child controller when at that address location.
My login also uses localStorage, so logins will persist, you can refresh and not have to keep logging in, and you can change it to be cookies or sessionStorage easy enough.
This is going to need to be adapted for your needs specifically if you choose to use this method, it's quite complex no matter what, but this is very solid for me and I'm no longer needing to worry about firebaseAuth or angularFire stuff now that my factories are all setup for passing data back and forth. I'm just doing angularJS stuff mostly with directives. So here's my code.
NOTE: This will need modifying, and some things will be pseudo or open-ended for you to figure out for your needs.
AuthCtrl.js
'use strict';
angular.module('YOUR_APP', []).
controller('AuthCtrl', [
'$scope',
'$location',
'angularFire',
'fireFactory',
function AuthCtrl($scope, $location, angularFire, fireFactory) {
// FirebaseAuth callback
$scope.authCallback = function(error, user) {
if (error) {
console.log('error: ', error.code);
/*if (error.code === 'EXPIRED_TOKEN') {
$location.path('/');
}*/
} else if (user) {
console.log('Logged In', $scope);
// Store the auth token
localStorage.setItem('token', user.firebaseAuthToken);
$scope.isLoggedIn = true;
$scope.userId = user.id;
// Set the userRef and add user child refs once
$scope.userRef = fireFactory.firebaseRef('users').child(user.id);
$scope.userRef.once('value', function(data) {
// Set the userRef children if this is first login
var val = data.val();
var info = {
userId: user.id,
name: user.name
};
// Use snapshot value if not first login
if (val) {
info = val;
}
$scope.userRef.set(info); // set user child data once
});
$location.path('/user/' + $scope.userRef.name());
} else {
localStorage.clear();
$scope.isLoggedIn = false;
$location.path('/');
}
};
var authClient = new FirebaseAuthClient(fireFactory.firebaseRef('users'), $scope.authCallback);
$scope.login = function(provider) {
$scope.token = localStorage.getItem('token');
var options = {
'rememberMe': true
};
provider = 'twitter';
if ($scope.token) {
console.log('login with token', $scope.token);
fireFactory.firebaseRef('users').auth($scope.token, $scope.authCallback);
} else {
console.log('login with authClient');
authClient.login(provider, options);
}
};
$scope.logout = function() {
localStorage.clear();
authClient.logout();
$location.path('/');
};
}
]);
And now for the nice and simple yet quite reusable factory. You will need to set your Firebase path for your app for the baseUrl variable for it to work.
fireFactory.js
'use strict';
angular.module('YOUR_APP').
factory('fireFactory', [
function fireFactory() {
return {
firebaseRef: function(path) {
var baseUrl = 'https://YOUR_FIREBASE_PATH.firebaseio.com';
path = (path !== '') ? baseUrl + '/' + path : baseUrl;
return new Firebase(path);
}
};
}
]);
Info
You give the factory just a piece of the path reference such as 'users' which will be used as part of the full path ref to where you want to store your user data.
fireFactory.firebaseRef('users')
Once you have a reference set for a user, they won't need to be set again it will just use the existing data and .auth() to it. Additionally if there's an existing 'token' in localStorage it will use that to auth() the user too.
Otherwise, it will login() the user and pop open the Oauth windows for them to do so using whatever option you provide them.
I have spent a lot of time, many many hours days and yes even months searching for something better than this when it comes to Firebase/FirebaseAuthClient and angularFire. With the way the Firebase API and FireAuth API is, it's very annoying to make them play nicely with each other when using them with angularFire anyways. It's very frustrating but I've gotten past it finally.
If you want to check out the code for my app and see how I'm doing these things more completely, you can find it in this branch of my Webernote github repo.
Feel free to fork it, install and run it locally, or contribute to it even if you like. I could use some help myself :)
Hope this helps you!!
Here's the way I do it.
First of all I have my firebase auth service (I'm not using Angularfire) that calls off to Singly to handle logins. When the user status changes it $broadcasts an event.
p4pApp.factory('firebaseAuth', function($rootScope, singlyAuth) {
var auth = {},
FBref = new Firebase(p4pApp.FIREBASEPATH);
auth.login = function(service) {
singlyAuth.login(service);
};
auth.logout = function() {
FBref.unauth();
auth.user = null;
auth.broadcastAuthEvent();
};
auth.broadcastAuthEvent = function() {
$rootScope.$broadcast('authEvent');
};
auth.authWithToken = function(token) {
if (token !== undefined) {
FBref.auth(token, function(error, authData) {
if(!error) {
auth.user = authData.auth.account;
auth.broadcastAuthEvent();
} else {
auth.user = null;
auth.broadcastAuthEvent();
}
}, function(error) {
auth.user = null;
auth.broadcastAuthEvent();
});
}
};
return auth;
});
Then I have a 'top level' controller that looks after authorisation state.
var AuthCtrl = function($scope, firebaseAuth, singlyAuth, firebase, user) {
$scope.user = null;
$scope.logout = function() {
firebaseAuth.logout();
};
$scope.isLoggedIn = function() {
return !!$scope.user;
};
// src: Alex Vanston (https://coderwall.com/p/ngisma)
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
$scope.$on('authEvent', function() {
$scope.safeApply(function() {
$scope.user = firebaseAuth.user;
});
user.setID(firebaseAuth.user);
if (firebaseAuth.user) {
firebase.fetch(['users', firebaseAuth.user], function(results) {
if (results) {
user.setData(results);
} else {
results = {};
results.createdAt = DateTimeStamp();
}
results.lastLogin = DateTimeStamp();
firebase.set('users', firebaseAuth.user, results);
});
} else {
user.clearData();
}
});
};
Finally, I use a dedicated user service to maintain user state. (It's still in development)
p4pApp.factory('user', function() {
var userService = {}, user={};
user.data = {};
userService.setID = function(id) {
user.id = id;
};
userService.getID = function() {
return user.id;
};
userService.setData = function(data) {
user.data = data || {};
};
userService.getData = function() {
return user.data;
};
userService.clearData = function() {
user.data = {};
};
userService.setDataField = function(field, data) {
user.data[field] = data;
};
userService.clearDataField = function(field) {
delete user.data[field];
};
userService.pushData = function(key, data) {
if (typeof(key) === 'string') {
user.data[key] = data;
} else {
_.reduce(key, function(obj, child, index, list) {
obj[child] = obj[child] || {};
if (index == list.length-1) {
obj[child] = data;
}
return obj[child];
}, user.data);
}
};
userService.deleteData = function(key) {
if (typeof(key) === 'string') {
delete user.data[key];
} else {
_.reduce(key, function(obj, child, index, list) {
obj[child] = obj[child] || {};
if (index == list.length-1) {
delete obj[child];
return;
}
return obj[child];
}, user.data);
}
};
return userService;
});
This is most probably due to async nature of the call. To fix it you would have to
Inject $scope into the factory function (similar to angularFire dependency)
Use $scope.$apply() after the assigment currentUser = user;
Another solution that works for me:
account.service.js
(function() {
'use strict';
angular
.module('app.account')
.factory('Account', Account);
Account.$inject = [
'$firebaseAuth',
'$firebaseObject'
];
/* #ngInject */
function Account(
$firebaseAuth,
$firebaseObject,
) {
var firebaseRef = new Firebase('https://<<-- MY_FIREBASE -->>.firebaseio.com');
var authObj = $firebaseAuth(firebaseRef);
var service = {
userInfo: null
};
activate();
return service;
////////////////
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('users').child(authData.uid);
var loadedInfo = $firebaseObject(userRef);
loadedInfo.$loaded()
.then(function() {
service.userInfo = loadedInfo;
})
.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.');
}
});
}
}
})();
some-component.controller.js
(function() {
'use strict';
angular
.module('app')
.controller('SomeController', SomeController);
SomeController.$inject = [
'Account'
];
/* #ngInject */
function SomeController(
Account
) {
var vm = this;
vm.userInfo = userInfo;
////////////////
function userInfo() {
return Account.userInfo;
}
...
}
})();
some-component.html
...
<div class="user-id">
{{vm.userInfo().id}}
<div>
...