Transition multiple elements at the same time on Vue component render - javascript

Simple case shown in the following jsfiddle:
https://jsfiddle.net/hsak2rdu/
I want to swap and animate two elements, but fail. As you can see after clicking the toggle button in the playground, the second element flashes into the final position.
I want them both animated at the same time, like crossing each other. Is it possible?
Template:
<div id="app">
<div class="dom" v-for="(_d, _i) in list" :key="_d.id" :style="{ top: _i * 50 + 'px'}">
<span>{{_d.text}}{{_i}}</span>
</div>
<button #click="handle">Toggle</button>
{{list}}
</div>
JS:
new Vue({
el: '#app',
data: {
show: true,
list: [
{
id:1,
text:'First'
},
{
id:2,
text:'Second'
}
]
},
methods:{
handle: function (){
console.log("DEBUG", this.list)
let a = JSON.parse(JSON.stringify(this.list[0]));
let b = JSON.parse(JSON.stringify(this.list[1]))
this.$set(this.list, 0, b);
this.$set(this.list, 1, a);
}
}
});

The only necessary change is to wrap the v-for in a <transition-group>:
<transition-group tag="div" name="list">
<div class="dom" v-for="(_d, _i) in list" :key="_d.id" :style="{ top: _i * 20 + 'px' }">
<span>{{_d.text}}{{_i}}</span>
</div>
</transition-group>
From the docs:
This might seem like magic, but under the hood, Vue is using an animation technique called FLIP to smoothly transition elements from their old position to their new position using transforms
Here's a demo:
new Vue({
el: '#app',
data: () => ({
show: true,
list: [
{
id:1,
text:'First'
},
{
id:2,
text:'Second'
}
]
}),
methods:{
handle: function (){
console.log("DEBUG", this.list)
let a = JSON.parse(JSON.stringify(this.list[0]));
let b = JSON.parse(JSON.stringify(this.list[1]))
this.$set(this.list, 0, b);
this.$set(this.list, 1, a);
}
}
});
.dom{
position: absolute;
transition: all 1s linear;
opacity: 1;
}
button{
margin-top: 50px;
}
#app{
margin-top: 50px;
position: relative;
}
<div id="app">
<transition-group tag="div" name="list">
<div class="dom" v-for="(_d, _i) in list" :key="_d.id" :style="{ top: _i * 20 + 'px' }">
<span>{{_d.text}}{{_i}}</span>
</div>
</transition-group>
<button #click="handle">Toggle</button>
{{list}}
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>

Related

When to use transition vs transition-group

I'm trying to add transitions to my form, so that when a user selects a specific answer to a question, it will dynamically show the next question. Currently I have it looking something like this:
<input type="text" :value="vueModel.question1" />
<div v-if="vueModel.question1 === 'hello'">
//Do something
</div>
<div v-else-if="vueModel.question1 === 'hihi'">
//Do something
</div>
<div v-else>
//Do something
</div>
My question is, should I be adding transitions this way? (and why?)
<input type="text" :value="vueModel.question1" />
<transition-group name="slide-fade" mode="in-out">
<div v-if="vueModel.question1 === 'hello'" key="key1">
//Do something
</div>
<div v-else-if="vueModel.question1 === 'hihi'" key="key2">
//Do something
</div>
<div v-else key="key3">
//Do something
</div>
</transition-group>
or, this way? (and why?)
<input type="text" :value="vueModel.question1" />
<transition name="slide-fade" mode="in-out">
<div v-if="vueModel.question1 === 'hello'" key="key1">
//Do something
</div>
<div v-else-if="vueModel.question1 === 'hihi'" key="key2">
//Do something
</div>
<div v-else key="key3">
//Do something
</div>
</transition>
Or, is there another way I could do this better and which fits with Vue best practices?
So when you we have a list of items and you want to render and filter simultaneously, for example with v-for? In this case, you may use transition-group component. Unlike transition, this will render an actual element: like div in the next sniped. However, you can change the element that is rendered with the tag attribute.
NOTE
Elements inside are always required to have a unique key attribute.
Update
If you just have a question and then you want to "do something" use only transition as in this sample.
The main difference between transition and transition-group is that transition will affect one component. This means if you have a component an you want to replaced with another component you can use transition.
new Vue({
el: '#vue-transition',
data: {
show: false,
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://cdn.jsdelivr.net/npm/animate.css#3.5.1" rel="stylesheet" type="text/css">
<div id="vue-transition">
<button #click="show = !show"> Simple Transition </button>
<transition
name="custom-classes-transition"
enter-active-class="animated tada"
leave-active-class="animated bounceOutRight"
>
<p v-if="show">Teocci</p>
</transition>
</div>
On the other hand, transition-group renders an actual element from a list of elements, for that reason elements inside are always required to have a unique key attribute. For example, if you have 9 questions but you want to render a transition of each element moving randomly to another position in the SAME group.
new Vue({
el: '#list-complete-demo',
data: {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
},
methods: {
shuffle: function() {
this.items = _.shuffle(this.items)
}
}
})
.list-complete-item {
transition: all 1s;
display: inline-block;
margin-right: 10px;
}
.list-complete-enter,
.list-complete-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-complete-leave-active {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<div id="list-complete-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="list-complete" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-complete-item">
{{ item }}
</span>
</transition-group>
</div>
For that reason, if you just want to "do something" use transition as shown in this snippet to achieve what you want.
let app = new Vue({
el: '#vue-selector',
data: {
questions: [{
id: 0,
description: 'Question 01',
answer: 'hihi'
},
{
id: 1,
description: 'Question 02',
answer: 'lala'
},
{
id: 2,
description: 'Question 03',
answer: 'hello'
},
{
id: 3,
description: 'Question 04',
answer: 'none'
},
{
id: 4,
description: 'Question 05',
answer: 'teo'
},
],
answer: {
question: -1,
text: '',
},
answerText: '',
selected: '',
},
computed: {
computedQuestions: function() {
let vm = this;
return this.questions.filter(function(item, index) {
return index !== vm.answers;
})
}
},
methods: {
answerQuestion: function(index) {
this.answer.question = index;
this.answer.text = this.answerText;
},
beforeEnter: function(el) {
el.style.opacity = 0
el.style.height = 0
},
enter: function(el, done) {
var delay = el.dataset.index * 150
setTimeout(function() {
Velocity(
el, {
opacity: 1,
height: '1.6em'
}, {
complete: done
}
)
}, delay)
},
leave: function(el, done) {
var delay = el.dataset.index * 150
setTimeout(function() {
Velocity(
el, {
opacity: 0,
height: 0
}, {
complete: done
}
)
}, delay)
},
},
});
div.selector {
display: block;
padding-top: 25px;
padding-bottom: 125px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/2.0.3/velocity.min.js"></script>
<div id="vue-selector">
<div>Question 01</div>
<input v-model="answerText" placeholder="answer me">
<button #click="answerQuestion(0)"> Answer </button>
<transition name="slide-fade" mode="in-out">
<div v-if="answer.text === 'hello'">
Do something A
</div>
<div v-else-if="answer.text === 'hihi'">
Do something B
</div>
<div v-else>
Waiting for answer
</div>
</transition>
</div>

How to use transitions on select option using Vue.js

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

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>

Vue.js: Collapse/expand all elements from parent

I need to add "expand/collapse all" functionality for my Vue component(some collapsible panel).
If user clicks collapse button then clicks on some panel and expand it then clicking on collapse button will do nothing because watched parameter will not change.
So how to implement this functionality properly (buttons must collapse and expand components always)?
I prepared simple example(sorry for bad formatting, it looks nice in editor :( ):
var collapsible = {
template: "#collapsible",
props: ["collapseAll"],
data: function () {
return {
collapsed: true
}
},
watch: {
collapseAll: function(value) {
this.collapsed = value
}
}
}
var app = new Vue({
template: "#app",
el: "#foo",
data: {
collapseAll: true
},
components: {
collapsible: collapsible
}
});
.wrapper {
width: 100%;
}
.wrapper + .wrapper {
margin-top: 10px;
}
.header {
height: 20px;
width: 100%;
background: #ccc;
}
.collapsible {
height: 100px;
width: 100%;
background: #aaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="foo"></div>
<script type="text/x-template" id="collapsible">
<div class="wrapper">
<div class="header" v-on:click="collapsed = !collapsed"></div>
<div class="collapsible" v-show="!collapsed"></div>
</div>
</script>
<script type="text/x-template" id="app">
<div>
<button v-on:click="collapseAll = true">Collapse All</button>
<button v-on:click="collapseAll = false">Expand All</button>
<collapsible v-for="a in 10" v-bind:collapseAll="collapseAll" v-bind:key="a"></collapsible>
</div>
</script>
Thanks!
This is a case where I might use a ref.
<button v-on:click="collapseAll">Collapse All</button>
<button v-on:click="expandAll">Expand All</button>
<collapsible ref="collapsible" v-for="a in 10" v-bind:key="a"></collapsible>
And add methods to your Vue.
var app = new Vue({
template: "#app",
el: "#foo",
methods:{
collapseAll(){
this.$refs.collapsible.map(c => c.collapsed = true)
},
expandAll(){
this.$refs.collapsible.map(c => c.collapsed = false)
}
},
components: {
collapsible: collapsible
}
});
Example.

Categories