I have a function where i'm creating a user with Firebase and Vuex, and want to store some additional information about the user.
When I create a user, I run the code below, where payload contains email, password and name as an object.
This is in actions in vuex.
signUserUp ({ commit }, payload) {
firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then((u) => {
firebase.database().ref('users/' + u.user.uid).set({
name: payload.name,
email: payload.email,
registeredTools: []
})
// This commit('setUser')... sets the local data (local state).
commit('setUser', { id: u.user.uid, name: payload.name, email: payload.email, registeredTools: [] })
})
.catch(
error => {
console.log(error)
}
)
},
The user gets created, and my database store data like name and email, (I will soon be adding some new datafields to the user. )
This workes as expected. I have an autoSignIn-function which updates the local user when refreshing or closing/opening the browser. My problem occurs when i'm getting the userdata, and try to merge the object from the auth-function (only ID and email), with the the object from my database, which currently, only holds the name of the user.
-Here is my attempt:
autoSignIn ({ commit }, payload) {
const userDataFromAuth = { id: payload.uid, email: payload.email }
firebase.database().ref('users/' + payload.uid).once('value')
.then((data) => {
const userDataFromDatabase = []
const obj = data.val()
for (let key in obj) {
userDataFromDatabase.push({
name: obj[key].name
})
}
var userData = {...userDataFromAuth, ...userDataFromDatabase}
console.log(userData)
commit('setUser', userData)
})
},
Any ideas on how I might do this?
This is a pretty good solution to the problem:
autoSignIn ({ commit }, payload) {
const userDataFromAuth = { id: payload.uid, email: payload.email }
firebase.database().ref('users/' + payload.uid).once('value')
.then((data) => {
const userDataFromDatabase = { name: data.child('name').val() }
const userData = { ...userDataFromAuth, ...userDataFromDatabase }
commit('setUser', userData)
})
},
First, we get the data from the auth-function of firebase (which contains id and email). And then we get the data under 'users/' + payload.uid (id), and then we merge those two javascript objects, and store them in the store.users (vuex's local store of data), which we can use throughout our application. Please feel free to ask questions or suggestions for improvements.
Related
I have 2 collections in my firestore. One called owners and the second is unicorns.
An owner has only a name field and a unicorn has a name and a reference to the owner.
I want a query to return an array of objects that looks like this
unicorns = { id: 123,
name: Dreamy,
owner: { id: 1
name: John Cane
}
}
my query looks like this but there is something missing that I can't figure out
let unis = [];
db
.collection("unicorns")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
let uni = {
id: doc.id,
name: doc.data().name,
owner: doc.data().owner,
};
if (uni.owner) {
doc
.data()
.owner.get()
.then((res) => {
uni.owner = {
id: res.id,
name: res.data().name,
};
unis.push(uni);
})
.then(() => {
setUnicorns(unis);
})
.catch((err) => console.error(err));
} else {
unis.push(uni);
}
});
})
When I setUnicorns hook and I try to map the results I don't get all the data I need
You cannot run the .get() method on a string. You would have to run a separate request to firestore to get owner documents. I would recommend using a for-of inside a async function as shown below.
const db = admin.firestore()
//Declare the reference for collections before the loop starts
const ownersCollection = db.collection("owners")
const unicornsCollection = db.collection("unicorns")
const ownersData = {}
let unis = [];
unicornsCollection.get().then(async (querySnapshot) => {
for (const doc of querySnapshot) {
let uni = {
id: doc.id,
name: doc.data().name,
//Assuming the owner field is the UID of owner
owner: doc.data().owner,
};
if (uni.owner) {
//Check if owner's data is already fetched to save extra requests to Firestore
if (ownersData[uni.owner]) {
uni.owner = {
id: ownersData[uni.owner].id,
name: ownersData[uni.owner].name,
};
unis.push(uni);
} else {
//User the ownersCollection Reference to get owner information
ownersCollection.doc(uni.owner).get().then((ownerDoc) => {
uni.owner = {
id: ownerDoc.data().id,
name: ownerDoc.data().name,
};
ownersData[uni.owner] = {
id: ownerDoc.data().id,
name: ownerDoc.data().name,
}
unis.push(uni);
}).then(() => {
setUnicorns(unis);
}).catch((err) => console.error(err));
}
} else {
unis.push(uni);
}
}
})
How this works? It fetches all the unicorn documents and iterates over then to check if the document has owner's UID. If yes then runs another request to Firestore to get that unicorn's owner's document. Let me know if you have any questions.
Edit: In case a single owner has multiple unicorns then you surely don't want to fetch the data of same owner again and again. So add that in an object locally and check if the data of that owner is already fetched before making a request to Firestore. Code for the same updated above.
I am trying to block user on client-side from editing another user's profile. My URL structure is like so:
/users/edit/XpuBjKFoLSRHJAloNg38Amqn2jQ2
Thus, if user tries to acccess path of another user (ie, http://localhost:3000/users/edit/blahdasd) I need to redirect him to homepage.
I tried to set up an anonymous middle ware like so on my page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
return redirect('/')
}
},
But, I get page error of:
Cannot read property 'uid' of null
So, how do I correctly access the store here? I have no problem accessing uid from computed property on same page:
user() {
return this.$store.state.user.currentUser
},
Update (more information):
Here is my edit user profile page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
// return redirect('/')
console.log(store.state.user.currentUser.uid)
console.log(params.uid)
}
},
computed: {
user() {
return this.$store.state.user.currentUser
},
And here is my store/user.js file:
export const state = () => ({
currentUser: null,
})
export const mutations = {
SET_AUTH_USER(state, payload) {
state.currentUser = payload
}
}
export const actions = {
async onAuthStateChangedAction({ commit, dispatch }, { authUser }) {
console.log('auth state changed....')
try {
if (authUser && authUser.emailVerified) {
const {
uid,
email,
emailVerified,
displayName = '',
photoURL,
metadata,
providerData,
providerId,
tenantId
} = authUser
commit('SET_AUTH_USER', {
uid,
email,
emailVerified,
displayName,
photoURL,
metadata,
providerData,
providerId,
tenantId
})
console.log('fetching profile...')
await dispatch('getUserProfile', authUser)
} else {
console.log('User logged out or not verified')
return null
}
} catch (error) {
console.error('Error with Auth State observer: ', error)
}
},
I am using VUEX and Firebase to create a register form with three fields NAME, EMAIL, PASSWORD. First i am using createUserWithEmailAndPassword method to add the user but I also want to ad the name filed data to an Existing Blank collection, here I am using set method. But it is not adding the name field data in the collection.
methods: {
onSignUp() {
firebase
.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
console.log(user);
console.log(user.user.uid);
firebase.database.collection("profiles").doc(user.user.id).set({
name: this.name
})
.then(function() {
console.log("Document successfully written!");
})
.catch(function(error) {
console.error("Error writing document: ", error);
});
this.$store.dispatch('signUserUp', user);
})
.catch(error => {
this.$store.dispatch('signUserError', error)
});
}
}
data(){
return {
name: "",
email: "",
password: "",
}
}
After submitting the form it's adding a new user and I can also see the uid in the console but some how its not updating the name field in the database.
You should use firebase.firestore() and not firebase.database (See this doc) and therefore adapt your code as follows:
onSignUp() {
firebase
.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
console.log(user);
console.log(user.user.uid);
return firebase.firestore().collection("profiles").doc(user.user.id).set({
name: this.name
});
})
.then(() => {
console.log("Document successfully written!");
this.$store.dispatch('signUserUp', user);
})
.catch(error => {
this.$store.dispatch('signUserError', error)
});
}
}
You should also check that your security rules for the profiles collection are correctly set. Normally (authenticated) users should only be able to write a document with their own userid as document id.
I am trying have a user confirm their account using a verification code. I want to get the user document from the firestore db, check to ensure the authentication code matches the value provided, and then changed the hasVerfied field of the document to True.
This is for a mobile application (on device, not server-side) so I can not use firebase-admin... I have a screen appearing but once I fill out the authentication field click the button no action occurs, but I can confirm that the function is definitely being reached, just not executing the code within because of some error.
handleConfirmation = () => {
const auth_code = this.state.authCode;
let user = firebase.firestore().collection('users').where('uid', '==', firebase.auth().currentUser.uid);
// ^ I am not sure if this is correct... could be a source of wrongness.
if (user.exists === true) {
console.log(user.data());
let user_to_verify = user.data();
const has_verified = user_to_verify.hasVerified;
if (has_verified === false) {
const user_auth_code = user.authCode;
if (auth_code === user_auth_code) {
console.log("User verification code is correct");
this.setState({hasVerified: true});
this.updateUser();
// ^ this function should set the
// value of user.hasVerified to True, and
// save it in firestore (aka firebase firestore)
//
// Now the user can successfully login to app
}
}else{
// user doesnt exist... throw error and exit
}
on submission of form (onPress of button in app) handleConfirmation is executed and the auth_code is compared to user_auth_code (which is the value of the authCode field from the firebase firestore document), if these values match, the hasVerified field of user is changed to True and saved in firebase.
Please help! FYI this is my first ever post on StackOverFlow so let me know if I followed the proper guidelines.
//EDIT: showing how I initialize users upon creation.
constructor() {
super();
this.ref = firebase.firestore().collection('users');
this.state =
{
firstname: '<first name>',
lastname: '<last name>',
email: '<email>',
password: '<password>',
errorMessage: '<none unless error occurs>',
secureTextEntry: true,
confirmPassword: '<password>',
modalVisible: false,
imageURI: '<some url>',
authCode: '<authentication code>',
hasVerified: false,
};
this._keyboardDidHide = this._keyboardDidHide.bind(this);
this.setDate = this.setDate.bind(this);
}
.
. // SKIPPED SOME IN-BETWEEN LINES FOR BREVITY
.
updateUser() {
let user_data = {
uid: firebase.auth().currentUser.uid,
firstname: this.state.firstname,
lastname: this.state.lastname,
email: this.state.email,
imageURI: this.state.imageURI,
authCode: this.state.authCode,
hasVerified: this.state.hasVerified,
};
console.log(user_data);
this.ref.doc(firebase.auth().currentUser.uid).set(user_data);
this.props.navigation.navigate('homescreen');
}
Checkout the below code,
You have to store the doc-ID of the document inside the document to updateUser in later stages. I have given an example of how to do it as well in the last.
handleConfirmation = () => {
const auth_code = this.state.authCode;
var user = firebase
.firestore()
.collection("users")
.where("uid", "==", firebase.auth().currentUser.uid)
.get()
.then(querySnapshot => {
if (querySnapshot._docs.length > 0) { // User exists !!
console.log(querySnapshot._docs);
// You require the doc_Id of the document so that you can update it in the later stage.
const has_verified = querySnapshot._docs[0]._data.hasVerified; //_docs is a array, considering there is only 1 unique user
if (has_verified == false) {
const user_auth_code = querySnapshot._docs[0]._data.authCode; // or use firebase.auth().currentUser.uid instead.
if (auth_code === user_auth_code) {
console.log("User verification code is correct");
this.setState({ hasVerified: true });
this.updateUser(querySnapshot._docs[0]._data.doc_Id); // As told above doc_ID is required
}
}
}
});
};
updateUser = doc_id => {
var user = firebase
.firestore()
.collection("users")
.doc(doc_id)
.set({
hasVerified: true
});
};
//Example for adding doc_ID in document during document creation. Make sure you do this process during user creation.
//The below code is for your reference.
exampleDocCreate = () => {
var user = firebase
.firestore()
.collection("users")
.add({
userName: "React Native User"
})
.then(data => {
var user = firebase
.firestore()
.collection("users")
.doc(data.id)
.set({
doc_id: data.id
});
});
};
As per my understanding you are looking for a way to,
1) find a user who exists.
2) If exists grab their hasVerified and authCode information.
3) Compare and Update the their Document inside the Collection.
I hope I could help you
Would you happen to know how to set the user to the currentUser?
I am using vuefire and firebase.
The user.uid is returning undefined.
<td v-if="(building['key'] && building['key'].child('ownerId')) == user.uid">
<p >{{building['.key']}} + {{user.uid}}</p>
</td>
this is the rest of my code:
import firebase,{ db, usersRef, buildingsRef } from '../firebase-config';
import { store } from '../store/store';
export default {
firebase() {
// from my understanding this just gives me a quick access to the Refs and a short nomenclature
return {
users: usersRef,
buildings: buildingsRef,
}
},
name: 'buildings',
data () {
return {
user: "",
building: {
name: '',
address: '',
comments: '',
ownerId: '',
},
}
},
I am trying to do it through the store in the beforeCreate hook:
beforeCreate () {
this.$store.dispatch('setUser');
let user = this.$store.getters.getUser;
console.log(user); // this works!!!
this.$set(this.user, 'uid', user.uid )
}
},
If instead I set the user in create hook like this:
created () {
this.$store.dispatch('setUser');
let user = this.$store.getters.getUser;
console.log(user); // this works!!!
this.$set(this.user, 'uid', user.uid )
}
I get this error:
In Vue, the state (aka data properties) is initialized between the beforeCreate and created hooks.
So any change you do to data in beforeCreate is lost.
Change your hook to created
created() { // changed this line
this.$store.dispatch('setUser');
let user = this.$store.getters.getUser;
console.log(user); // this works!!!
this.$set(this.user, 'uid', user.uid)
}