Calling 'this' inside of firebase once function - javascript

So I am creating a Vuejs application and I am trying to fetch firebase data on component mount. That is working, but I try to write the value into a vuejs data property. For this I would only call this.property = snapshot.val(); inside the firebase function (see code below) but that is not working: this is undefined
So my code just is:
export default {
data() {
return {
imageList: "No images!"
}
},
mounted() {
if (!firebase.apps.length) {
// Initialize Firebase
var config = {
...
};
firebase.initializeApp(config);
var database = firebase.database();
var ref = database.ref("images").orderByChild("date");
firebase.database().ref("images/").once('value').then(function(snapshot) {
this.imageList= snapshot.val();
});
}
}
}
I tried calling this.imageList= snapshot.val(); outside of the function with a variable to store the value, and 'this' wasn't undefined anymore, but the data wasn't there because the request hasn't finished yet.
So basically I want to get the snapshot.val(); into this.imageList!
Thanks in advance.

You can use es6 arrow function syntax to solve the issue.
firebase.database().ref("images/").once('value').then((snapshot) => {
this.imageList= snapshot.val();
});
or bind this to the context of the function.
var that = this;

Use an arrow function, a closure, or bind.
firebase.database().ref("images/").once('value').then(snapshot => {
this.imageList= snapshot.val();
});
See How to access the correct this inside a callback.

You can use bind as well.
firebase.database().ref('images/').once('value').then(function(snapshot) {
this.imageList = snapshot.val()
}.bind(this))

Related

Vue.js - Data Property isn't accessible

I have a Vue.js 3 app. In this app, I'm trying to search through an array of object. I've created a fiddle here. In this fiddle, the code causing the issue looks like this:
async runSearch() {
let searchResults = this.data;
if (this.searchQuery) {
let info = JSON.stringify(searchIndex);
alert(info);
console.log(searchIndex);
searchResults = await courseIndex.search(courses);
}
this.results = searchResults;
}
For some reason, it's like searchIndex doesn't exist. However, I do have it in the model as shown here:
data() {
return {
searchIndex: null,
searchQuery: null,
data: data,
results: null,
}
}
What am I doing wrong? Why can't I execute a search?
use this.searchIndex to access reactive variables defined in data in Vue.

Wait until firebase has finished loading data (vue) / 'await' not working

In my vue app, I am fetching some data from firebase in the created-hook.
I want the code afterwards to be exectuted after the data was finished with loading. But I can not make it work.
Here is my code:
<template>
<div>
<h1>Test 2</h1>
</div>
</template>
<script>
import { institutesCollection } from '../firebase';
export default {
name: 'Test2',
data() {
return {
settings: null,
}
},
async created() {
await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(
await function(doc) {
this.settings = doc.data();
console.log(this.settings);
}
)
console.log('Done with fetching Data');
console.log(this.settings)
},
methods: {
}
}
</script>
But one the console, first 'Done with fetching Data' is displayed, then null (because this.settings is still null) and only after that the Objekt settings is printed.
But I need this.settings to be defined there (and not null anymore) to work with there.
What I tried so far:
Putting in awaits
Adding a loaded parameter
Adding the code afterwards in a then()
Nothing worked.
The onSnapshot() method is not an asynchronous method. As explained in the doc, it "attaches a listener for DocumentSnapshot events".
So, you should use it if you want to set a continuous listener to the Firestore document: if something changes in the document (e.g. a field gets a new value) the listener will be triggered. Note that "an initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document" (see the doc).
You get the Firestore document data only in the callback function that you pass to onSnapshot(), as follows:
created() {
institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(doc => {
this.settings = doc.data();
console.log(this.settings);
});
}
As Nilesh Patel mentioned, note that you need to use an arrow function in order to use this in this callback. Also note that the created function does NOT need to be async.
Finally, note that it is a good practice to call the unsubscribe function returned by onSnapshot() when you destroy the Vue.js component. Use the beforeDestroy hook as follows:
// ...
data() {
return {
settings: null,
firestoreListener: null
}
},
created() {
this.firestoreListener = institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(doc => {
this.settings = doc.data();
console.log(this.settings);
});
},
beforeDestroy() {
if (this.firestoreListener) {
this.firestoreListener();
}
},
On the other hand, if you just want to read the document data ONLY ONCE in the created Vue.js hook, you should use the get() method, as follows:
async created() {
const doc = await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').get();
if (doc.exists) {
this.settings = doc.data();
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}
Note that, in this case, the created function NEEDS to be async, since get() is asynchronous.
More details on the difference between get() and onSnapshot() in this SO answer.
try using arrow function
async created() {
await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(
doc => {
this.settings = doc.data();
console.log(this.settings);
}
)
console.log('Done with fetching Data');
console.log(this.settings)
},

Vue.js directive v-html not updating if the model is overwritten

By running the following code (a Vue.js component), I expect that, after the AJAX call returns, both the v-html directive and the console.log() display the same value.
On the contrary, v-html is stuck with "loading...(1)" even though obj.html has a different value, as console.log() confirms.
The behaviour is caused by getObject overwriting obj, and being afterwards obj.html undefined for a short time before getHTML returns (all this happens in function created).
Can please someone explain whether this is Vue's desired behavior (doc links are welcome), or whether should I submit a bug report, or finally whether I am simply structuring my code in a bad way?
Thanks in advance
<template>
<main v-html="obj.html || 'loading... (1)'">
</main>
</template>
<script>
export default {
name: 'Post',
data: function () {
return {
obj: {
html: 'loading... (2)'
}
}
},
created: async function () {
this.obj = await this.getObject()
this.obj.html = await this.getHtml()
console.log(this.obj.html)
},
methods: {
getObject: async function () {
const resp = await this.$http.get('https://jsonplaceholder.typicode.com/todos')
return resp.body[0]
},
getHtml: async function () {
const resp = await this.$http.get('https://jsonplaceholder.typicode.com/todos')
return resp.body[0].title
},
}
}
</script>
The function getObject returns a String so at the first line of created hook
this.obj = await this.getObject()
you change the reference of the obj and you make it pointing to a string and then you try to put a property on a string, which does not work ;)
it's like you would do
this.obj = 'test'
then console.log(this.obj);
// test
and then this.obj.abc = 'whatever'
console.log(this.obj.abc);
// undefined
You would need to parse the object before, see JSON.parse(string)
Update:
If this is not the case i.e you somehow have an object coming from that service.
Then the only problem I can think is that you lose the reference of your original obj and v-html is still pointing to the old one. In that case you have to avoid modification of the root obj or you can use the vue $set method: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
It seems vue data objects are not deeply reactive, which means that altering a property will not trigger change detection in the template.
Try rearranging the created hook to compose the full object before assigning it to the data property. That way when the template reacts it will see the html property of obj.
Ref CodeSandbox
created: async function () {
const fetchedObj = await this.getObject()
fetchedObj.html = await this.getHtml()
this.obj = fetchedObj;
console.log(this.obj.html)
},

socket.io and vue.js fetching data

I am trying to fetching the data of vue.js using this
fetchData: function () {
socket.emit('getAllSongs')
socket.on('allSongs',function(data) {
this.songs = data
});
}
FetchData is called when the vue app is created
created: function () {
this.fetchData();
}
But when I try to call the variable songs outside socket.on it stills empty.
What could I do?
Try changing you fetchData function to this:
fetchData: function () {
socket.emit('getAllSongs')
var that = this;
socket.on('allSongs',function(data) {
that.songs = data
});
}
The reason is the value this is different in the callback context and is not the component object which you expect it to be.
I think you need to bind this to the end of your function or to use ES6 arrow syntax =>
socket.on('allSongs',function(data) {
that.songs = data
}.bind(this));
socket.on('allSongs',data => {
that.songs = data
});

Angular2 subscribe understand arrow function

I try to understand arrow functions of typescript by the example of Angular 2 Observable subscribe method. Could somebody explain me:
I have this code which works:
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
but should it be the same if I use this? But this doesn't work.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}
);
Arrow function is anonymous and doesn't bind its own this. Hence, this is this of current context.
Normal function binds this to the caller if we don't bind it explicitly
Then
this.readdataservice.getPost().subscribe(
posts => { this.posts = posts; }
);
Can be
var self = this;
this.readdataservice.getPost().subscribe(
function(posts) { self.posts = posts; }
);
Or
this.readdataservice.getPost().subscribe(
function(posts) { this.posts = posts; }.bind(this)
);
JS by default executes functions in the scope of the caller. If you pass a function around to be called somewhere else, this points to the caller.
In your code you pass the function to the observable via the subscribe(...) method and then the function is called by the observable when an event is to be emitted.
If you use arrow function, then this keeps pointing to the class where it is defined.
An alternative is using .bind(this) to tell JS this should keep pointing at the current class instance.
this.readdataservice.getPost().subscribe(
function (posts) {
this.posts = posts;
}.bind(this)
);
See also https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Categories

Resources