How to use transitions on select option using Vue.js - javascript

I have quick question regarding transitions with Vue.js.
In my boostrap template I'm trying to add one more select option dropdown based on selected option. So, I added change event on first select option. So, if I select 'first item' then change classes and add dropdown in row, or else hides it.
Something like this:
selectTodo: function(e) {
let selectValue = e.target.options[e.target.selectedIndex].text
if (selectValue === 'Learn Vue') {
this.styleObject.display = 'unset';
this.col_md = 'form-group col-md-4';
this.showCropStageList = true;
}
else {
this.showCropStageList = false;
this.styleObject.display = 'none';
this.col_md = 'form-group col-md-6';
this.cropStageList = null;
}
}
I managed to do that but I want more smoother transitions on the first two dropdowns.
I've managed to set up some fiddle where you can see smooth slide transition for third select dropdown but if if I change select from "Learn Vue" to something else, the third dropdown hides it but without any animation as well.
You can see it in the snippet below, too!
new Vue({
el: "#app",
data: {
todos: [
{ id:1 ,text: "Learn JavaScript", done: false },
{ id:2 ,text: "Learn Vue", done: false },
{ id:3 ,text: "Play around in JSFiddle", done: true },
{ id:4 ,text: "Build something awesome", done: true }
],
col_md: 'form-group col-md-6',
styleObject: {
display: 'none'
},
showCropStageList: false,
},
methods: {
toggle: function(todo){
todo.done = !todo.done
},
selectTodo: function(e) {
let selectValue = e.target.options[e.target.selectedIndex].text
if (selectValue === 'Learn JavaScript') {
this.styleObject.display = 'unset';
this.col_md = 'form-group col-md-4';
this.showCropStageList = true;
}
else {
this.showCropStageList = false;
this.styleObject.display = 'none';
this.col_md = 'form-group col-md-6';
this.cropStageList = null;
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
.fade-enter {
opacity: 0;
}
.fade-enter-active {
transition: opacity 1s;
}
.fade-leave {}
.fade-leave-active {
transition: opacity 1s;
opacity: 0;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div class="container">
<!-- Content here -->
<div id="app">
<div class="form-row">
<transition name="fade" appear>
<div v-bind:class=col_md>
<label for="cropType" class="col-form-label-sm font-weight-bold">Select Learn Javascript </label>
<select class="form-control" v-on:change="selectTodo" id="cropType" v-model="pickedCropType" #change="getCropsByType()">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
</transition>
<div v-bind:class=col_md>
<label for="cropCulture" class="col-form-label-sm font-weight-bold">2. Second</label>
<select class="form-control" id="cropCulture">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
<transition
enter-active-class="animated fadeInLeft"
leave-active-class="animated fadeOutLeft"
>
<div class="form-group col-md-4" v-if="showCropStageList" v-bind:class="{styleObject }">
<label for="cropStage" class="col-form-label-sm font-weight-bold">3. Third</label>
<select class="form-control" id="cropStage">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
</transition>
</div>
</div>
</div>
You can see change class for first two dropdowns but without any transitions. So, is it possible to add some transitions for first two dropdowns?
EDIT :
As you can see I'm changing class from *form-group col-md-6* to *form-group col-md-4* and I' wondering if is it possible that that tranisition from md-6 to md-4 can be a bit smoother.
https://jsfiddle.net/Loque/akt0su98/

In your codes, you directly update the class from col-md-4 to col-md-6 in selectTodo. It will cause first two <div> take full width of the row before 3rd div finishes its animation.
The solution, uses this.$nextTick and setTimeout.
when add the third div, delay execute show the third div before first two <div> finish their animations.
when remove the third div, delay apply col-md-6 to the first two <div> before the third <div> finishes its animations.
Below is one sample: (PS: I used transition-group here, because it will be better for Reusable Transitions)
Warning: Please test below demo under full page, otherwise bootstrap will treat it as extre-small screen (place each <div> in each row), though I think the effects in small screen is still not bad.
new Vue({
el: "#app",
data: {
todos: [
{ id:1 ,text: "Learn JavaScript", done: false },
{ id:2 ,text: "Learn Vue", done: false },
{ id:3 ,text: "Play around in JSFiddle", done: true },
{ id:4 ,text: "Build something awesome", done: true }
],
col_md: 'form-group col-md-6',
styleObject: {
display: 'none'
},
showCropStageList: false,
pickedCropType: ''
},
methods: {
getCropsByType: function () {},
toggle: function(todo){
todo.done = !todo.done
},
selectTodo: function(e) {
let selectValue = e.target.options[e.target.selectedIndex].text
if (selectValue === 'Learn Vue') {
this.col_md = 'form-group col-md-4';
this.$nextTick(() => { //delay display the third div before first two div finish animation
setTimeout(()=>{
this.styleObject.display = 'unset';
this.showCropStageList = true;
}, 500)
})
}
else {
this.showCropStageList = false;
this.styleObject.display = 'none';
this.$nextTick(() => { //delay apply `md-6` to first two div before 3rd div finish animation
setTimeout(()=>{
this.col_md = 'form-group col-md-6';
}, 1000)
})
this.cropStageList = null;
}
}
}
})
.sample {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.fade-item {
transition: all 0.5s;
display:inline-block;
}
.fade-enter {
opacity: 0;
transform: translateX(-130px);
}
.fade-enter-active {
transition: all 1.5s;
}
.fade-leave-to {
transform: translateX(130px);
opacity: 0;
flex: 0 0 20%;
}
.fade-leave-active {
transition: all 1s;
}
#app {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<!-- Content here -->
<div id="app">
<div class="container">
<div class="sample">
<transition-group name="fade" tag="div" class="form-row">
<div v-bind:class="col_md" :key="1" class="fade-item">
<label for="cropType" class="col-form-label-sm font-weight-bold">Select Learn Javascript </label>
<select class="form-control" v-on:change="selectTodo" id="cropType" v-model="pickedCropType" #change="getCropsByType()">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
<div v-bind:class="col_md" :key="2" class="fade-item">
<label for="cropCulture" class="col-form-label-sm font-weight-bold">2. Second</label>
<select class="form-control" id="cropCulture">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
<div class="form-group col-md-4 fade-item" v-if="showCropStageList" v-bind:class="{styleObject }" :key="3">
<label for="cropStage" class="col-form-label-sm font-weight-bold">3. Third</label>
<select class="form-control" id="cropStage">
<option v-for="(todo, index) in todos" :key="index" :value="todo.id" >
{{todo.text}}
</option>
</select>
</div>
</transition-group>
</div>
</div>
</div>

I don't know if there is a specific way to do in Vue but what you want can be easily achieved using some CSS magic. It is simple in a matter of fact, all you are going to do is add a class to your changing div let's say for example the class name is transition and in your CSS you define transition styles as follow:
.transition {
transition: all 0.25s linear;
}
You can find an updated fiddle here
Note you can change the timing function linear and the time 0.25s to suit your needs, you can read more about CSS transitions and effects here

Related

Add background color when data changes with Vue

I have 3 containers where the first two of them have a drag and drop function, so that when I drop an element there, it changes that container color. This works correctly, but what I want to do is that when the 1st and 2nd container have a color, the 3rd one automatically updates to a new color. I am using Vuejs.
For instance: Container 1 has red color and container 2 has yellow color. The 3rd container would automatically update to an orange color. This would happen everytime I change the color of the containers.
This is my code:
HTML
<!-- the containers where I drop the elements -->
<div class="box-flex">
<div class="box" #dragover.prevent #drop="drop" id="box-1"></div>
<div class="box" #dragover.prevent #drop="drop2" id="box-2"></div>
<div class="box" id="box-3"></div>
</div>
<!-- the elements I want to drag -->
<div v-for="(flower, i) in flowers" :key="i">
<div v-if="flower_type == flower.type">
<p>{{ flower.type }}</p>
<p v-for="(color, j) in flower.colors" :key="j">
<span
:id="flower.type + '-' + color"
:draggable="true"
#dragstart="dragStart"
#dragover.stop
>{{ color }}</span>
</p>
</div>
</div>
SCRIPT
data() {
return {
//flowers
flowers: [
{
type: "Cosmos",
colors: ["red", "yellow", "orange", "black", "pink", "white"]
},
],
};
},
methods: {
dragStart(e) {
let target = e.target;
e.dataTransfer.setData("color_id", target.id);
e.dataTransfer.setData("box_el", target);
let box1 = document.getElementById("box-1");
},
//first container drop
drop(e) {
let colorId = e.dataTransfer.getData("color_id"); //id of the color (red, yellow...)
let colorEl = e.dataTransfer.getData("box_el"); //element of the color
let box1 = document.getElementById("box-1");
box1.classList.add(colorId);
if (box1.classList.length > 2) { //I only want one color-class in the container
box1.classList.remove(box1.classList[1]);
}
},
//second container drop
drop2(e) {
let colorId = e.dataTransfer.getData("color_id"); //id of the color (red, yellow...)
let colorEl = e.dataTransfer.getData("box_el"); //element of the color
let box2 = document.getElementById("box-2");
box2.classList.add(colorId);
if (box2.classList.length > 2) {
box2.classList.remove(box2.classList[1]);
}
}
},
computed: {
evFlower() {
let box1 = document.getElementById("box-1");
let box2 = document.getElementById("box-2");
let box3 = document.getElementById("box-3");
if (
box1.classList.contains("Cosmos-red") &&
box2.classList.contains("Cosmos-yellow")
) {
box3.classList.add("Cosmos-orange");
}
}
}
CSS
.box-flex {
height: 50%;
display: flex;
justify-content: space-around;
align-items: center;
}
.box,
.box-1,
.box-2 {
width: 20%;
height: 20%;
border: 2px solid black;
}
.Cosmos-red {
background: red;
}
.Cosmos-yellow {
background: yellow;
}
.Cosmos-orange {
background: orange;
}
I've tried with a watcher but haven't found a way to 'watch' on DOM changes.
Thanks in advance!!
I think you are over complicating things, it is better to avoid DOM manipulations unless it is strictly needed.
It is a better solution to add inline styles, or switch classes on the vue templates with reactive data instead of adding and removing classes directly on the DOM as you'd do with the old Jquery.
This is reactive, easy and cheap to control:
<div :style="{ background: box1}"></div>
I've rewrited your code following this rules:
<template>
<div>
<div class="box-flex">
<div class="box" #dragover.prevent #drop="drop" id="box-1" :style="{ background: box1}"></div>
<div class="box" #dragover.prevent #drop="drop2" id="box-2" :style="{ background: box2}"></div>
<div class="box" :style="{ background: box3}"></div>
</div>
<!-- the elements I want to drag -->
<div v-for="(flower, i) in flowers" :key="i">
<div>
<p>{{ flower.type }}</p>
<p v-for="(color, j) in flower.colors" :key="j">
<span
:id="color"
:draggable="true"
#dragstart="dragStart"
#dragover.stop
>{{ color }}</span>
</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Intro',
components: {
},
data() {
return {
//flowers
flowers: [
{
type: "Cosmos",
colors: ["red", "yellow", "orange", "black", "pink", "white"]
},
],
box1: 'white',
box2: 'white',
box3: 'white',
};
},
methods: {
dragStart(e) {
let target = e.target;
e.dataTransfer.setData("color_id", target.id);
e.dataTransfer.setData("box_el", target);
},
//first container drop
drop(e) {
this.box1 = e.dataTransfer.getData("color_id");
if ( this.box1 == 'red' && this.box2 == 'yellow') {
this.box3 = 'orange'
}
else {
this.box3 = 'white'
}
},
//second container drop
drop2(e) {
this.box2 = e.dataTransfer.getData("color_id");
if ( this.box1 == 'red' && this.box2 == 'yellow') {
this.box3 = 'orange'
}
else {
this.box3 = 'white'
}
}
},
};
</script>
<style>
.box-flex {
height: 50px;
display: flex;
justify-content: space-around;
align-items: center;
}
.box,
.box-1,
.box-2 {
width: 20px;
height: 20px;
border: 2px solid black;
}
.Cosmos-red {
background: red;
}
.Cosmos-yellow {
background: yellow;
}
.Cosmos-orange {
background: orange;
}
</style>

Enable / Disable Vue Transitions based on boolean

In Vue, can I somehow disable a transition-animation, based on a boolean?
So now the animation is enabled:
<transition name="fadeUp">
<div v-if="elIsVisible">
<p>Foo Bar</p>
</div>
</transition>
But I wish I do something like this:
<transition name="fadeUp" animation-enabled="false">
<div v-if="elIsVisible">
<p>Foo Bar</p>
</div>
</transition>
Or perhaps a smart work-around?
It's a for module-based website (one component per block), where it could be nifty if the user could enable/disable the animation for certain blocks.
There's a workaround, yes. You can skip the CSS detection by setting v-bind:css to false on the <transition> component.
new Vue({
el: '#app',
data: () => ({
show: true,
animated: true
})
})
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
p {
background-color: beige;
border: 1px solid orange;
padding: 4px 6px;
}
button {
display: block;
margin-top: 2em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<transition name="fade" :css="animated">
<p v-if="show">Hey, there!</p>
</transition>
<label>
<input type="checkbox" v-model="animated" />
Animated
</label>
<button #click="show = !show">Toggle visibility</button>
</div>
You can bind :name in <transition>, which if set to empty, no transition is applied (like when no name is given).
<transition :name="transitionName">
<div v-if="elIsVisible">
<p>Foo Bar</p>
</div>
</transition>
<select #change="e => { transitionName = e.target.value }">
<option
v-for="transition in ['fadeUp', '']"
:key="transition"
:value="transition"
>
{{ transition }}
</option>
</select>

VueJS - input file repeater

I want delete item from array but there are the same item and js delete last one !
let app = new Vue({
el: '#app',
data: {
items: []
},
methods: {
addItem() {
this.items.push('');
},
removeItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items">
remove
<input name="form[]" type='file'>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>
JSFiddle: https://jsfiddle.net/6hvbqju2/
Vue uses an "in-place patch strategy" when dealing with list of elements. This strategy is not suitable when relying on form input values.
When using v-for directive it is better to define a v-bind:key to give Vue a hint to track each node.
We'll store numbers in the items array and use them as a key. In your case you should use an item's property that can serve as a unique key.
let app = new Vue({
el: '#app',
data: {
counter: 0,
items: []
},
methods: {
addItem() {
this.items.push(this.counter++);
},
removeItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items" :key="item">
remove
<input name="form[]" type='file'>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>
Your codes working fine but,
This is because of file input auto complete behaviour
See this example
let app = new Vue({
el : '#app',
data : {
items: [],
},
methods : {
addItem() {
this.items.push({file: null});
console.log(this.items)
},
removeItem(index) {
this.items.splice(index,1);
},
handleChange(item, event){
item.file = event.target.files["0"];
}
}
});
.upload-btn-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
vertical-align: middle;
}
.btn {
border: 1px solid gray;
color: gray;
background-color: white;
padding: 4px 10px;
border-radius: 4px;
font-size: 15px;
font-weight: bold;
}
.upload-btn-wrapper input[type=file] {
font-size: 100px;
position: absolute;
left: 0;
top: 0;
opacity: 0;
}
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
<div id="app">
<ul class="list-group">
<li class="list-group-item" v-for="(item , index) in items">
remove
<div type="button" class="upload-btn-wrapper">
<button class="btn">{{ item.file ? item.file.name : 'Choose File' }}</button>
<input name="form[]" type="file" #change="handleChange(item, $event)">
</div>
</li>
</ul>
<button #click='addItem'>new item</button>
</div>

How to show an elements value in a vue modal?

I have several elements that are displayed as <li> elements in a loop. For each element I want behavior such that when the element is clicked a modal box opens up. Inside the modal box I want contents that are specific to the element that was clicked.
The data below shows all the elements:
{value: 10, name: "foo"},
{value: 12, name: "bar"},
{value: 14, name: "foobar"},
{value: 22, name: "test"},
{value: 1, name: "testtooo"},
{value: 8, name: "something"}
When I click on an element, I want to see the value property for it inside the modal box.
I've created a fiddle for this: https://jsfiddle.net/hvb9hvog/14/
Question
I've gotten the modal working, however, how can I show each elements value property inside the modal?
I am sure there are multiple ways to go about this, but one way would be to create a new data property, let's call it value. When you #click the li you get it's value, set it to the value property and display that value property in the body of the modal ({{this.value}}).
You can have two #click methods, so create another one that updates the data property you just created, called value.
Here's a fiddle
Relevant code changes:
In your li element:
<li v-for="request in filteredRequests">
{{request.name}}
</li>
In your modal markup:
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
<div slot="body">
{{this.value}}
</div>
</modal>
In vue data:
data: {
requests: [
{value: 10, name: "foo"},
{value: 12, name: "bar"},
{value: 14, name: "foobar"},
{value: 22, name: "test"},
{value: 1, name: "testtooo"},
{value: 8, name: "something"}
],
num: 0,
showModal: false,
value: 9999999999
},
In vue methods:
methods: {
setVal(val) {
this.value = val;
}
},
Vue.component('modal', {
template: '#modal-template'
})
var vm = new Vue({
el: "#app",
data: {
requests: [{
value: 10,
name: "foo"
},
{
value: 12,
name: "bar"
},
{
value: 14,
name: "foobar"
},
{
value: 22,
name: "test"
},
{
value: 1,
name: "testtooo"
},
{
value: 8,
name: "something"
}
],
num: 0,
showModal: false,
value: 9999999999
},
methods: {
setVal(val) {
this.value = val;
}
},
computed: {
c: function() {
return `Slider Number: (${this.num})`
},
filteredRequests() {
return this.requests.filter(r => r.value > this.num)
}
},
});
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
display: table;
transition: opacity .3s ease;
}
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
.modal-container {
width: 300px;
margin: 0px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
transition: all .3s ease;
font-family: Helvetica, Arial, sans-serif;
}
.modal-header h3 {
margin-top: 0;
color: #42b983;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://unpkg.com/vue#2.3.4/dist/vue.js"></script>
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<div id="app">
<div class="form-horizontal">
<div class="form-group">
<label class="col-md-2 control-label">色</label>
<div class="col-md-10">
<input class="form-control" v-model="c" :style="{backgroundColor: c}" />
<div class="help-block">
<input type="range" min="0" max="360" v-model.number="num" />
<ul class="ml-thumbs">
<li v-for="request in filteredRequests">
{{request.name}}
</li>
</ul>
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
<div slot="body">
{{this.value}}
</div>
</modal>
</div>
</div>
</div>
</div>
</div>
Add "req" property to data
data() {
return {
...
req: {},
...
}
}
set click event:
{{request.name}}
add body slot
...
<h3 slot="header">custom header</h3>
<div slot="body">
{{req.value}}
</div>
...
https://jsfiddle.net/w4e6hr86/
I'm not sure if you are asking this using Vue.js or just JS. So, here are my answers (basic examples). I recommend you to read about event delegation + events on vuejs.
Vue Js
<template>
<div class="content">
<ul>
<li v-for="item in items" #click.prevent="showModal(item)">{{ item }}</li>
</ul>
<div class="modal" v-show="isModalVisible">
{{ JSON.stringify(selectedItem) }}
close modal
</div>
</div>
</template>
<script>
export default {
name: 'something',
data () {
return {
selectedItem: item,
items: [{
id: 1,
name: 'something'
}, {
id: 2,
name: 'something #2'
}]
}
},
computed: {
isModalVisible () {
return this.selectedItem !== null
}
}
methods: {
showModal (item) {
this.selectedItem = item
}
}
}
</script>
Plain javascript
const toggleModal = content => {
const $body = document.querySelector('body')
const $modal = $body.querySelector('.modal')
$modal && $modal.remove()
$body.insertAdjacentHTML('beforeend',`<div class="modal">${content}</div>`)
}
document.querySelector('ul').addEventListener('click', e => {
if (! e.target.matches('li')) {
return
}
toggleModal(e.target.innerText)
});
About Event delegation.
About insertAdjacentHtml.
About Vuejs Event handling

How to detect V-if content was changed

i am trying to apply animation width:20% on wrong class when v-if condition becomes false , how can i watch v-if change .
export default {
data() {
return {
ok: true
}
},
methods: {
toggle() {
this.ok = !this.ok;
}
},
watch: {
ok() {
// apply animation after v-if is false
}
}
}
.wrong {
background-color: #fdd;
transition: width 2s;
width: 100%;
}
<template>
<div class="view1">
<div v-if="ok">
<p class="right">OK</p>
</div>
<div v-else>
<p class="wrong">NO</p>
</div>
</div>
<button #click="toggle">Toggle</button>
</template>
You can do this with CSS rather than jQuery. The trick is to have a width transition, and a delay in applying the width style. You listen to a change by using a watch.
new Vue({
el: '.view1',
data: {
ok: true,
delayedReaction: null
},
methods: {
toggle() {
this.ok = !this.ok;
}
},
watch: {
ok() {
if (this.ok) {
this.delayedReaction = null;
} else {
// nextTick didn't suffice
setTimeout(() => {
this.delayedReaction = {
width: '20%'
};
}, 0);
}
}
}
});
.wrong {
background-color: #fdd;
transition: width 2s;
width: 100%;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="view1">
<div v-if="ok">
<p class="right">OK</p>
</div>
<div v-else>
<p class="wrong" :style="delayedReaction">NO</p>
</div>
<button #click="toggle">Toggle</button>
</div>

Categories