I've seen other posts about problems with $save(), but I couldn't relate it to my code. I have a profile controller with an updateProfile() method that ultimately attempts to save the new data to the database after it has been changed.
I have defined my profile controller as follows:
angular.module('angularfireSlackApp')
.controller('ProfileCtrl', function($state, md5, profile) {
var profileCtrl = this;
var currentUser = firebase.auth().currentUser;
profileCtrl.profile = profile;
// Retrieves the user's email from input field, hashes it, and saves the data to the database
profileCtrl.updateProfile = function() {
console.log(profileCtrl.profile); // Profile object exists and is populated as expected
profileCtrl.profile.emailHash = md5.createHash(currentUser.email);
profileCtrl.profile.$save();
}
});
My user service:
angular.module('angularfireSlackApp')
// Provides a means of retrieving User data or list of all Users
.factory('Users', function($firebaseArray, $firebaseObject) {
// Provides a means of retrieving User data or list of all Users
// Create a reference to users that can be used to retrieve an array of users
var usersRef = firebase.database().ref("users");
users = $firebaseArray(usersRef);
var Users = {
// Returns a firebase object of a specific user's profile
getProfile: function(uid) {
return $firebaseObject(usersRef.child(uid));
},
// Returns the display name of a specific user
getDisplayName: function(uid) {
return users.$getRecord(uid).displayName;
},
// Returns the Gravatar url that corresponds to the user
getGravatar: function(uid) {
return 'www.gravatar.com/avatar/' + users.$getRecord(uid).emailHash;
},
// Returns a firebase array of users
all: users
};
return Users;
});
Profile state from main app:
.state('profile', {
url: '/profile',
controller: 'ProfileCtrl as profileCtrl',
templateUrl: 'users/profile.html',
resolve: {
auth: function($state) {
firebase.auth().onAuthStateChanged(function(user) {
if (user == null) {
$state.go('home');
console.log("In auth but user NOT valid");
} else {
console.log("In auth and user valid");
}
});
},
profile: function(Users) {
firebase.auth().onAuthStateChanged(function(user) {
if (user != null) {
console.log("In profile and user valid");
return Users.getProfile(user.uid).$loaded();
} else {
console.log("In profile but user NOT valid");
}
});
}
}
});
console.log:
For some reason I'm getting an error that profileCtrl.profile.$save() is not a function. I know that the profileCtrl.profile object is legitimate and that I'm using $save() appropriately, but I just can't figure out what else could be the problem.
My gut feeling is that I'm missing something simple, but I'm brand new to AngularJS and Firebase so I wouldn't be surprised.
In the resolve of your "profile" state you are not returning anything.
You should fix it to:
auth: function($state, $firebaseAuth) {
return $firebaseAuth().$onAuthStateChanged().then(function(user) {
if (!user) {
$state.go('home');
console.log("In auth but user NOT valid");
} else {
console.log("In auth and user valid");
}
}); // classic situation to use $requiresAuth()
},
profile: function(Users, $firebaseAuth) {
return $firebaseAuth().$requireSignIn();
}
Moreover, your service shouldn't keep the reference to the $firebaseArray but create it for each controller that wants to use it.
The down-side is you'll have to make some changes, but the up-side is a more predictable, maintainable code:
var Users = {
// Returns a firebase object of a specific user's profile
getProfile: function(uid) {
return $firebaseObject(usersRef.child(uid));
},
// Returns the display name of a specific user
getDisplayName: function(uid) {
// This is actually an issue to get the name synchronously, but I see no reason why not using the Firebase SDK and fetch a-sync
//return users.$getRecord(uid).displayName;
return usersRef.child(uid)
.child('displayName')
.once('value')
.then(function (snap) {
return snap.val();
});
},
// Returns the Gravatar url that corresponds to the user
getGravatar: function(uid) {
// Again fetch a-synchronously
//return 'www.gravatar.com/avatar/' + users.$getRecord(uid).emailHash;
return usersRef.child(uid)
.child('emailHash')
.once('value')
.then(function (snap) {
return 'www.gravatar.com/avatar/' + snap.val();
});
},
// Returns a firebase array of users
all: function () {
return $firebaseArray(usersRef);
}
};
Also refer to the new AngularFire docs: API reference
Related
I am trying to detect the userstate. If the user is logged in I want to set the data "userstate" to true. I am using vuefire and firebase into my vue project. I tried the way shown below, but it does not work
data() {
return {
userstate:false
};
},
watch:{
userstate:{
firebase.auth().onAuthStateChanged(function(user){
if(user){
this.userstate= true;}
else{
this.userstate=false;
}
})}
In Firebase you can check whether the user is signed in or not by using a function provided by the firebase which is auth().currentUser
// user will return true which means user EXISTS!
let user = firebase.auth().currentUser;
if (user) {
this.userstate = true; // If it exists
} else {
this.userstate = false; // If it doesn't
}
There are cases when the above mentioned method returns null / undefined for the user. So this solution is for your existing solution. So in that case try modifying your existing function to this:
async function IsLoggedIn() {
try {
await new Promise((resolve, reject) =>
firbase.auth().onAuthStateChanged(
user => {
if (user) {
// Yes User is signed in.
resolve('User is there');
} else {
// No user is not signed in.
reject('There is no user');
}
},
// Prevent console errors
error => reject(error)
)
)
return true
} catch (error) {
return false
}
}
Also since you intend to watch for the auth state change you can simply register the listener right after you initialize Firebase, you do not necessarily have to insert it in a VueJS watch block, you can insert it in your main.js for example, and if you are using a store like VueX you can update the state in the store and pull that information from any component of the VueX application.
firebase.initializeApp(configOptions);
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.userstate = true;
} else {
this.userstate = false;
}
});
I have an app using sign in with a custom token, written on webpack observes. What I want to do now is mark the user after successful login by custom token as logged on firebase auth and firebase firestore, where I have the collections with users, and document for each user with data and some uid. I don't know how to to that.
Here is my code:
generateToken(uid) {
const uid = 'some-uid';
this.trigger(this.signals.action.onGenerateToken);
firebase.admin.auth().createCustomToken(uid)
.then((customToken) => {
console.log(customToken);
})
.catch(function (error){
if (error.Code === 'auth/invalid-custom-token') {
alert('The token you provided is not valid.');
}
else {
this.trigger(this.signals.error.onGenerateToken);
}
})
}
login(uid) {
firebase.auth().signInWithCustomToken(token)
.then(function() {
var user = firebase.auth().currentUser;
if (user) {
//mark the user as active (logged) after successful login on firebase auth and firebase firestore
};
this.trigger(this.signals.success.onLogin);
})
.catch(function(error) {
if (errorCode === 'auth/too-many-requests') {
this.trigger(this.signals.error.tooManyRequests);
}
else {
this.trigger(this.signals.error.userDisabled);
}
});
}
If I understand your question correctly, first create a reference to your user document, then call update() on the reference and pass in an object containing the properties you want to update and their new values.
let userRef = firebase.database().ref('users/' + userId);
userRef.update({active:true});
Check the firebase docs for more info on how to read and write to firebase.
I created a query that is in a service.TS file that shows an items "state" that is based on a logged in user's UID:
getLists(): FirebaseListObservable<any> {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {console.log("blah", firebase.auth().currentUser.uid)
// User is signed in.
}
});
return this.db.list('/foods', {
query: {
orderByChild: 'state/'+firebase.auth().currentUser.uid+'/state',
equalTo: 'listed'
}
});
}
Using this query, I'm getting an error that reads:
Uncaught (in promise): TypeError: null is not an object (evaluating 'WEBPACK_IMPORTED_MODULE_2_firebase_app"auth".currentUser.uid').
Using {console.log("blah", firebase.auth().currentUser.uid), which you can see above, correctly displays the UID in the log, confirming that the user is signed in.
I'm not sure if it's relevant but this query is being called by the page's main TS file with:
ngOnInit() {
this.moviesSvc.getLists()
.subscribe(lists => {console.log(lists); console.log("UID", firebase.auth().currentUser.uid); this.lists = lists})
}
What do I need to do to correctly pass the UID to the query?
onAuthStateChanged() is asynchronous, so when you have the user uid in its callback, then only perform your operation, like this:
getLists(): FirebaseListObservable<any> {
var self = this;
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var userID = user.uid;
return self.db.list('/foods', {
query: {
orderByChild: 'state/' + userID + '/state',
equalTo: 'listed'
}
});
}
});
}
Using the code snippet below I'm authenticating email, password. The customerlogin() method returns some JSON data which I want to show in the next page. In other words I want to pass the data returned from customerlogin() to then() and then pass it to /customerprofile
Please help
login(form) {
this.submitted = true;
if (form.$valid) {
this.Auth.customerlogin({
email: this.operator.email,
password: this.operator.password
})
.then(() => {
// Logged in, redirect to home
this.$location.path('/customerprofile');
})
.catch(err => {
this.errors.login = err.message;
});
}
}
//Other file Auth.js
customerlogin({
email,
password
}, callback) {
console.log('Customer Authentice Method');
return $http.post(properties.customer_login, {
email, password
})
.then(res => {
properties.GetId = res.data.id;
$cookies.put('token', res.data.token);
currentUser = User.get();
return currentUser.$promise;
})
.then(user => {
safeCb(callback)(null, user);
return user;
})
.catch(err => {
Auth.logout();
safeCb(callback)(err.data);
return $q.reject(err.data);
});
}
i want show data these textbox
enter image description here
Your login function should be calling a service method which makes the ajax call and stores the response as an object property on that service. The controller then has that on scope because you've injected the service. There's nothing to pass. It's already there and is watched automatically by Angular.
Something like this:
angular.someModule('someModule')
.service('someService', function($http) {
return {
loginCall: function(...) {
// do ajax call here
return loginStuff; // must be an object (or wrapped in one)
}
};
})
.controller('SomeController', ['someService', function(someService) {
var sc = this; // controllerAs syntax
sc.login = function(form) {
someService.customerlogin(...).then(...).catch(...);
// because someService has been injected, someService.loginCall is now
// available and being watched by the controller, on its scope...
// and can be used in your view like {{someService.loginCall.dataProperty}}
...
};
}]);
There are probably some missing pieces here (module injections), but this should get you started.
At first, try to use this construction for your .then:
.then(function (data) {
$log.debug(data); //to console log passed data via angular
});
I created logins and unique todo lists per user using Firebase and TodoMVC as proof of concept for another project. I'm using Firebase and Google to log users in and when things are working, they get a unique persistent todo list.
Everything works (I think) when the user is already logged into Google via their browser.
The problem happens when they aren't. Instead of their todo list, or a blank one under their user id, they see the todo list of an undefined user until they hit refresh, then things work again. The Firebase url doesn't see their uid until they hit refresh. If you're logged in to Google, you can replicate the error by opening an incognito window.
You can see the errors in my code at http://lacyjpr.github.io/todo-backbone, and my repo at https://github.com/lacyjpr/todo-backbone
This is my authentication code:
// Authenticate with Google
var ref = new Firebase(<firebase url>);
ref.onAuth(function(authData) {
if (authData) {
console.log("Authenticated successfully");
} else {
// Try to authenticate with Google via OAuth redirection
ref.authWithOAuthRedirect("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
}
});
}
})
// Create a callback which logs the current auth state
function authDataCallback(authData) {
if (authData) {
console.log("User " + authData.uid + " is logged in with " + authData.provider);
uid = authData.uid;
} else {
console.log("User is logged out");
}
}
This is the code that gets the UID to use as a firebase key:
// Get the uid of the user so we can save data on a per user basis
var ref = new Firebase(<firebase url>);
var authData = ref.getAuth();
if (authData) {
var uid = authData.uid;
console.log(uid);
}
// The collection of todos is backed by firebase instead of localstorage
var TodoList = Backbone.Firebase.Collection.extend({
// Reference to this collection's model.
model: app.Todo,
// Save all of the todos to firebase
url: <firebase url> + uid,
Thanks in advance for any advice you can offer!
You're calling .getAuth() before a user is authenticated.
Your app heavily relies on the uid to work properly. So in your case you would want to kick off the Backbone portion of the app once user has successfully authenticated.
You could modify your app.js to only kick off if the user is authenticated.
// js/app.js
var app = app || {};
var ENTER_KEY = 13;
$(function() {
var ref = new Firebase(<firebase url>);
var authData = ref.getAuth();
if (authData) {
ref.authWithOAuthRedirect("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
// kick off app
new app.AppView();
}
});
} else {
new app.AppView();
}
});
While this will work, it isn't the ideal solution. But there is no other option since you don't have a login screen.
Ideally, you'd want to provide the user a place to login, and then you'd have access the .getAuth() value.
Also, don't worry about storing the uid on the window. .getAuth() is the cached user, so there's no network call to get the data.