I'm trying to create a vue component but whenever I want to hide some elements with v-show it doesn't work.
When you click any element on the list I want to hide it and on the click event element.visible is set to false, so in the component template I bind that value to v-show but it wont hide.
I guess it's because element.visible it's kind of nested attribute but I'm not really sure.
var collection = [
{ id: 1, name: 'element 1' },
{ id: 2, name: 'element 2' },
{ id: 3, name: 'element 3' },
{ id: 4, name: 'element 4' },
{ id: 5, name: 'element 5' },
{ id: 6, name: 'element 6' },
{ id: 7, name: 'element 7' },
{ id: 8, name: 'element 8' },
];
var multiselect = {
props: ['collection'],
data: function() {
return {
subscribed: [],
toSubscribe: [],
toUnsubscribe: [],
dataset: []
}
},
mounted: function(){
this.dataset = _.map(this.collection, function(element){
element.visible = true;
return element;
});
},
methods: {
subscribe: function(element){
element.visible = false;
}
}
}
new Vue({
el: '#app',
components: {
'multiselect': multiselect
},
data: {
elements: collection
}
})
.multiselect .list {
border: 1px solid #000;
height: 215px;
max-height: 215px;
overflow: scroll;
}
.multiselect .list .list-element {
text-align: center;
padding: 0.2em;
cursor: pointer;
}
.multiselect .list .list-element:hover {
background-color: #d6dbdf;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<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://cdn.jsdelivr.net/npm/lodash#4.17.4/lodash.min.js"></script>
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<multiselect inline-template :collection="elements">
<div class="col-sm-12 multiselect">
<div class="col-sm-5 list">
<div class="col-sm-12">
<div v-for="element in dataset" class="list-element" #click="subscribe(element)" v-show="element.visible">
{{element.name}}
</div>
</div>
</div>
<div class="col-sm-2">
<button class="btn btn-primary btn-fill">
<i class="fa fa-arrow-right" aria-hidden="true"></i>
</button>
<button class="btn btn-primary btn-fill">
<i class="fa fa-arrow-left" aria-hidden="true"></i>
</button>
</div>
<div class="col-sm-5 list">
</div>
</div>
</multiselect>
</div>
As an interesting variation, you don't need to clone the collection elements or set a property on them.
It is enough to have a parallel array of flag, but you have to be careful of the syntax to update them and the flag must be contained in an object in order to be observable.
i.e an array of { visible: true } rather than an array of true.
Ref: Mutation-Methods
var collection = [
{ id: 1, name: 'element 1' },
{ id: 2, name: 'element 2' },
{ id: 3, name: 'element 3' },
{ id: 4, name: 'element 4' },
];
var multiselect = {
props: ['collection'],
data: function() {
return {
visibleFlags: []
}
},
created: function(){
this.collection.forEach(x => {
this.visibleFlags.push({visible: true}); // Vue mutation method
})
},
methods: {
subscribe: function(index){
this.$set(this.visibleFlags, index, false)
}
}
}
new Vue({
el: '#app',
components: {
'multiselect': multiselect
},
data: {
elements: collection
}
})
.multiselect .list {
border: 1px solid #000;
height: 125px;
max-height: 215px;
overflow: scroll;
}
.multiselect .list .list-element {
text-align: center;
padding: 0.2em;
cursor: pointer;
}
.multiselect .list .list-element:hover {
background-color: #d6dbdf;
}
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<multiselect inline-template :collection="elements">
<div class="col-sm-12 multiselect">
<div class="col-sm-5 list">
<div class="col-sm-12">
<div v-for="(element, index) in collection"
class="list-element" v-show="visibleFlags[index].visible"
#click="subscribe(index)">
{{element.name}}
</div>
</div>
</div>
</div>
</multiselect>
</div>
The problem is that you are modifying an already-responsive object. Vue cannot detect property additions.
It's obscured by the fact that you're copying via map, and assigning it to a new array, but it's an array of references to responsive objects, to each of which you have added the visible property. If you examine the data items in the parent, you'll see that it gets visible added, too.
The minimal fix is to use Object.assign to create a new object and copy properties into it. This way all properties are inserted into a non-responsive object, which is then made responsive during assignment.
mounted: function(){
this.dataset = _.map(this.collection, function(element){
return Object.assign({}, element, {visible: true});
});
},
You could do this in created, since you don't need the DOM element.
Related
Is there's any way to bind multiple checkboxes from many children to one parent (f.e. via model)?
Let's say that the child component has something like:
<input
type="checkbox"
:id="'ticket-'+id"
:checked="checked"
/>
Now, is it possible to have a parent prop like selected that would collect the id as values in an array of all checked checkboxes from children?
{
selected: [
5,
8
]
}
The nearest thing to my solution in mind is this. But in my case, I don't want to keep track of unchecked instances.
You can listen to the checkbox change events on the parent and call a method which updates the data model.
Check out the fiddle below demonstrating this. (Go full page to see it properly)
new Vue({
el: "#app",
data: {
list: [
{ id: 1, label: 'Check 1'},
{ id: 2, label: 'Check 2'},
{ id: 3, label: 'Check 3'},
{ id: 4, label: 'Check 4'},
],
selected: []
},
methods: {
handleChange: function(ev) {
let id = ev.target.id;
if (ev.target.checked) {
if (!this.selected.includes(id)) {
this.selected.push(id);
}
} else {
this.selected = this.selected.filter(function (item) {
return item !== id;
});
}
console.log('Updated: ', this.selected);
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<ol #change="handleChange">
<li v-for="check in list">
<label>
<input type="checkbox" :id="check.id" />
<span>{{check.label}}</span>
</label>
</li>
</ol>
</div>
I have 2 Vuetify data tables without pagination. Every row of the second one got exactly one parent in the first one. If I click on one of those entries in the second one, I want to search for the parent and jump to that row. All I found for now is filtering, but I only want to have that row on top of my table.
Is something like this possible?
We can't really help you without code, even if i've seen that you can't, it should be cool if you could modify some parts of your code like variables and datas ...
However, i'll try to do my best to explain
What you have to do is to reorder your data array binded on your table depending on a given id (or other data) to identify it.
I made a similar exemple of your needs, but i repeat i can't really be exhausitve :
Parent component:
<template>
<div id="app">
<listone :list="listOne" :toggled="toggledParent"></listone>
<listtwo :list="listTwo" v-model="toggledParent"></listtwo>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
toggledParent: 0,
listOne: [
{
id: 1,
title: "parent1",
},
{
id: 2,
title: "parent2",
},
{
id: 3,
title: "parent3",
},
],
listTwo: [
{
title: "title1",
parent: 3,
},
{
title: "title2",
parent: 1,
},
{
title: "title3",
parent: 2,
},
],
}),
components: {
listone: () => import("#/components/listone"),
listtwo: () => import("#/components/listtwo"),
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
}
</style>
ListOne component :
<template>
<div class="list">
<a v-for="item in treatedList" :key="item.title">
{{ item.title }}
</a>
</div>
</template>
<script>
export default {
name: "listone",
props: {
list: Array,
toggled: Number,
},
computed: {
treatedList: function () {
let tmp = this.list;
let $this = this
return tmp.sort(function (a, b) {
return a.id === $this.toggled ? -1 : b.id === $this.toggled ? 1 : 0;
});
},
},
};
</script>
<style>
.list {
display: flex;
flex-flow: column nowrap;
}
.list > a {
border: 1px solid red;
}
</style>
ListTwo component :
<template>
<div class="list">
<a
v-for="item in list"
:key="item.title"
#click="$emit('input', item.parent)">
{{ item.title }}
</a>
</div>
</template>
<script>
export default {
name: "listtwo",
props: {
list: Array,
toggledParent: Number
},
};
</script>
<style scoped>
.list {
display: flex;
flex-flow: column nowrap;
}
.list > a {
border: 1px solid red;
}
</style>
Try it and say me it helps you as wanted
Demo on codesandbox.io : https://codesandbox.io/s/mutable-thunder-zo8vn?fontsize=14&hidenavigation=1&theme=dark
So without knowing what your data looks like i'll use a standard array of objects without meta data.
What you could do, is use the sort function inside of computed property like below, this will re-organize your list every time you match or filter the list.
(Note: This re-arranges your entire list every time)
Here is a really basic example:
new Vue({
el: "#app",
data: {
search: 'Bananas',
tableData: [
{
item: "Cherries",
price: 3.99,
type: "Fruit"
},
{
item: "Chicken",
price: 6.99,
type: "Meat"
},
{
item: "Bananas",
price: 1.99,
type: "Fruit"
},
{
item: "Cola",
price: 0.99,
type: "Drink"
},
{
item: "Coffee",
price: 2.99,
type: "Drink"
},
]
},
computed: {
getTableData: function() {
return this.tableData.sort((x,y) => { return x.item == this.search ? -1 : y.item == this.search ? 1 : 0; });
}
}
})
table {
background: #ccc;
padding: 20px;
width:50%;
}
table tr {
background: #f1f1f1;
}
table tr td {
padding: 10px;
}
.firstRow {
background: green;
color:white;
}
.searchBar {
padding:20px;
width: 40%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="searchBar">
Search <input type="text" v-model="search">
</div>
<table>
<tr class="firstRow"><td>Item</td> <td>Price</td> <td>Type</td></tr>
<tr v-for="(product, index) in getTableData" :key="index">
<td>{{product.item}}</td>
<td>{{product.type}}</td>
<td>{{product.price}}</td>
</tr>
</table>
</div>
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>
I am new to Vue and I am trying to display a list of cards. The cards will be broken out into rows of three. That works, but I want to give each row a different class name based on a list of classes in an array but can't seem to figure out how to do that with what I have right now.
I tried using v-bind:class on the row but not sure if that is the way to go with what I am trying to do.
Here is what my HTML structure looks like:
<div class="row" v-for="i in row”>
<div v-for="(show, index) in rowItems(i)" class="card" v-bind:class="{ new: item.new }">
<img v-bind:src="item.illustration">
<p>{{ item.name }}</p>
</div>
</div>
Here is what I have in Vue. My data is in an object (itemList).
let app = new Vue({
el: '#container',
data: {
rowItems: 3,
items: itemList,
rowClasses: ['row1', 'row2', 'row3', 'row4']
},
computed:{
row:function(){
return Math.ceil(this.items.length / this.rowItems);
},
},
methods:{
rowItems:function(index){
return this.items.slice((index - 1) * this.rowItems, index * this.rowItems)
}
}
});
You can v-bind the class using object syntax like this:
<div :class="{ new: item.new, [rowClasses[index]]: true }">
new Vue({
el: '#app',
data() {
return {
rowCount: 3,
items: [
{ name: 'A', new: false },
{ name: 'B', new: false },
{ name: 'C', new: true },
{ name: 'D', new: false },
],
rowClasses: ['row1', 'row2', 'row3', 'row4']
};
},
computed: {
row() {
return Math.ceil(this.items.length / this.rowCount);
},
},
methods: {
rowItems(index) {
return this.items.slice((index - 1) * this.rowCount, index * this.rowCount);
},
}
})
.card {
border: solid 1px gray;
margin: 10px;
padding: 10px;
}
.new {
background-color: lightyellow;
}
.row1 {
color: red;
}
.row2 {
color: green;
}
.row3 {
color: blue;
}
<script src="https://unpkg.com/vue#2.5.16"></script>
<div id="app">
<div class="row" v-for="i in row">
<div v-for="(item, index) in rowItems(i)"
class="card"
:class="{ new: item.new, [rowClasses[index]]: true }">
<pre>{ new: {{item.new}}, [{{rowClasses[index]}}]: true }</pre>
<p>{{ item.name }}</p>
</div>
</div>
</div>
Or you can call a method that returns such an object:
// <template>
<div :class="getRowClass(item, index)">
// <script>
methods: {
getRowClass(item, index) {
return {
new: item.new,
[this.rowClasses[index]]: true
};
}
}
new Vue({
el: '#app',
data() {
return {
rowCount: 3,
items: [
{ name: 'A', new: false },
{ name: 'B', new: false },
{ name: 'C', new: true },
{ name: 'D', new: false },
],
rowClasses: ['row1', 'row2', 'row3', 'row4']
};
},
computed: {
row() {
return Math.ceil(this.items.length / this.rowCount);
},
},
methods: {
rowItems(index) {
return this.items.slice((index - 1) * this.rowCount, index * this.rowCount);
},
getRowClass(item, index) {
const rowClass = this.rowClasses[index % this.rowClasses.length];
return {
new: item.new,
[rowClass]: true
};
}
}
})
.card {
border: solid 1px gray;
margin: 10px;
padding: 10px;
}
.new {
background-color: lightyellow;
}
.row1 {
color: red;
}
.row2 {
color: green;
}
.row3 {
color: blue;
}
<script src="https://unpkg.com/vue#2.5.16"></script>
<div id="app">
<div class="row" v-for="i in row">
<div v-for="(item, index) in rowItems(i)"
class="card"
:class="getRowClass(item, index)">
<pre>{{getRowClass(item, index)}}</pre>
<p>{{ item.name }}</p>
</div>
</div>
</div>
Or you can do this entirely in CSS, using nth-of-type() and eliminating the need for rowClasses[].
// <style>
.card:nth-of-type(1n) {} // every 1st card
.card:nth-of-type(2n) {} // every 2nd card
.card:nth-of-type(3n) {} // every 3rd card
new Vue({
el: '#app',
data() {
return {
rowCount: 3,
items: [
{ name: 'A', new: false },
{ name: 'B', new: false },
{ name: 'C', new: true },
{ name: 'D', new: false },
],
};
},
computed: {
row() {
return Math.ceil(this.items.length / this.rowCount);
}
},
methods: {
rowItems(index) {
return this.items.slice((index - 1) * this.rowCount, index * this.rowCount);
}
}
})
.card {
border: solid 1px gray;
margin: 10px;
padding: 10px;
}
.new {
background-color: lightyellow;
}
.card:nth-of-type(1n) {
color: red;
}
.card:nth-of-type(2n) {
color: green;
}
.card:nth-of-type(3n) {
color: blue;
}
<script src="https://unpkg.com/vue#2.5.16"></script>
<div id="app">
<div class="row" v-for="i in row">
<div v-for="(item, index) in rowItems(i)"
class="card"
:class="{ new: item.new }">
<pre>.card:nth-of-type({{ index+1 }}n)</pre>
<p>{{ item.name }}</p>
</div>
</div>
</div>
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