Vue scope: how to delay handling of #mouseover - javascript

So I want to have an action only if the user has the mouse on the div for at least 1 second. Inside template:
<div #mouseover="trigger"></div>
Inside script:
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }, 1000)
}
}
My problem is that I don't understand the scope of Vue. Because this.hovered is inside another function, it does not find the actual hovered data variable. What's the solution to this?

Have you tried using an arrow function in your setTimeout? It will maintain this.
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(() => { this.hovered = true }, 1000)
}
}

<div #mouseover="activarOver" #mouseleave="resetOver "> eventos </div>
data: () => {
return {
countMouseOver: 0
}
},
methods: {
activarOver () {
this.countMouseOver++
if (this.countMouseOver < 2) {
this.setMostrarPopup()
}
console.log(this.countMouseOver)
},
resetOver () {
this.countMouseOver = 0
console.log('reset')
},
}

Implementation to show when hovered over for 1 second, then disappear when no longer hovered.
<span #mouseover="hover" #mouseleave="unhover">Hover over me!</span>
<div v-if="show">...</div>
data() {
return {
show: false;
hovering: false,
};
},
methods: {
hover() {
this.hovering = true;
setTimeout(() => this.show = this.hovering, 1000);
},
unhover() {
this.hovering = false;
this.show = false;
},
}

I have been solving this problem for selecting items in a list only if the user hovers for some time (to prevent menu flickering)
Template (pug):
.div(
#mouseover="select(identifier)"
#mouseout="clear()"
) {{content}}
Data:
hovered: false
selectedId: ""
and the methods
select(identifier) {
this.selectedId = identifier
setTimeout(() => {
if(this.selectedId === identifier )
this.hovered = true
},
1000
)
},
clear() {
this.selectedId = ''
}
the approach is to check if whatever user is hovering on is the same as they were hovering on a second ago.

If you want to use old syntax, bind 'this' to the setTimeout function
data() {
return {
hovered: false
}
}
methods: {
trigger() {
setTimeout(function(){ this.hovered = true }.bind(this), 1000)
}
}

Related

Trigger a function if countdown reaches 0 - VueJS

I have created a countdown timer which decrease a number in the template perfectly, but now I need it to launch a function declared within methods after it reaches 0. I've tried to check if condition is met within methods but it doesn't launch anything when reaching 0.
Here is my index.vue
<template>
<div>
{{ timerCount }}
</div>
</template>
<script>
export default {
data(){
return{
timerCount: 60
}
},
watch: {
timerEnabled(value) {
if (value) {
setTimeout(() => {
this.timerCount = this.timerCount - 1;
}, 1000);
}
},
timerCount: {
handler(value) {
if (value > 0 && this.timerEnabled) {
setTimeout(() => {
this.timerCount = this.timerCount - 1;
}, 1000);
}
},
immediate: true
},
},
methods:{
launchThis() {
// I need this function to be launched if timerCount reaches 0
}
}
}
</script>
Any guidance to make it work will greatly appreciated.
You can use something like this
<script>
export default {
watch: {
timerCount: {
handler(value) {
if (value > 0 && this.timerEnabled) {
setTimeout(() => {
this.timerCount = this.timerCount - 1;
}, 1000);
} else {
this.launchThis() // run your function here
}
},
immediate: true
},
},
}
</script>

How to know from which child is emitting an event in vue.js 3?

If I have a component:
<product-display
:premium="premium"
:cart="cart"
#add-to-cart='updateCart'
#remove-from-cart='removeById'>
</product-display>
with these two methods:
methods: {
removeFromCart() {
this.$emit('remove-from-cart', this.variants[this.selectedVariant].id)
},
addToCart() {
this.$emit('add-to-cart', this.variants[this.selectedVariant].id)
},
And the parent has these methods:
methods: {
updateCart (id) {
this.cart.push(id)
},
removeById(id) {
const index = this.cart.indexOf(id)
if (index > -1) {
this.cart.splice(index, 1)
}
}
}
Is there a way to remove one method from the parent and use only updateCart(id) knowing from which child is emitting the event?
So in the HTML you end up with:
#add-to-cart='updateCart'
#remove-from-cart='updateCart'
You can do that in a few different ways. My preferred one is like this:
// Component.vue
updateCart(action) {
this.$emit('update-cart', {
id: this.variants[this.selectedVariant].id), action
}
//Parent.vue
#update-cart='updateCart'
methods: {
updateCard({id, action}) {
if (action == 'add') {
return this.cart.push(id);
}
this.card = this.card.filter(x => x != id);
}
}

Display currentTime of a song in vue

I'd like to know how can I make time constantly update itself. So when I press the play button the seconds start to update automatically from 0:00 to the end , because now it just updates onclick. I am trying to use HTML5 audio and I have successfully managed to get the time updating as a label from this line of code:
sound.ontimeupdate = function () { document.getElementById('Time').innerHTML = sound.currentTime.toFixed() }
But thats not what I need, I would like to get the time attribute in data() to get updated and displayed in the label as shown in my HTML code.
I tried adding an event listener but it did not work... It gets called and every call was logged with console.log but time attribute was not updated
let sound = null
export default {
data () {
return {
isPlaying: false,
time: 0,
duration: 0
}
},
methods: {
playMusic () {
if (!sound) {
sound = new Audio(require('assets/YES.mp3'))
}
this.isPlaying = true
sound.play()
// sound.addEventListener('timeupdate', function () { this.time = sound.currentTime.toFixed() }) -- did not work
this.time = sound.currentTime.toFixed()
}
Html:
<label id="Time" #timeupdate>
{ { time } }:{ { duration } }
</label>
Inside your addEventListener you get a different this than you might expect.
Either use fat arrow
sound.addEventListener('timeupdate', () => this.time = sound.currentTime.toFixed() )
or, the old way, save this
let that = this
sound.addEventListener('timeupdate', function () { that.time = sound.currentTime.toFixed() })
you could just add a generic timer dynamically. You can use a watch to add/remove it like so:
(untested code)
export default {
data() {
return {
isPlaying: false,
time: 0,
duration: 0,
intervalId: null,
sound: null
};
},
watch: {
isPlaying(isPlaying) {
if (this.intervalId !== null) {
clearInterval(this.intervalId);
}
if (isPlaying) {
this.sound.play();
this.intervalId = setInterval(() => {
this.time = this.sound.currentTime.toFixed();
}, 500);
} else {
this.sound.stop();
}
}
},
methods: {
playMusic() {
if (!this.sound) {
this.sound = new Audio(require("assets/YES.mp3"));
}
this.isPlaying = true;
}
}
};

Vuetify - How to access data in form rule

Can I access a data element in a rule?
Here is my code running
I'm trying to flip the value of a data element on a text field rule in a Vuetify form.
The rule itself works fine, however I'm unable to access the data element, I'm getting this error:
TypeError: Cannot set property 'disabled' of undefined
Here is my code:
data: function() {
return {
disabled: false,
rules:{
sellerId(value){
if(value.length == 0){
this.disabled = true;
return "What are you trying to do here?";
}
else{
return true;
}
}
},
What am I doing wrong?
rules are an array of functions, and if you need the function to be able to access data property, you can define them as component methods:
data: function () {
return {
disabled: false
}
},
methods: {
sellerId (value) {
if (value.length === 0) {
this.disabled = true;
return "What are you trying to do here?";
} else {
return true;
}
}
}
And then in your Vuetify component:
<v-text-field :rules="[ sellerId ]"></v-text-field>
try to define rules as computed property :
data: function() {
return {
disabled: false,
...
}
},
computed: {
sellerIdRules() {
return [
(v) => {
if (value.length == 0) {
this.disabled = true;
return "What are you trying to do here?";
} else {
return true;
} ]
}
}
}
While this isn't available to a rule function you can accomplish this by assigning the vue instance to a variable, which will bring it into scope by closure.
vm = new Vue({
el: '#app',
data: () => ({
disabled: true,
rules: [
value => {
if (value.length == 0) {
vm.disabled = true;
return "What are you trying to do here?";
}
else {
return true;
}
}
],
'''

Using a variable as a function name that could be multiple levels deep

I have had a look around the site and seem to have come across some solutions, but none seem to work. So here's the problem.
I have the following Javascript:
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
THEOBJECT[functionName]();
}, time);
}
};
THEOBJECT.init();
I am getting an error when running the THEOBJECT.utils.timeout saying:
THEOBJECT[functionName] is not a function
I'm trying to run THEOBJECT.tools.init()
I'm assuming this is because it's not a direct function of the THEOBJECT object is there a way around this or is a split() the best way to go?
There is other option to do it using lodash.js. Please see the updated snippet.
Or go with conventional approch mentioned below
You are trying to access a property in wrong way
you are doing THEOBJECT[tools.init] which incorrect. It should be like THEOBEJECT[tools][init]
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
console.log("active")
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
_.get(THEOBJECT,functionName);
}, time);
}
};
THEOBJECT.init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
you can also go with a simple reduce and make it a one-liner:
functionName.split('.').reduce((acc,curr) => return acc[curr], THEOBJECT)();
within your code:
THEOBJECT = {
init: function() {
this.tools.init();
}
};
THEOBJECT.tools = {
active: false,
init: function() {
// active variable updated elsewhere/loading
if (!this.active) {
THEOBJECT.utils.timeout('tools.init', 100);
}
// is active so continue
}
};
THEOBJECT.utils = {
timeout: function(functionName, time) {
setTimeout(function() {
functionName.split('.').reduce((acc,curr) => acc[curr],THEOBJECT)();
}, time);
}
};
THEOBJECT.init();

Categories

Resources