I'm trying to delete a field in a Firebase document from a React app using firebase.firestore.FieldValue.delete() but it isn't working. Reading and writing data to documents and collections is working fine but I am struggling to get FieldValue to work. I've set up the Firebase instance in src/firebase.js like this:
import app from 'firebase/app';
import 'firebase/firestore';
var config = {
******
};
class Firebase {
constructor() {
app.initializeApp(config);
this.fv = app.firestore.FieldValue;
this.db = app.firestore();
};
};
export default Firebase;
And then in the module that I am trying to use FieldValue in I am doing:
import Firebase from "./firebase.js";
class App extends Component {
constructor(props) {
super(props);
this.firebase = new Firebase();
this.db = this.firebase.db;
this.fv = this.firebase.fv;
};
deleteFunction(id) {
this.db.collection("collection_id").doc("doc_id").update({
field_id: this.fv.delete()
});
}
};
However no variation of imports or doc references as mentioned here have been successful for me. I don't receive any errors and I know that the firestore instance is working correctly because I am using it to set and read the same data. I have also verified that the field_id I am trying to delete is the correct id and that this.fv is FieldValue by console logging them both.
What am I missing? I feel like this should be super easy and straight forward and that I am overlooking something super obvious,
My guess is that you're trying to delete the field that is identified by field_id, so say:
// THIS WON'T WORK
let field_id = "fieldToDelete";
this.db.collection("collection_id").doc("doc_id").update({
field_id: this.fv.delete()
})
The above code is trying to delete the literal field_id field, which probably doesn't exist.
You'll need to use [] notation to build the object you pass to Firestore.
let field_id = "fieldToDelete";
let updates = {};
updates[field_id] = this.fv.delete()
this.db.collection("collection_id").doc("doc_id").update(updates)
Now you're using the value of field_id in the call to Firestore, and it will delete fieldToDelete.
Related
I'm trying to get a single record from Firestore to display in Vue.js. I'm using VueFire and the guide here.
<script setup>
import { initializeApp } from 'firebase/app'
import { getFirestore , doc } from "firebase/firestore";
import { useDocument } from 'vuefire'
const firebaseConfig = {...};
// Initialize Firebase
const firebaseApp = initializeApp(firebaseConfig);
const analytics = getAnalytics(firebaseApp);
const db = getFirestore(firebaseApp);
const place = useDocument(doc(db, "data", "key"));
console.log(place)
</script>
<template>
{{ place.title }}
</template>
The data logged is RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue : {title: 'I am a title', however when it gets to the template there is an error
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'title')
Maybe try this.
...
const { data: place, pending } = useDocument(doc(db, "data", "key"));
</script>
<template>
<h1>Place</h1>
<template v-if="!pending">{{ place.title }}</template>
</template>
I'm just following what they the author of VueFire has posted here: https://github.com/vuejs/vuefire/blob/df3c235f226d4e4c821391bcce74a1c3a6134406/packages/nuxt/playground/pages/firestore-useDocument.vue
You can use the pending property to show the place once it has loaded.
You can also read about Subscription State here where they talk about destructuring. https://v3.vuefire.vuejs.org/guide/realtime-data.html#subscription-state
If anyone finds this because they need the data from useDocument() asynchronously, you can do something similar, based on Kyle's suggestion above (the Github post by the vueFire author):
const { data: place, promise } = useDocument(doc(db, 'data', 'key'))
promise.value.then((place) => {
// do something with place like console.table(place)
})
Even if you don't want to listen for changes, you still have to use the "subscription" method to get the data asynchronously. async/await doesn't work. This needs to be a lot clearer in the documentation IMHO.
Hello I am new to react native and particullary firebase. I've been watching tutorial about using firebase with react native and i read the react native firebase documentation but i am stuck. I have data in my firestore data base, in my collection called user but I am not able to read, to get the data from it. Here is my firestore database :
and here is how I tried to get the data in my component :
const Profile = ({navigation, ...props}) => {
async function getUserData () {
const userCollection = await await firebase.firestore().collection('users').doc(firebase.auth().currentUser.uid).get()
return userCollection
}
console.log('🐲' + getUserData())
this return me that : [object Object]
I also tried this, (it was how it was done in the instagram clone tutorial of Adrian Twarog) :
const Profile = ({navigation, ...props}) => {
function getUserDataFirstTry() {
firebase.firestore()
.collection("users")
.doc(firebase.auth().currentUser.uid)
.get()
.then((snapchot) => {
if(snapchot.exists){
console.log('🦜' + snapchot.data())
} else {
console.log('merde ca marche pas')
}
})
}
console.log('🐲🐲' + getUserDataFirstTry())
But I get the same result in my console : [object Object]
I tried to put my function in a useEffect hook but this changes nothing.
Can you tell me what I am doing wrong ? (if possible in the second example because it's realtime changes). Thanks.
As Nicholas commented, since you're concatenating snapchot.data() to a string, you get the string representation of that object, which is pretty useless.
To log a specific field from the data:
console.log('🦜' + snapchot.data().userName)
To log all fields from the data in a more useful format:
console.log('🦜' + JSON.stringify(snapchot.data()))
Note: the proper spelling is snapshot (not snapchot), but I kept your original spelling here for ease of copy/paste.
I am using React.js to query a field in a document from Firebase. However, when I try to get the data, I keep getting an error. The code, the collection on Firestore, and and the error message are below. How do I properly query specific fields of data without running into this error?
import React from 'react';
import styled from 'styled-components';
import { firestore } from '../../firebase/firebase.utils';
class Economics extends React.Component {
constructor() {
super();
this.state = {
name: firestore.collection('blog-categories').doc('8uVaHd22tT5oXSzpOOuj').get('name')
}
}
render() {
return (
<div>
hi
</div>
)
}
}
export default Economics;
I think this is proper reference for the API you are using.
When you enter parameter to get method API is expecting options object, so when you enter String "name" there you will get this error massage.
The method is asynchronous so you have to take value form DocumentSnapshot (reference) object returned as Promise which result of the method.
I hope it will help!
UPDATE: As requested I'm adding example:
Firestore has collection collection1 with document doc1 that has field field1 with value 'value1':
var Firestore = require('#google-cloud/firestore');
var db = new Firestore();
var docRef = db.collection("collection1").doc("doc1");
docRef.get().then(docSnap => {
console.log(docSnap.data().field1);
console.log(docSnap.get("field1"));
});
If you run this in node result is:
value1
value1
I'm new to Vue and managed to make my first app with some glitches but I'm really enjoying it so far. I used a video tutorial which jump started with vue-cli project creation which as turns out is a litte different due to webpack.
I've created the project, the project does mostly what it should right now I'm trying to do some refactoring which includes DRYing out the code.
On each page I would like to access a variable stored in the cookie file I've done the saving and reading on the HomeComponent in the script section which works as promised.
<script>
import MenuComponent from '#/components/MenuComponent.vue'
import Typewriter from '#/components/vue-type-writer.vue'
export default {
name: 'HomeComponent',
components: {
MenuComponent,
Typewriter
},
prop:{
isPlaying: Boolean,
username: String,
currentSound: Object
},
data() {
return{
currentSound: null,
isPlaying: false,
username: ''
}
},
methods:{
clickButton() {
this.msg= 'test 2'
},
toggleSound(){
var a = this.currentSound;
if (a.paused) {
a.play();
this.isPlaying = true;
} else {
a.pause();
this.isPlaying = false;
}
},
getCookieInfo(){
var value = "; " + document.cookie;
var parts = value.split("; weegreename=");
if (parts.length == 2)
this.username = parts.pop().split(";").shift();
else this.username = '';
},
seveFormValues (submitEvent) {
this.username = submitEvent.target.elements.name.value;
this.$refs.audio1.pause();
this.$refs.audio2.play();
var expires = "";
var days = 31;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = "weegreename=" + (this.username || "") + expires + "; path=/";
}
},
mounted(){
this.isPlaying = true;
this.getCookieInfo();
if (this.username) this.currentSound = this.$refs.audio2;
else this.currentSound = this.$refs.audio1;
this.currentSound.play();
}
}
</script>
Now on every sub page I would like to access the getCookieInfo() method to check id the username is set.
I've tried to add this in the main App.vue script section, in the main.js
new Vue({
router,
render: h => h(App),
methods: {
//here the getCookieInfo code from above
}
}).$mount('#app')
created a new component whit the methods and then tried to access them in the main app via componentname.method as below.
import CookieComponent from '#/components/CookieComponent.vue'
export default {
// prop:{
// isToggled: Boolean
// },
components: {
MenuComponent,
CookieComponent
},
data() {
return{
isToggled: false
}
},
methods:{
clickToggle() {
this.isToggled = !this.isToggled;
},
},
mounted(){
CookieComponent.getCookieInfo();
}
}
I don't know right now the best approach and I will learn more in the future but this project is time sensitive - I decided to learn vue by making a simple site for my client :)
If you need it on every page it can be put into your App.vue. From there you have three options:
Pass the data as a prop to child components.
Create an event bus and emit the data to whichever component needs it.
Use Vuex to store the data and access it from your components.
If you really want to keep your cookie data inside the component you need to emit it up your component chain.
https://v2.vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event
Depending on how deep your chain goes and how many sibling components you have this can get really messy and in those cases Vuex or an event bus might be a better idea.
Do not try to do things like:
CookieComponent.getCookieInfo();
Please review the documentation to see good example on how to do component communication.
For that kind of stuff, the best practice is to use a state. It will save data of your application and will allow you to access them accross all components/pages.
You can see a simple state management in the Vue doc, or directly use VueX, the official state management library for Vue.
To sum up how it works (with VueX):
You create a cookieStore:
// Where data will be saved
const state = { cookie: {} }
// Getters allow you to access data
const getters = { cookie: state => state.cookie }
// Mutations allow you to modify the state
const mutations = {
// Set cookie data
saveCookie (state, cookieData) {
state.cookie = cookieData
}
}
In your HomeComponent, you will get the cookie info, and save it in
the store: this.$store.commit('saveCookie', cookieData)
In all other components, instead of getting the cookie info from the cookie, you can access the saved data from the store and do what you want with it: this.$store.getters.cookie
I'm stuck trying to figure out how to write a flux store and action that works in just fetching data from my express API using altjs
import $ from 'jquery';
const utils = {
myProfile: () => {
return $.ajax({
url: '/myProfile',
type: 'GET'
});
}
};
This is how I believe I should write my GET request for just grabbing a user's profile (which should return a json with user info).
then for my store :
import UserActions from 'actions/UserActions';
import alt from 'altInstance';
class UserStore {
constructor() {
this.userProfile = [];
this.on('init', this.bootstrap);
this.on('bootstrap', this.bootstrap);
this.bindListeners({
fetchUserProfile: UserActions.FETCHUSERPROFILE,
});
}
fetchUserProfile(profile) {
this.userProfile = profile;
}
}
export default alt.createStore(UserStore, 'UserStore');
However the action is where i'm the most clueless
import alt from 'altInstance';
import UserWebAPIUtils from 'utils/UserWebAPIUtils';
fetchProfile(){
this.dispatch();
UserWebAPIUtils.getProfile()
//what do we do with it to let our store know we have the data?
});
}
}
}
All im trying to do, is grab data from the server, tell my store we've recieved the data and fill the userprofile array with the data from our api, and the messenger for telling our store is through a dispatcher which belongs to 'actions' correct? I've looked at a lot of tutorials but I still dont feel very confident on how I am thinking about this. What if I wanted to update data through a POST request what would that be like?
Looking through altjs doc it seems like they recommend doing the async operations from actions. I prefer this approach as well because it keeps stores synchronous and easy to understand. Based on their example
LocationAction
LocationsFetcher.fetch()
.then((locations) => {
// we can access other actions within our action through `this.actions`
this.actions.updateLocations(locations);
})
.catch((errorMessage) => {
this.actions.locationsFailed(errorMessage);
});
Basically they are fetching the information and then triggering 2 actions depending on the result of the request which the store is listening on to.
LocationStore
this.bindListeners({
handleUpdateLocations: LocationActions.UPDATE_LOCATIONS,
handleFetchLocations: LocationActions.FETCH_LOCATIONS,
handleLocationsFailed: LocationActions.LOCATIONS_FAILED
});
When the store receives a handleUpdateLocations action which happens when the fetcher returns successfully. The store will update itself with new data and dispatch
handleUpdateLocations(locations) {
this.locations = locations;
this.errorMessage = null;
}
With your code you can do something similar. The fetch user profile will be triggered when data is originally requested. Here I am setting user profile to be [] which is your original init value but you can set it to anything to indicate data is being loaded. I then added 2 more methods, handleFetchUserProfileComplete and handleFetchUserProfileError which get called depending on if your fetch was successful or not. The code below is a rough idea of what you should have.
constructor() {
this.userProfile = [];
this.on('init', this.bootstrap);
this.on('bootstrap', this.bootstrap);
this.bindListeners({
handleFetchUserProfile: UserActions.FETCH_USER_PROFILE,
handleFetchUserProfileComplete: UserActions.FETCH_USER_PROFILE_COMPLETE,
handleFetchUserProfileError: UserActions.FETCH_USER_PROFILE_ERROR,
});
}
fetchUserProfile() {
this.userProfile = [];
}
handleFetchUserProfileComplete(profile) {
this.userProfile = profile;
}
handleFetchUserProfileError(error) {
this.error= error;
}
export default alt.createStore(UserStore, 'UserStore');
The only thing left is to trigger these 2 actions depending on the result of your fetch request in your action code
fetchUserProfile(){
this.dispatch();
UserWebAPIUtils.getProfile().then((data) => {
//what do we do with it to let our store know we have the data?
this.actions.fetchUserProfileComplete(data)
})
.catch((errorMessage) => {
this.actions.locationsFailed(errorMessage);
});
}
fetchUserProfileComplete(profile) {
this.dispatch(profile);
}
fetchUserProfileError(error) {
this.dispatch(error);
}