I'm not very experienced, and I was wondering how I can extract data from firebase, updating the status element of vue.js. The problem is that I can not extract the fusion data dedicated to firebase "snapshot".
I leave you all the code and all the errors below.
Vue.js code:
<template>
<div id="app">
<div>
<h1>ON/OFF led</h1>
<h2>Status: {{ device.status() }}</h2>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import { deviceStatusRef } from './firebase';
var pinReport;
export default {
data () {
return {
name: '',
device: {
nome: 'led',
status: function(){
deviceStatusRef.on("value", function(snapshot) {
pinReport = snapshot.val();
console.log("[1] - pinReport value --> " + pinReport);
});
console.log("[2] - pinReport VALUE --> " + pinReport);
return pinReport;
}
}
}
},
}
</script>
<style>
</style>
Errors on Chrome:
That's the expected behavior. Data is loaded from Firebase asynchronously. By the time your [2] log statement runs, the data hasn't been loaded yet and pinReport is undefined.
This means that all code that needs the data must be inside the callback, like your [1] log statement. It also means that you can't return the value from the database from a function, since the return runs before the data is loaded.
Related
I'm new in VueJs, actually coming from react, the problem I'm having is that when i fetch the data with an axios.get the console.log shows a succesfull response. But when I to iterate through the Array with v-for it renders nothing.
It's quite important so it be super appreciated if you can tell me what im doing wrong or you have a suggestion.
heres the code:
<SignUpForm />
<h1>Countries List</h1 >
<div v-for="country in countries" :key="country.name" >
<p>{{country.name}}</p>
</div>
</template>
<script>
import SignUpForm from "#/components/SignUpForm.vue";
import axios from 'axios'
export default{
name: 'SignUpView',
components:{
SignUpForm
},
data(){
return{
countries: []
}
},
async mounted() {
const {data} = await axios.get('https://gist.githubusercontent.com/keeguon/2310008/raw/bdc2ce1c1e3f28f9cab5b4393c7549f38361be4e/countries.json')
console.info(data)
this.countries = data
}
}
</script>
You can see that what I'm actuallt doing is the axios.get in the mounted(){} f(x), later I = the data to my empty Array declared in data(){}. => So I can iterate trough the json response with a v-for in a div.
Heres the picture so you can see:
Observations :
countries list is a string and v-for is work if the input data is an array.
No. of objects in an array is 243 but this 9251 is a count of characters in a string.
Also, if we will try to convert that into a JSON object via JSON.parse() getting SyntaxError: Unexpected token n in JSON at position 6.
new Vue({
el: "#app",
data: {
countries: []
},
methods: {
getData() {
var vm = this;
axios.get("https://gist.githubusercontent.com/keeguon/2310008/raw/bdc2ce1c1e3f28f9cab5b4393c7549f38361be4e/countries.json")
.then(function(response) {
console.log(typeof response.data); <---- string
console.log(response.data.length); <---- actual objects in an array is 243 but this 9251 is a count of characters in a string.
console.log(JSON.parse(response.data)); <---- SyntaxError: Unexpected token n in JSON at position 6
})
.catch(function(error) {
alert(error);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<button #click="getData">Click to check Api response</button>
</div>
I am trying to make a GET request to an API, assign the response to a variable and console.log it. Currently I get null in the console and I don't know why. when I just console.log the res.data I get the data, but for some reason the value does not get assigned to the variable allBeers, how can I save the response to that variable and use it?
Here is my code:
<script>
import axios from "axios";
export default {
name: "BeerList",
data() {
return {
allBeers: null
};
},
created() {
axios.get("https://api.punkapi.com/v2/beers").then(res => {
this.allBeers = res.data;
});
console.log(this.allBeers);
}
};
</script>
Try this solution. axios.get is a promise, and console.log will happen before axios.get is completed. That's why you get null.
<script>
import axios from "axios";
export default {
name: "BeerList",
data() {
return {
allBeers: null
};
},
created() {
axios.get("https://api.punkapi.com/v2/beers").then(res => {
this.allBeers = res.data;
console.log(this.allBeers);
});
}
};
</script>
You are saving the response to 'allBeers' variable correctly. The "how to use it" part depends on what you want to do with the data. Vue's 'data' is reactive, so if you want to print it to console when it changes you should use Vue's watcher in your component:
watch: {
allBeers: function(value) {
console.log("my new beers:");
console.log(value);
}
}
As Vue is UI framework you might want to update your html with the new data, in which case you need to add html template to your component - it will be automatically re-rendered when 'allBeers' change:
template: `<span> {{ allBeers }} </span>`
Problem
Im trying to load data from my database using vuejs + axios in a Laravel project. Locally I can make the request using axios and it returns the data, assigns it to variables and then outputs the data to the DOM.
When I push the same code to production the axios request takes a little longer to execute but it seems to be long enough that vuejs is ready before all the data is returned, leaving me with an annoying 'undefined property' error when the data attempts to be displayed. Sometimes this error persist's through 20+ refreshes before the data loads.
What I think the issue is
So my guess from reading up is that axios isn't getting the data as quickly as it should do so vuejs is ready to start serving the data but axios hasn't had a chance to collect it yet, resulting in the undefined error
What i've read and tried
I've read that using v-if statements on the objects that depend on the data should fix this issue. But it doesn't, all it does it hide the object from view if the data is not present. For Example...
HTML
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
#{{ user.name }}
</div>
JAVASCRIPT
<script>
new Vue({
el: '#container',
data: {
user: {}
},
mounted() {
this.getUserData();
},
methods: {
getUserData(){
axios.post('/user/get').then((response) => {
// Check the response was a success
if(response.data.status === 'success')
{
this.user = response.data.user;
}
});
},
}
})
</script>
This doesn't work, it just doesn't display anything when the data hasn't been loaded yet.
Question
How do I ensure that my loaded data is displayed and not return an undefined error? The only way I can think is by allowing the user to 'click to retrieve data' on a failed attempt.
Further Information
Im not using vue templates, the child/parent structure or any vue libraries. I'm importing vue.js via CDN and using it on the page in a script as seen above. Im not sure if using it like this will cause any such limitations, i've only learnt vue.js on a basic and this works for the company and the boss...
You could add a 'loaded' boolean attribute to resolve this. For example:
<script>
new Vue({
el: '#container',
data: {
isLoaded: false,
user: {}
},
mounted() {
this.getUserData();
},
methods: {
getUserData(){
axios.post('/user/get').then((response) => {
this.isLoaded = true;
// Check the response was a success
if(response.data.status === 'success')
{
this.user = response.data.user;
}
});
},
}
})
</script>
Then wrap your html to check if the data has been loaded:
<div v-if="isLoaded">
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
#{{ user.name }}
</div>
</div>
You could then also display a loader:
<div v-if="isLoaded">
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
#{{ user.name }}
</div>
</div>
<div v-else>
Loading...
</div>
Edited answer, to explain better what I mean.
Your code should work, no matter if it takes 1 sec or 10 sec for axios to fetch the data. You shouldn't have to check with v-if on user.name. VueJS has reactivity and upates the view if the data property changes.
See this code example.
https://codepen.io/bergur/pen/VOXrzR
Here I wait 10 seconds to populate the user object
{{ user.name }} shows nothing and then Bergur
{{ computedName }} shows undefined Hallgrímsson because user.name is undefined for 10 seconds.
{{ user.address.country }} will throw an error (Cannot read property 'country' of undefined) because user.address isn't available in the initial data object.
So one solution is to define address initially in the data object
user: {
address: {}
}
My point is that your initial code should work (when rendering). The undefined errors come when
a) You're using that name property in your vue app, e.g user.name when there is no name property.
or
b) When you're rendering 2nd level property, e.g user.address.country because address hasn't been initially defined.
With the help of lodash's debounce you can delay the time between when a method function or computed property is called and excecuted.
Also you can differentiate the delay time for the same method creating different functions eg. one being triggered by mounted and the other one by a watcher. In my case the function being called on mounted, needs to wait before the axios call returns a response and sets store.state.TableDataInit. In the watcher case it can run immediately because the store.state variable already has been set, there is no need to wait for an axios response anymore. For example:
created: function() {
this.setInitRange = _.debounce(this.setRange, 600);
this.setInitBoundaries = _.debounce(this.setBoundaries, 600);
this.setWatchRange = this.setRange;
this.setWatchBoundaries = this.setBoundaries;
},
mounted() {
this.setInitRange(this.selected);
this.setInitBoundaries();
},
watch: {
selected() {
store.commit("storedMetric", this.selected);
this.setWatchRange(this.selected);
this.setWatchBoundaries();
}
},
methods: {
setRange(value) {
var metric = value;
var dataArray = store.state.TableDataInit;
const maxMetric = dataArray.reduce(function(prev, current) {
return _.get(prev, metric) > _.get(current, metric) ? prev : current;
})[metric];
const minMetric = dataArray.reduce(function(prev, current) {
return _.get(prev, metric) < _.get(current, metric) ? prev : current;
})[metric];
this.range = [minMetric, maxMetric];
store.commit("storedOutBoundRange", this.range);
},
setBoundaries() {
this.min = store.state.OutBoundRange[0];
this.max = store.state.OutBoundRange[1];
},
<div v-if="user">
<div>
#{{ user.name }}
</div>
<div>
#{{ user.second_name }}
</div>
<div>
#{{ user.phone }}
</div>
<div>
#{{ user.any }}
</div>
</div>
I'm just wondering how to create a global function wherein it checks whether the localStorage is not empty specifically if there's a token inside
What I've tried is creating a global variable in my main.js
Vue.$checkIfTokenIsNotEmpty = !!localStorage.getItem('token');
// this returns true or false
In my component,
<template>
Is token Empty? {{ isTokenIsEmpty }} // I'm able to get true or false here
</template>
<script>
export default {
data(){
return {
isTokenIsEmpty: this.$checkIfTokenIsNotEmpty
}
}
}
</script>
This works properly when I reload the page. The problem is, it's not reactive or real time. If I clear the localStorage, the value of my this.$checkIfTokenIsNotEmpty doesn't change. It only changes when I reload the page which is bad in my spa vue project.
You can acces token like here: https://jsfiddle.net/djsj8dku/1/
data: function() {
return {
world: 'world',
get token() {
return localStorage.getItem('token') || 0;
},
set token(value) {
localStorage.setItem('token', value);
}
};
}
Or you can use one of this packages: vue-reactive-storage, vue-local-storage
You cannot detect when localStorage is wiped out manually but you can watch when localStorage is updated. So watcher is what you need. Link
Regarding global function you can set a method & variable inside root component.
new Vue({
el:"#app",
data:{
isTokenIsEmpty:null
},
methods: {
checkIfTokenIsNotEmpty() {
this.isTokenIsEmpty= !!localStorage.getItem('token');
}
}
})
Inside component,
mounted(){
this.$root.checkIfTokenIsNotEmpty() //can be added anywhere when needed to check localStorage's Availablity
}
Html
<template> Is token Empty? {{ $root.isTokenIsEmpty }} // I'm able to get true or false here </template>
Given an express route,
page.js
router.post('/results', (req, res, next) => {
output.terms = "hello"
output.results = [{"text": "hello world"}, {"text": "hello sunshine"}]
res.render("/myview",output)
})
The following works and shows the text.
myview.pug
extends ../layout.pug
block content
each result in results
span=result.text
But the following vue component doesn't. I receive TypeError: results is undefined
results-view.vue
<template lang="pug">
li(v-for='result in results')
span {{ result.text }}
</template>
<script>
export default {
...
}
</script>
myview.pug
extends ../layout.pug
block content
results-view
How do I pass the data from router to vue component?
You should probably think of some other solution. It does not make sense to pass data from route to component.
You can create an API endpoint that returns data and in Vue you can access it via request.
In the end, this worked. Might not be the best practice (I'm not sure, really) as warned by commenter serkan
results-view.vue
<template lang="pug">
li(v-for='result in results')
span {{ result.text }}
</template>
<script>
export default {
name: 'results-view',
props: ['results']
...
}
</script>
myview.pug
extends ../layout.pug
block content
results-view(results=results)
There are times when it makes sense to pass data from the router to your frontend component. No need to write an API and fetch that data in another roundtrip.
const customer = { id: '123', name: 'Jojo Sasa' }
res.render('customer/detail', { customer })
customer/detail.pug
// This will define customer as a global variable
script window.customer= !{JSON.stringify(customer)}
//The below code will run in the browser
script.
// This code will run in the browser
alert(`Customer is: ${cutomer.name}`)