Add Firebase image URL to my collection - firebase

I have the following method I'm accessing when my VueJS component is loading:
getServices () {
fb.servicesCollection.get().then(querySnapshot => {
this.serviceList = []
querySnapshot.forEach(doc => {
const { name, icon } = doc.data()
fb.storage.ref().child(icon).getDownloadURL().then(function (url) {
console.log(url)
})
this.serviceList.push({id: doc.id, name: name, icon: 'iconURL'})
})
this.isLoading = false
}).catch(error => {
console.log(error)
})
}
What I want to achieve is to get the url to replace the current 'iconURL' string. Didn't find any method to do that in the last couple of hours. Please help!

The following should do the trick. (However note that I could no test it, so it may need a bit of fine tuning... You can report how it works in the comments and we correct it if necessary)
Since you want to execute several getDownloadURL() asynchronous calls to Firebase Storage in parallel, you have to use Promise.all(), since getDownloadURL() returns a promise, see the doc.
getServices () {
let namesArray = []
let docIdArray = []
fb.servicesCollection.get().then(querySnapshot => {
this.serviceList = []
let promises = []
querySnapshot.forEach(doc => {
const icon = doc.data().icon;
promises.push(fb.storage.ref().child(icon).getDownloadURL())
namesArray.push(doc.data().name)
docIdArray.push(doc.id)
})
return Promise.all(promises)
})
.then(results => {
results.forEach((value, index) => {
this.serviceList.push({id: docIdArray[index], name: namesArray[index], icon: value})
})
})
}).catch(error => {
console.log(error)
})
}

This is how I got it in the end...
getServices () {
fb.servicesCollection.get().then(querySnapshot => {
this.serviceList = []
querySnapshot.forEach(doc => {
const { name, icon } = doc.data()
fb.storage.ref(icon).getDownloadURL().then(url => {
this.serviceList.push({id: doc.id, name: name, icon: url})
})
})
this.isLoading = false
}).catch(error => {
console.log(error)
})
}
Thank you for all your efforts to help me!!! Highly appreciate it!

Related

Getting parent and child document data from Firestore in a single Vue component

I have a parent (organisation) document in firestore and multiple child documents. I want load he data based on if the parent or child was clicked in the same component.
The below code works, the data is shown, but updates to the child organisations are not shown in real time (I have to reload to see it.). I'm guessing it is because I'm binding the array orgArray and not the object org that I actually use to display the data. Is there a way to just bind the object and not the whole array?
<template>
<div class="route-container">
<div class="item__container">
<FmisTitle/>
<Hero
:orgName="org.name"
:orgLogo="org.logo"
:orgState="org.state"
:orgNumber="org.number"
:orgType="org.type"
:orgDateStart="org.dateStart"
:orgDateEnd="org.dateEnd"
:orgDateStartF="org.dateStartFunctional"
:orgDateEndF="org.dateEndFunctional"
:orgCoverImage="org.coverImagex745"
:childRef="org.id"
:orgRef="orgRef"
/>
<Contact
:orgEmail="org.email"
:orgPhone="org.phoneNumber"
:orgAddress="org.address"
:orgWebsite="org.website"
:orgSocials="org.socials"
:childRef="org.id"
:orgRef="orgRef"
/>
<ButtonDuo/>
</div>
</div>
</template>
export default {
data() {
return {
org: {},
orgArray: [],
orgRef: '',
};
},
created() {
firebase.auth().onAuthStateChanged((user) => {
firestore.collectionGroup('people').where('userId', '==', user.uid).get().then((query) => {
query.forEach((userRef) => {
const orgRef = userRef.ref.parent.parent.id;
this.orgRef = orgRef;
if (!this.$route.params.parent) {
const organisation = firestore.collection('organisations').doc(orgRef).collection('childOrganisations').where('name', '==', this.$route.params.id);
this.$bind('orgArray', organisation).then((doc) => {
const org = doc[0];
this.org = org;
});
} else {
const organisation = firestore.collection('organisations').doc(orgRef);
this.$bind('org', organisation);
}
});
});
}, (error) => {
console.log(error);
});
},
}
I solved this by using the id from the childOrg and getting the data with that id, that way I could bind the data object directly.
firebase.auth().onAuthStateChanged((user) => {
firestore.collectionGroup('people').where('userId', '==', user.uid).get().then((query) => {
query.forEach((userRef) => {
const orgRef = userRef.ref.parent.parent.id;
this.orgRef = orgRef;
if (this.$route.query.parent !== 'true') {
firestore.collection('organisations').doc(orgRef).collection('childOrganisations').where('name', '==', this.$route.params.id)
.get()
.then((q) => {
q.forEach((ref) => {
const orgId = ref.id;
const organisation = firestore.collection('organisations').doc(orgRef).collection('childOrganisations').doc(orgId);
this.$bind('org', organisation);
});
});
} else {
const organisation = firestore.collection('organisations').doc(orgRef);
this.$bind('org', organisation);
}
});
});
}, (error) => {
console.log(error);
});

Firestore vuejs chained calls stop

I've been studying the tutorial on https://savvyapps.com/blog/definitive-guide-building-web-app-vuejs-firebase. The following piece of code was not properly working for me.
signup() {
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user)
// create user obj
fb.usersCollection.doc(user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile')
this.$router.push('/dashboard')
}).catch(err => {
console.log(err)
})
}).catch(err => {
console.log(err)
})
}
Could anyone help me?
P.S: After lots of research, I've stumbled upon that by this way I got the document created but the code inside then is not working yet. This change was based on Doug Stevenson's video on youtube about promises.
const promise = fb.usersCollection.doc(user.user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
})
return promise.then(() => {
console.log('set ok')
this.$store.dispatch('fetchUserProfile')
this.performingRequest = false
this.$router.push('/dashboard')
}).catch(err => {
this.performingRequest = false
console.log(err)
this.errorMsg = err.message
})
P.S 2: The line of code below helps to get the document created:
firebase.firestore().settings({ experimentalForceLongPolling: true })
Tylerhan has answered the question on github. Here is the solution:
async signup () {
this.performingRequest = true
await fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user.user)
})
const user = this.$store.getters.getCurrentUser
const userProfile = {
name: this.signupForm.name,
title: this.signupForm.title
}
fb.usersCollection.doc(user.uid).set(userProfile).then(() => {
this.$store.dispatch('fetchUserProfile')
this.performingRequest = false
this.$router.push('/dashboard')
}).catch(err => {
this.performingRequest = false
console.log(err)
this.errorMsg = err.message
})
}

VueJS Firebase Syntax error

I cannot find the syntax error in this signup method. It's been more than an hour now.
.then(() => {
db.collection("users").doc(this.slug).get().then((doc) => {
let data = doc.data()
db.collection("users").doc(cred.user.uid).set(data).then({
db.collection("users").doc(this.slug).delete()
})
})
})
This code above basically gets the newly created document, then puts the data into let data. After that, it creates a new document with the User UID as the name passes the data to it and then just deletes the old document. A syntax error lies in that code, but the indicator says, it is the dot between db and collection (db.collection)
Error report
methods: {
signup(){
console.log('signup ran')
if(this.heroName){
this.slug = slugify(this.heroName, {
replacement: '-',
remove: /[$*_+~.()'"!\-:#]/g,
lower: true
})
console.log(this.slug)
let ref = db.collection('users').doc(this.slug)
ref.get().then(doc => {
if(doc.exists){
this.feedback = 'This alias already exists'
} else {
// this alias does not yet exists in the db
this.feedback = 'This alias is free to use'
firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
.then(cred => {
ref.set({
alias: this.heroName,
user_id: cred.user.uid,
gemcount: 15
})
// FIXME: error below
.then(() => {
db.collection("users").doc(this.slug).get().then((doc) => {
let data = doc.data()
db.collection("users").doc(cred.user.uid).set(data).then({
db.collection("users").doc(this.slug).delete()
})
})
})
.then(() => {
this.$router.push({ name: 'Core' })
})
})
.catch(err => {
console.log(err)
this.feedback = err.message;
})
}
})
} else {
this.feedback = 'Please enter a heroName'
}
}
}
db.collection("users").doc(cred.user.uid).set(data).then({
db.collection("users").doc(this.slug).delete()
})
then expect its parameter to be a function, I reckon it where your error come from, the correct should be:
db.collection("users").doc(cred.user.uid).set(data).then(() => {
db.collection("users").doc(this.slug).delete()
})

TypeError: Cannot set property 'words' of undefined

I have this action in my vuex store:
loadExercises ({commit}) {
commit('setLoading', true)
const db = firebase.firestore()
db.collection('exercises').get()
.then(querySnapshot => {
const exercises = []
querySnapshot.forEach((doc) => {
exercises.push({
title: doc.data().title,
language: doc.data().language,
translated: doc.data().translated,
lastOpen: doc.data().lastOpen,
dueDate: doc.data().dueDate,
uid: doc.data().uid,
userId: doc.data().userId,
words: [{ word: '', uid: '', translation: '' }]
})
db.collection('exercises').doc(doc.data().uid).collection('words').get()
.then(words => {
const wordsArray = []
words.forEach(word => {
wordsArray.push(word.data())
})
let exercise = this.getters.loadedExercise(doc.data().uid)
exercise.words = wordsArray
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
})
commit('setLoading', false)
commit('setLoadedExercises', exercises)
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
}
It is supposed to fetch exercises from a firebase cloudstore db. It works on some routes but not all.
When using these two getters it works:
loadedExercises (state) {
return state.loadedExercises
},
loadedExercise (state) {
return (exerciseId) => {
return state.loadedExercises.find(exercise => {
return exercise.uid === exerciseId
})
}
}
But when I use these getters:
upcomingExercises (state, getters) {
return getters.loadedExercises.filter(exercise => {
return exercise.dueDate > 0
})
},
latestExercises (state, getters) {
return getters.loadedExercises.splice(0, 5)
},
it does not work I just get "TypeError: Cannot set property 'words' of undefined". What is it that I do wrong?
It looks to me like you aren't returning the values back to the function.
Try replacing
db.collection('exercises').get()
with
return db.collection('exercises').get()
and
db.collection('exercises').doc(doc.data().uid).collection('words').get()
with
return db.collection('exercises').doc(doc.data().uid).collection('words').get()

Redux Chain Multiple Ajax Promises in one Action

I have a Redux action which needs to make 2 subsequent ajax calls.
The first calls googlemaps api: https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}
The second calls a local service based on those results
/api/content/stores/byDistance/${lat},${lng}/sort
I'm using superagent to do the ajax calls. Clearly I'm experience difficulties keeping track of the promises, and including failures.
Am I mis-undestanding a core concept of Promises? Is there a simpler way to write the below?
export function loadBySearch(searchTerm) {
const geoSearchUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}`;
return {
types: [LOAD, LOAD_BY_LAT_LONG, LOAD_FAIL],
//Do I need to make this promise here?
promise: (client) => {
const promise = new Promise( (resolve, reject) => {
console.info('making google geocode request', geoSearchUrl);
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then( (successData1) =>{
const results = successData1.body.results;
if (!results.length) {
reject(`no results found for this search : ${searchTerm}`);
return;
}
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
client.get(path).then(
(successData2) => {
resolve( {
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
},
(errorData2) => {
reject( {
searchTerm: searchTerm,
result: errorData2
});
},
);
},
(errorData1) => {
reject({
searchTerm: searchTerm,
result: errorData1
});
}
);
});
return promise;
}
};
}
I'm not using superagent, but I'm guessing something like this might just work:
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then(successData1 => {
const results = successData1.body.results;
if (!results.length) {
throw(`no results found for this search : ${searchTerm}`);
}
return Promise.resolve(results);
})
.then(results => {
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
return client.get(path);
})
.then(successData2 => {
return Promise.resolve({
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
})
.catch(error => {
return Promise.reject({
searchTerm: searchTerm,
result: error
});
});
Haven't test it, but I hope at least it helps ;)

Resources