An API call (Firebase) determines the return value of my computed property and hence whether I display certain elements in my template.
Yet, my computed property is actually not reactive: its value in my template doesn't change upon the API call. Why not?
JS:
myComputedProperty: function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
return true;
}
else {
return false;
}
});
}
Template:
<span v-if="userConnected">
User connected
</span>
Your return value isn't the return value for your computed property. It's the return value of the callback function.
You should be using data() and created() or mounted() to check this instead:
data () {
return {
isAuthenticated: false
};
},
created () {
firebase.auth().onAuthStateChanged((user) => {
this.isAuthenticated = !!user
});
}
Related
I'm having lots of elements on which #mouseenter set a value to true and #mouseleave sets it to false. Basically what I need is a way to set a reactive variable to true if the mouse hovers the element.
I've been trying to figure out how to write such custom directive from the docs but it only mentions how to use .focus() js function on an element. Which js functions would be used for said directive?
Something like:
const vHover = {
mounted: (el) => {
el.addEventListener('mouseenter', state.hover=true)
el.addEventListener('mouseleave', state.hover=false)
}
}
I think you could do something like:
app.directive('hover', {
created(el, binding) {
const callback = binding.value
el.onmouseenter = () => callback(true)
el.onmouseleave = () => callback(false)
},
unmounted(el) {
el.onmouseenter = null
el.onmouseleave = null
}
})
Template:
<button v-hover="onHoverChange">Example</button>
Methods:
onHoverChange(isHovered) {
console.log(isHovered)
}
I believe this is not the intended use of directives. The value of the state cannot be mutated within the directive. You can pass the variable through the binding, but you cannot update it.
binding: an object containing the following properties.
value: The value passed to the directive. For example in v-my-directive="1 + 1", the value would be 2.
oldValue: The previous value, only available in beforeUpdate and updated. It is available whether or not the value has changed.
so if you do el.addEventListener('mouseenter', binding.hover=true), as you may have noticed, it will not update the state.
However, if we use the internals (PSA: though not recommended since they could potentially change at any time), you could get instance using the vnode, and use the binding.arg to denote which Proxy (state)
so you could get the reactive variable with vnode.el.__vueParentComponent.data[binding.arg]
<script>
export default {
data(){
return {
state: { hover:false }
}
},
directives: {
hover: {
mounted(el, binding, vnode) {
el.addEventListener('mouseenter', () => {
vnode.el.__vueParentComponent.data[binding.arg].hover = true
})
el.addEventListener('mouseleave', () => {
vnode.el.__vueParentComponent.data[binding.arg].hover = false
})
},
}
}
}
</script>
<template>
<h1 v-hover:state="state">HOVER {{ state }}</h1>
</template>
SFC playground link
of course you might want to add the unmounted and even consider adding mouseleave dynamically only when mouseenter fires
This is how it can be done inside the component:
const vHover = {
mounted: (el) => {
el.addEventListener('mouseenter', () => {state.hover=true})
el.addEventListener('mouseleave', () => {state.hover=false})
},
unmount: (el) => {
el.removeEventListener('mouseenter', () => {state.hover=true})
el.removeEventListener('mouseleave', () => {state.hover=false})
}
}
I am retrieving data from the Vuex Store. I first of all want to check of the array is present in the Vuex Store, after that I want to check if the noProducts object at index 0 is not present.
The reason for this is that tweakwiseSortedProducts is used for both products and a no Products boolean to react to in the front-end
tweakwiseHasProducts () {
if (this.$store.state.tweakwise?.tweakwiseSortedProducts) {
return (
this.$store.state.tweakwise.tweakwiseSortedProducts[0].noProducts ===
false
);
}
return false;
},
My front-end currently, often, returns:
this.$store.state.tweakwise.tweakwiseSortedProducts[0] is undefined
In the console.
This happens because tweakwiseSortedProducts is not undified but an empty list. You can try:
tweakwiseHasProducts () {
if (this.$store.state.tweakwise?.tweakwiseSortedProducts?.length !== 0) {
return (
this.$store.state.tweakwise.tweakwiseSortedProducts[0].noProducts ===
false
);
}
return false;
},
or just:
tweakwiseHasProducts () {
return this.$store.state.tweakwise?.tweakwiseSortedProducts[0]?.noProducts === false;
},
which will be false if any of this elements is undefined, or true if noProducts is really false
It is recommended to use getter when calling a value in Vuex.
Please refer to the following.
getters: {
getTweakwiseSortedProducts: (state: any) => {
return state.tweakwise?.tweakwiseSortedProducts || [];
},
},
tweakwiseHasProducts () {
this.$store.getters.getTweakwiseSortedProducts.length ? true : false;
}
I am trying to be able to read a value that is boolean to see if a user did a specific action or not and I am using the ReactJS functional component style. I am trying to read the runValue in my code to see if the run() method changed the value and I want to be able to read this value without recalling the function.
I want to be able to put in my useEffect method this line of code;
Run.RunFunction().run((index) => {
if (index) {
\\\ do stuff here if index is true
} else {
///if index is false
}
}
my code
const Run = {
RunFunction: () => {
let runValue = false;
return {
run() {
runValue = true
},
listener: function(val) {},
checkStatus: function(listen) {
this.listener = listen
}
}
},
}
Run.RunFunction().checkStatus((index) => {
if (index) {
console.log('running')
} else {
console.log('not running')
}
});
I am having trouble having this code to work and I want to be able to see the value of the runValue initially and if it changes.
Thank you
i have application that calls several requests and displays that data. Everything is working, but I getting some errors that I can't figure out where is the problem..
So I have two components:
--App :Parent
---Events :Children
In App.vue calling children component:
<Events :events="gameInfo" :results="results" #startNewGame="startNewGame" />
Giving as a props "gameInfo", "results" and listening for "startNewGame" event.
When application loads first time in App.vue i'm calling function:
// Get Next Game
getNextGame() {
this.gameInfo = [];
RouletteApi.getNextGame().then(response => {
this.gameInfo.push({ response });
});
}
That children component receives and displays data.
In children component:
<script>
export default {
name: "Events",
props: ["events", "results"],
data() {
return {
GameStartTime: null,
GameId: null,
GameUUID: null
};
},
watch: {
events: function(newVal, oldVal) {
this.GameStartTime = newVal[0]["response"].fakeStartDelta--;
this.GameId = newVal[0]["response"].id;
this.GameUUID = newVal[0]["response"].uuid;
}
},
created() {
setInterval(() => {
if (this.GameStartTime > 0) {
this.GameStartTime = this.events[0]["response"].fakeStartDelta--;
} else {
this.$emit("startNewGame", this.GameUUID); -- call to parent function
}
}, 1000);
}
};
</script>
I watching, getting the data and setting timer, to execute "startNewGame" function from parent, that will make another api call and give children new data.
After timer expires I'm calling "startNewGame" function from parent:
startNewGame(uuid) {
this.startSpinning();
RouletteApi.startNewGame(uuid).then(response => {
if (response.result == null) {
setTimeout(function() {
startNewGame(uuid);
}, 1000);
} else {
this.results.push({ response });
this.gameInfo = []; -- resetting that previous dat
this.getNextGame(); -- call to first function in example
}
});
That checks if response is null then setting timeout and calling that function until response will be not null. If response came not null than I pushing to children result, resetting that gameInfo array and calling again getNextGame() function that will call request and set new value for timer in children component.
RouletteApi.js:
import axios from 'axios'
export default {
getLayout() {
return axios.get('/configuration')
.then(response => {
return response.data
})
},
getStats() {
return axios.get('/stats?limit=200')
.then(response => {
return response.data
})
},
getNextGame() {
return axios.get('/nextGame')
.then(response => {
return response.data
})
},
startNewGame(uuid) {
return axios.get('/game/' + uuid)
.then(response => {
return response.data
})
}
}
Errors:
Error in callback for watcher "events": "TypeError: Cannot read property 'response' of undefined"
TypeError: Cannot read property 'response' of undefined
at VueComponent.events (Events.vue?5cf3:30)
Uncaught ReferenceError: startNewGame is not defined
First two errors i'm getting from children component in "watch" part.
Last one when calling function in setInterval in parent component.
It looks like the watcher is running before the api call finished. Console log the new value to see what your get. Try to check if the newVal is not null or an empty array and then set the values.
I have a Vue component that has a vue-switch element. When the component is loaded, the switch has to be set to ON or OFF depending on the data. This is currently happening within the 'mounted()' method. Then, when the switch is toggled, it needs to make an API call that will tell the database the new state. This is currently happening in the 'watch' method.
The problem is that because I am 'watching' the switch, the API call runs when the data gets set on mount. So if it's set to ON and you navigate to the component, the mounted() method sets the switch to ON but it ALSO calls the toggle API method which turns it off. Therefore the view says it's on but the data says it's off.
I have tried to change the API event so that it happens on a click method, but this doesn't work as it doesn't recognize a click and the function never runs.
How do I make it so that the API call is only made when the switch is clicked?
HTML
<switcher size="lg" color="green" open-name="ON" close-name="OFF" v-model="toggle"></switcher>
VUE
data: function() {
return {
toggle: false,
noAvailalableMonitoring: false
}
},
computed: {
report() { return this.$store.getters.currentReport },
isBeingMonitored() { return this.$store.getters.isBeingMonitored },
availableMonitoring() { return this.$store.getters.checkAvailableMonitoring }
},
mounted() {
this.toggle = this.isBeingMonitored;
},
watch: {
toggle: function() {
if(this.availableMonitoring) {
let dto = {
reportToken: this.report.reportToken,
version: this.report.version
}
this.$store.dispatch('TOGGLE_MONITORING', dto).then(response => {
}, error => {
console.log("Failed.")
})
} else {
this.toggle = false;
this.noAvailalableMonitoring = true;
}
}
}
I would recommend using a 2-way computed property for your model (Vue 2).
Attempted to update code here, but obvs not tested without your Vuex setup.
For reference, please see Two-Way Computed Property
data: function(){
return {
noAvailableMonitoring: false
}
},
computed: {
report() { return this.$store.getters.currentReport },
isBeingMonitored() { return this.$store.getters.isBeingMonitored },
availableMonitoring() { return this.$store.getters.checkAvailableMonitoring },
toggle: {
get() {
return this.$store.getters.getToggle;
},
set() {
if(this.availableMonitoring) {
let dto = {
reportToken: this.report.reportToken,
version: this.report.version
}
this.$store.dispatch('TOGGLE_MONITORING', dto).then(response => {
}, error => {
console.log("Failed.")
});
} else {
this.$store.commit('setToggle', false);
this.noAvailableMonitoring = true;
}
}
}
}
Instead of having a watch, create a new computed named clickToggle. Its get function returns toggle, its set function does what you're doing in your watch (as well as, ultimately, setting toggle). Your mounted can adjust toggle with impunity. Only changes to clickToggle will do the other stuff.