Firestore user_ref.update is not a function
I'm trying to update a document from a collection, with below lines of code
async function updateAbout(about_me){
try {
let user_ref = db.collection("users").where("uid", "==", $current_user.uid)
await user_ref.update({about_me})
} catch (error) { console.error("Can't update", error)
}
I'm getting the following error:
TypeError: user_ref.update is not a function
1 answer
-
answered 2020-11-25 07:24
Peter Haddad
where()
returns aQuery
, which does not have anupdate()
method. You need to do the following:let user_ref = await db.collection("users").where("uid", "==", $current_user.uid).get(); user_ref.forEach(doc => { const docRef = db.collection("users").doc(doc.id); docRef.update({about_me}); });
First get all the documents then iterate to get the document's id and update each document.
See also questions close to this topic
-
vue hot reload works but refresh page doesn't
I'm using vue2. I have a page listing members of my website. When I modify the code, the hot reloading works fine and my table displays fine. But when I refresh the page, the table is still fine (checked in VueJS devtools) but it doesn't display because the array I'm iterating becomes undefined. what should I do? weird bug isn't it? by the way I'm using props to pass the data to a 'listing' component.
props: ['table'], name: 'ListTable', components : { // UserCard }, data() { return { items: [], currentSort: 'id', currentSortDir: 'asc', pageSize: 10, currentPage: 1 } }, mounted() { this.items = this.$props.table.rows; // becomes undefined after page refresh, hot reloading works fine though },
-
How to prevent toggling class to go on top of page in vanilla JS?
I'm working on a website where users can click on a link in the header and it toggles a class to make an element appear. This is working, but every time I try it the page goes back to the top. How can I prevent this?
This is my code:
function openModal(e) { e.parentNode.classList.toggle("modal-open") }
header { position: fixed; top: 0; left: 0; width: 100%; } .modal { width: 100%; height: 100vh; background-color: red; position: fixed; top: 0; left: 100%; z-index: 999; } .modal-open .modal { left: 0; }
<header> <p class="open" onclick="openModal(this)">Open</p> <div class="modal" onclick="openModal(this)"> Content modal </div> </header>
Thanks for your answers!
-
Variable value not being saved in function
I want my filters variable to update, my guess is it's re-initializing as the set value every time the function is called, whenever i try to declare it outside of the function I get a lexical error, how can I make sure it keeps the value assigned to it after a button has clicked
export function categoryRender(){ let filter = 'RICK' console.log(filter) const all = document.getElementById('all'); all.onclick = function(){ filter = 'ALL' render(filter); } categories = categories.sort(); const filterContainer = document.getElementById("filter-container"); filterContainer.innerHTML = ""; const allFilterImg = document.getElementById('all-image'); if (filter === 'ALL'){ allFilterImg.setAttribute('src', './images/checked.jpeg') }else{ allFilterImg.setAttribute('src', './images/unchecked.png') console.log('unchecked all firing') } for (let i = 0; i < categories.length; i++){ const line = document.createElement("span"); const filterButton = document.createElement("img"); const filterLabel = document.createElement("h2"); filterContainer.appendChild(line); line.appendChild(filterButton); line.appendChild(filterLabel); line.setAttribute('id', categories[i]); line.classList.add('filter-line'); filterLabel.innerHTML = categories[i]; if (filter === categories[i]){ filterButton.setAttribute('src', './images/checked.jpeg') }else{ filterButton.setAttribute('src', './images/unchecked.png') } line.onclick = function(){ filter = categories[i]; render(filter) } } }
-
show event parameter after click event name firebase event
I want to make event in firebase analytics like
page_view
event. So after we click eventpage_view
in events menuin the next page, I want to show card / menu like image below
I want to count user interaction on specific page. In this case, I want to count how many times user go to page
dashboard
. I have tried using js code like below, but after I click event name, in exampleuser
, it doesn't show card in image I want above.Here's the code
<script> // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics().setUserProperties({name: 'dashboard_admin'}); firebase.analytics().logEvent('user',{content_id: 'dashboard_admin'); </script>
Any clue would be very helpful
-
How to prevent images from firebase storage to be downloaded every time on filter
I'm using firebase storage to load images into my web page. I have a page where I show different kinds of projects. I also provided a filter option. That is where I have my problem now. Every time users use a filter, the images from the firebase storage are downloaded again.
This is the code where I map over all my projects to show the project component.
{newProjects.map((project) => ( <Project project={project} key={project.id}></Project> ))}
In my project component, I use the project data to download the right image.
const imgRef = useRef(null); const storageRef = firebase.storage().ref(); storageRef .child('images/' + project.image) .getDownloadURL() .then(function (url) { imgRef.current.src = url; } }); return ( <img className={style.card_image} ref={imgRef} alt={project.title} </img> )
The projects are loaded in with the right images, great! The problem now is that if a user uses a filter, the images from the projects are downloaded from firebase again. The newProjects are a state and whenever I use the filter I will set the state again.
const [newProjects, setNewProjects] = useState(projects); const filter = () => { // filter stuff... setNewProjects(filteredProjects); }
Is there a way that the images won't download every time the users use a filter.
-
Firebase Vuexfire infinite loading. How to achieve showing the next results using vuexfire?
I am building the shop search project. I have two shop types in Product collection. The types are Supermarket and Restaurant.
In my menu.js file, I am setting the type in actions section, then I am showing the results in the Result.vue file.
When Result.vue is loaded, setMenuRef in menu.js is dispatched and the information is pushed into the array named getMenuItems.
If I press the supermarket button in Buttons.vue component, the current type is set to be Supermarket and then, I can see the results of shops which are categorized as Supermarket.
And if I press the restaurant button, I can see the results of shops which are categorized as Restaurant. The both methods are same.
What I want to achieve is, to add the loading functions for showing the next 8 results in the each result page.
I am searching the multiple websites to add the loading functions in vuexfire code. But I could not see what an approach is the best.
In Result.vue, I used the method for loading the new results. The method name is nextPage.
The logic in menu.js is bit complicated. Please feel free to ask me about the further information.
menu.js
import { firestoreAction } from 'vuexfire' import fireApp from '@/plugins/firebase' const firebase = require("firebase"); require("firebase/firestore"); const db = firebase.firestore(); const dbMenuRef = db.collection('Product').where("saleStatus", "==", 1).limit(8) const state = { currentType: 'menu', menuItems:[], store:[], supermarketItems:[], restaurantItems: [] } const getters = { currentType: state => state.currentType, getMenuItems: state => state.menuItems, supermarketItems: state => state.supermarketItems, restaurantItems: state => state.restaurantItems } const mutations = { setCurrentType(state, type) { state.currentType = type }, showSupermarketResult(state, supermarket) { state.supermarketItems.push(supermarket); }, showRestaurantResult(state, restaurant) { state.restaurantItems.push(restaurant); }, } const actions = { setMenuRef: firestoreAction(context => { return context.bindFirestoreRef('menuItems', dbMenuRef) }), setCollectionType: ({ commit, state }, type) => { commit('setCurrentType', type) const mutations = { Supermarket: 'showSupermarketResult', Restaurant: 'showRestaurantResult' } const states = { Supermarket: 'supermarketItems', Restaurant: 'restaurantItems' } if (state[states[type]].length) return const collectionRef = db.collection('Product').where("saleStatus", "==", 1).where('type', '==', type).limit(8) collectionRef.get() .then(snapshot => { if (snapshot.empty) { console.log('No matching documents.'); return; } snapshot.forEach(doc => { const data = doc.data() commit(mutations[type], data) }); }) .catch(err => { console.log('Error getting documents', err); }); }, } export default { state, mutations, getters, actions }
Result.vue
<template> <div class="result-body"> <Navbar class="Navbar"/> <NavbarMobile class="NavbarMobile" /> <Map /> <Buttons /> <b-container v-if="currentType === 'menu'" class="result-container"> <b-row class="result-row "> <b-col md="6" v-for="(item, index) in getMenuItems" :key="index" class="container-result "> <div class="check d-flex flex-wrap flex-row " v-if="item.remainingQuantity >= 0"> <b-col cols="3" class="data"> <b-list-group class="list"> <div href="#" class="flex-column align-items-start"> <div class="d-flex justify-content-between"> <div class="item sale"> <div class="line"> <span class="initial">{{ item.initial }}</span> <br> </div> <span class="price">{{ item.sale }}</span> </div> </div> </div> <div href="#" disabled class="flex-column align-items-start limit-area"> <div class="d-flex justify-content-between"> <div class="item limit"> <div class="position"> Until <br>{{ item.limitObject }} </div> </div> </div> </div> </b-list-group> </b-col> <b-col cols="9"> <div v-if="item.remainingQuantity == 0"> <b-overlay :show="show" class="overlay" variant="dark" :opacity="opacity" :blur="blur" spinnerType="false"> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </b-overlay> </div> <div v-else> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </div> </b-col> </div> </b-col> </b-row> </b-container> <b-container v-if="currentType === 'Restaurant'" class="result-container"> <b-row class="result-row-restaurant "> <b-col md="6" v-for="(item, index) in restaurantItems" :key="index" class="container-result "> <div class="check d-flex flex-wrap flex-row " v-if="item.remainingQuantity >= 0"> <b-col cols="3" class="data"> <b-list-group class="list"> <div href="#" class="flex-column align-items-start"> <div class="d-flex justify-content-between"> <div class="item sale"> <div class="line"> <span class="initial">{{ item.initial }}</span> <br> </div> <span class="price">{{ item.sale }}</span> </div> </div> </div> <div href="#" disabled class="flex-column align-items-start limit-area"> <div class="d-flex justify-content-between"> <div class="item limit"> <div class="position"> Until <br>{{ item.limitObject }} </div> </div> </div> </div> </b-list-group> </b-col> <b-col cols="9"> <div v-if="item.remainingQuantity == 0"> <b-overlay :show="show" class="overlay" variant="dark" :opacity="opacity" :blur="blur" spinnerType="false"> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </b-overlay> </div> <div v-else> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </div> </b-col> </div> </b-col> </b-row> </b-container> <b-container v-if="currentType === 'Supermarket'" class="result-container" > <b-row class="result-row "> <b-col md="6" v-for="(item, index) in supermarketItems" :key="index" class="container-result "> <div class="check d-flex flex-wrap flex-row " v-if="item.remainingQuantity >= 0"> <b-col cols="3" class="data"> <b-list-group class="list"> <div href="#" class="flex-column align-items-start"> <div class="d-flex justify-content-between"> <div class="item sale"> <div class="line"> <span class="initial">{{ item.initial }}</span> <br> </div> <span class="price">{{ item.sale }}</span> </div> </div> </div> <div href="#" disabled class="flex-column align-items-start limit-area"> <div class="d-flex justify-content-between"> <div class="item limit"> <div class="position"> Until <br>{{ item.limitObject }} </div> </div> </div> </div> </b-list-group> </b-col> <b-col cols="9"> <div v-if="item.remainingQuantity == 0"> <b-overlay :show="show" class="overlay" variant="dark" :opacity="opacity" :blur="blur" spinnerType="false"> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </b-overlay> </div> <div v-else> <div class="image-card"> <router-link :to="{name:'Product',params:{id:item.id}}"> <div class="img" v-for="(sample, index) in item.sample" :key="index"> <b-img class="storefront" :src="sample" alt="Image 3"></b-img> </div> </router-link> <div class="card__content"> <div class="card__info"> <span class="business float-left">{{ item.business }}</span> <span class="quantity float-right">{{ item.remainingQuantity }} left</span> </div> </div> </div> </div> </b-col> </div> </b-col> </b-row> </b-container> <b-button @click.prevent="nextPage" class="next">next</b-button> <Footer class="Footer" /> <FooterMobile class="FooterMobile"/> </div> </template> <script> import axios from 'axios'; import Navbar from "@/components/Navbar.vue"; import Buttons from "@/components/Buttons.vue"; import Map from "@/components/Map.vue"; import Footer from "@/sections/Footer.vue"; import NavbarMobile from "@/components/NavbarMobile.vue"; import FooterMobile from "@/sections/FooterMobile.vue"; import fireApp from '@/plugins/firebase' const firebase = require("firebase"); require("firebase/firestore"); const db = firebase.firestore(); import { mapGetters } from 'vuex' export default { name: "Result", data() { return { show: true, opacity: 0.8, blur: '0px', status: "", address: "", error: "", spinner: false, fav: false, type: "menu", userId: "", productId: "", lastVisible: "", getMenuItems: [] } }, components: { Navbar, Map, Buttons, Footer, NavbarMobile, FooterMobile, }, computed: { ...mapGetters([ 'currentType', 'getMenuItems', 'restaurantItems', 'supermarketItems' ]) }, created() { const dbMenuRef = db.collection('Product').where("saleStatus", "==", 1).limit(8) dbMenuRef.onSnapshot((snapshot) => { snapshot.forEach((doc) => { const quantity = doc.data().remainingQuantity console.log(quantity) //this.show = false this.productId = doc.data().id this.favoriteProduct = doc.data() this.$store.dispatch('setMenuRef', dbMenuRef) }) }) }, methods: { nextPage() { fireApp.auth().onAuthStateChanged((user) => { if (user) { const userId = fireApp.auth().currentUser.uid; const next = db.collection('Product').where("saleStatus", "==", 1).startAfter(this.lastVisible).limit(8) return next.onSnapshot((nextQuery) => { nextQuery.forEach((doc) => { const nextData = doc.data() console.log(nextData) this.getMenuItems.push(nextData) }) this.lastVisible = nextQuery.docs[nextQuery.docs.length-1]; }) }; }) } }, mounted() { this.$store.commit('setCurrentType', this.type) }, watch: { getMenuItems: { handler(getMenuItems) { //console.log('getMenuItems: ', getMenuItems); //Debug this.resultData = getMenuItems }, deep: true }, show: { handler(show) { this.show = show }, immediate: true } } } </script>
Buttons.vue
<template> <div> <section class="button-section"> <div class="d-flex buttons"> <b-button class="supermarket" @click.prevent="showCollection('Supermarket')">Supermarket</b-button> <b-button class="restaurant" @click.prevent="showCollection('Restaurant')">Restaurant</b-button> </div> </section> </div> </template> <script> import fireApp from '@/plugins/firebase' const firebase = require("firebase"); require("firebase/firestore"); const db = firebase.firestore(); import { mapGetters } from 'vuex' import store from '../store' export default { name: 'Buttons', data() { return { supermarket: "", restaurant: "" } }, methods: { showCollection(type) { this.$store.dispatch('setCollectionType', type) } } } </script>
-
Serialize value-types without creating additional fields for 'value' in the database
With firestore it's possible to use
(data) class(es)
data class Address(var person: Person = Person("", "")) data class Person(var firstName: String = "", var lastName: String = "")
that will be serialized into their properties like that
document: person: firstName: "John" lastName: "Doe"
However, I'd like to have a value-type for
firstName
like this one:data class FirstName(var value: String = "") { init { // check value etc. } }
but when I use it for the
firstName
in its default form, it'll serialize intodocument: person: firstName: value: "John" lastName: "Doe"
In order to de/serialize it, I use the standard mechanism like:
// Serialize/write firestore .collection("path") .document() .set(address) // Deserialize/read e.g. firestore .collection("path") .whereEqualTo("person.firstName", "John") .get() .await() .toObjects(Address::class.java)
How can I make it serialize into and deserialize from a string so that the
value
field is not created? -
Firebase Emulator not taking the latest data
I'm creating a cloud function to update data on around 2000 documents. I'm using batch to do it inside my cloud function. When I first run the function, it works just fine but then when I run it a second time to make sure everything was correctly updated, I can see that some my function is getting old data, the one that was there before I run my first query.
I've tried to wait a couple of minutes, delete all fields in the documents to see if I can see any changes but the function keep taking the old data.
Just as a reference, this is the cloud function I'm using. Any idea what I'm doing wrong?
Edit: It's actually really weird. If I inspect the array I receive, I have duplicates with the same ID but one is the old data and the other one is the updated data.
.runWith({ timeoutSeconds: 540, memory: '2GB', }) .https.onRequest(async (req, res) => { const batch = admin.firestore().batch(); const request = await admin.firestore().collection('products').get(); const products = request.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); try { products.forEach((product) => { const productRef = admin .firestore() .collection('products') .doc(product.id); const ga = product.genderAges; if (product.genderAges) { if ( ga.includes( 'female0To3Months', 'female3To6Months', 'female6MonthsTo1year' ) ) { batch.update(productRef, { filterRankUp: admin.firestore.FieldValue.arrayUnion( 'genderAge-femaleBaby' ), }); batch.update(productRef, { genderAges: admin.firestore.FieldValue.arrayRemove( 'female0To3Months', 'female3To6Months', 'female6MonthsTo1year' ), }); } if ( ga.includes( 'male0To3Months', 'male3To6Months', 'male6MonthsTo1year' ) ) { console.log(product); batch.update(productRef, { filterRankUp: admin.firestore.FieldValue.arrayUnion( 'genderAge-maleBaby' ), }); batch.update(productRef, { genderAges: admin.firestore.FieldValue.arrayRemove( 'male0To3Months', 'male3To6Months', 'male6MonthsTo1year' ), }); } } }); console.log(batch._ops); await batch.commit(); res.status(200).send('Yooohhoooo'); } catch (error) { console.log(error); res.status(500).send(error); } });```