Vue: Change class with the value of a variable in setInterval - vue.js

I am learning Vue JS. I want to change class using setInterval. But can’t pass the changing value of Method to Computed Property. After two seconds class will change automatically with the changed value of "changeColor"
My Code:
HTML:
<div>
<button #click="startEffect">Start Effect</button>
<div id="effect" :class="myClass"></div>
</div>
CSS:
.highlight {
background-color: red;
width: 200px !important;
}
.shrink {
background-color: gray;
width: 50px !important;
}
Vue JS:
new Vue({
el: '#exercise',
data: {
changeColor: false
},
methods : {
startEffect: function() {
setInterval(function(){
this.changeColor = !this.changeColor;
//alert(this.changeColor);
}, 2000);
//alert(this.changeColor);
}
},
computed: {
myClass: function() {
return {
highlight: this.changeColor,
shrink: !this.changeColor
}
}
}
})

bind your function to the component...
setInterval(function(){
this.changeColor = !this.changeColor;
}.bind(this), 2000);
and then you can do ...
<div id="effect" :class="[changeColor?'highlight':'shrink']"></div>

Related

Pass a variable to a child component

I'm trying to pass a class name as a variable to a child component.
Context: I have two types of header ( 2 different bakgrounds ) used all across the site.
Page1:
<HeaderMain bg="bg" />
data() {
return {
pageTitle: 'patterson.travel',
bg: 'bg-white',
}
}
Page:2
<HeaderMain bg="bg" />
data() {
return {
pageTitle: 'patterson.travel',
bg: 'bg-header',
}
}
HeaderMain :
<header>
<nav class="main md:bg-transparent" :class="bg"></nav>
</header>
But the class never gets applied to the <nav>
I tried adding the variable to HeaderMain component like so (as a default):
data() {
return {
bg: 'bg-red', // default?
}
}
But That is the class it allways has...
So, any idea what I'm missing, here?
( I also tried :bg="bg" )
You have to define the bg property on your HeaderMain component:
// HeaderMain.vue
<script>
export default {
props: ['bg']
}
</script>
You will then be able to use it in your template just like you did:
<header>
<nav class="main md:bg-transparent" :class="bg"></nav>
</header>
In order to access it, you need to define props as an array inside your children component.
Look at the code below.
Vue.component('user-name', {
props: ['name'],
template: '<p>Hi {{ name }}</p>'
})
const LastName = {
template: '<span> Patel </span>'
}
const Header = {
props: ['bg'],
template: '<span :class="bg"> {{bg}} Header </span>'
}
new Vue({
el: "#app",
data: function() {
return {
headerBg: 'header-bg'
}
},
components: {
'last-name': LastName,
'header-view': Header
}
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.header-bg {
background: red;
}
.header-bg {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header-view :bg="headerBg"></header-view>
<user-name name="Varit"></user-name>
<last-name></last-name>
</div>

Can I execute a method when an input box gets to a certain length?

I have an input box that takes a string. Can I execute a method (in vue.js) when the length of the string gets to a certain number?
something like
<input v-if="inputBox.length == 6 THEN runme()"...>
You can use watch option, you'll be able to react to data changes :
new Vue({
el: '#root',
data: {
message: '',
inputLength : undefined
},
methods : {
doSomething(){
console.log('I did it !')
}
},
watch :{
message : function(val) {
if(val.length>=5){
this.inputLength = val.length
this.doSomething();
}
}
}
})
.container {
padding-top: 2em;
}
.intro {
font-size: 1.5em;
margin-bottom: 1.5em;
}
.input-value {
margin-top: 1em;
font-size: 1.25em;
}
.highlight {
color: #00d1b2;
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container">
<h1 class="intro">Binding with Vue</h1>
<div id='root' class="box">
<label class="label">Enter text here</label>
<input class="input is-medium" type='text' id='input' v-model='message'>
<p class="input-value">The value of the input is: <span class="highlight">{{ inputLength }}</span></p>
</div>
</div>
In this example, if input length is >= 5 then it will change the inputLenght value in data and execute a method.
For more informations about this, go see :
https://v2.vuejs.org/v2/guide/computed.html#Watchers
You can use a watcher to trigger a method when the string exceeds the length:
new Vue({
data () {
return {
model: ''
}
},
watch: {
model: {
handler: function (value) {
if (value.length >= 6) {
this.trigger()
}
}
}
},
el: '#app',
methods: {
trigger () {
alert('hi there')
}
},
template: `<input v-model="model">`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Vuejs: autofit div according to remaining space in window

Within vueJs (2.5.16) I'm trying to set the style property of a div so it auto fit the remaining space of a window depending of its size:
I intended to use a computed value which would give me live the correct height and bind it into the style property of the wanted div:
Vue.component('menus', {
template: '<div id="menu-div">MY MENU</div>'
});
Vue.component('expandable', {
template: '<div id="expandable-div" :style="{height:expandableHeight}">EXPANDABLE<div>{{expandableHeight}}</div></div>',
computed: {
expandableHeight() {
return ($(window).height() - $("#expandable-div").position().top) + 'px'
}
}
});
var vm = new Vue({
el: '#app'
});
fiddle
Using $(window).height() and $("#expandable-div").position().top from jQuery I thought I could achieve a result since it works in my console.
Unfortunately I have an error:
TypeError: Cannot read property 'top' of undefined
Why not use CSS's flexbox to achieve that?
Vue.component('menus', {
template: '<div id="menu-div">MY MENU</div>'
});
Vue.component('expandable', {
template: '<div id="expandable-div">EXPANDABLE<div>foo</div></div>',
computed: {}
});
var vm = new Vue({
el: '#app'
});
body {
margin: 0;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
#menu-div {
height: 50px;
background: #768ffb;
margin-bottom: 5px;
}
#expandable-div {
background: #76fbc1;
flex-grow: 1;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<menus></menus>
<expandable></expandable>
</div>

Vue.js css transition on class change

I am trying to use the css transition property to fade the #app background from black to white by adding a class of .lightTheme. The adding of .lightTheme class works as expected, but the transition does not.
Do I have to add the Vue transition element? And if so how? #app is not leaving/entering the dom - I just want to animate it's properties (and I don't want to add unnecessary code if possible!
HTML
<div id="app" v-bind:class="{ lightTheme: isActive }">
<button v-on:click="toggleTheme">Change theme</button>
</div>
JS
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
// some code to filter users
}
}
});
CSS
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
Thanks!
To answer your question if you have to use transition for something like this, the answer is no. The solution you proposed should work out of the box.
There is probably something else that is interfering with your code, but the one you posted is correct.
I attached the working snippet.
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
}
}
});
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app" :class="{ lightTheme: isActive }">
<button #click="toggleTheme">Change theme</button>
</div>

flash message sometimes does not disappear in vue2.js

I have just made a flash message component, which receives flash messages from the eventBus and then displays the flash message for 3 seconds before disappearing. The component is as follows:
<template>
<transition name="fade">
<div v-if="visible" v-bind:class="type" role="alert">{{ message }}</div>
</transition>
</template>
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
</style>
<script>
import EventBus from '../../config/EventBus';
export default {
name: 'flash-view',
data() {
return {
type: '',
message: '',
visible: false,
};
},
mounted() {
EventBus.subscribeFlashMessage(data => this.setData(data));
setTimeout(() => (
this.visible = false
), 3000);
},
methods: {
setData(data) {
this.setType(data.type);
this.message = data.message;
this.visible = true;
},
setType(type) {
this.type = `alert alert-${type}`;
},
},
};
</script>
The component works perfectly for the first flash message, however if flash messages are triggered subsequently or if I change routes (VueRouter) then the flash message does not disappear. I presume this is because javascript is retriggered, which nulls the effect of setTimeout, however I have no idea how to fix this in Vue.
I have managed to fix the bug, thanks to Belmin Bedak's help. The following is now implemented with created() instead of mounted(), as mounted gets retriggered for every update cycle. Created() sets the listener once, and watch is used to check if the visible has been changed (this happens when a new event is pushed to the listener). If the visible is changed, the setTimeout gets triggered properly.
<template>
<transition name="fade">
<div
v-if="visible"
v-bind:class="type"
role="alert"
v-text="message"
>
</div>
</transition>
</template>
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to {
opacity: 0
}
</style>
<script>
export default {
name: 'flash-view',
data() {
return {
type: '',
message: '',
visible: false,
};
},
created() {
this.$on('flashMessage', data => this.setData(data));
},
methods: {
setData(data) {
this.type = `alert alert-${data.type}`;
this.message = data.message;
this.visible = true;
},
setFadeOut() {
setTimeout(() => (
this.visible = false
), 2500);
},
},
watch: {
visible: 'setFadeOut',
},
};
</script>