Reputation: 13162
I have two component
My first component(parent component) like this :
<template>
<ul class="list-group">
<li v-for="item in invoices" class="list-group-item">
<div class="row">
...
<div class="col-md-7">
...
<a href="javascript:"
class="toggle-show"
aria-expanded="false"
data-toggle="collapse"
:data-target="'#' + item.id"
@click="show(item.id)">
Show <span class="caret"></span>
</a>
</div>
</div>
<div class="collapse" :id="item.id">
<order-collapse/>
</div>
</li>
</ul>
</template>
<script>
import orderCollapse from './orderCollapse.vue'
export default {
...
components: {orderCollapse},
data() {
return {
invoices: [
{
id: 1,
...
},
{
id: 2,
...
},
{
id: 3,
...
}
]
}
},
methods: {
show(id) {
// orderCollapse executed if this method run
}
},
}
</script>
My second component (child component) like this :
<template>
<table class="table table-bordered table-collapse">
<!-- this is used to display detail by id -->
</table>
</template>
<script>
export default {
name: 'order-collapse',
...
}
</script>
If the parent component executed, the child component automatically executed too
I want if the parent component executed, the child component not executed
I want the child component executed if user click show link
How can I do it?
Upvotes: 0
Views: 66
Reputation: 135752
How about creating a property (displayedIds
) to hold the display status of each order-collapse
and then condition their display using v-if
:
<div ... v-if="displayedIds[item.id]">
<order-collapse :id="item.id"></order-collapse>
</div>
And, considering displayedIds
is initially declared as {}
, you would have the show()
method as:
methods: {
show(id) {
// orderCollapse executed if this method run
this.$set(this.displayedIds, id, true); // use $set to be reactive
}
},
Demo:
Vue.component('order-collapse', {
template: "#oc",
name: 'order-collapse',
props: ['id'],
mounted() {
console.log('order-collapsed mounted for id', this.id);
}
});
new Vue({
el: '#app',
data() {
return {
displayedIds: {},
invoices: [{id: 1},{id: 2},{id: 3}]
}
},
methods: {
show(id) {
// orderCollapse executed if this method run
this.$set(this.displayedIds, id, true);
}
},
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app">
<ul class="list-group">
<li v-for="item in invoices" class="list-group-item">
<div class="row">
...
<div class="col-md-7">
...
<a href="javascript:"
class="toggle-show"
aria-expanded="false"
data-toggle="collapse"
:data-target="'#' + item.id"
@click="show(item.id)">
Show <span class="caret"></span>
</a>
</div>
</div>
<div class="collapse" :id="item.id" v-if="displayedIds[item.id]">
<order-collapse :id="item.id"></order-collapse>
</div>
</li>
</ul>
</div>
<template id="oc">
<table class="table table-bordered table-collapse">
<!-- this is used to display detail by id -->
<tr>
<td>ID: {{ id }}</td>
</tr>
</table>
</template>
Another possibility is having displayedIds
as an array instead of an object. This way you could not use $set
, just a regular .push()
:
methods: {
show(id) {
// orderCollapse executed if this method run
this.displayedIds.push(id);
}
},
And condition the display using .includes()
:
<div ... v-if="displayedIds.includes(item.id)">
<order-collapse :id="item.id"></order-collapse>
</div>
Demo:
Vue.component('order-collapse', {
template: "#oc",
name: 'order-collapse',
props: ['id'],
mounted() {
console.log('order-collapsed mounted for id', this.id);
}
});
new Vue({
el: '#app',
data() {
return {
displayedIds: [],
invoices: [{id: 1},{id: 2},{id: 3}]
}
},
methods: {
show(id) {
// orderCollapse executed if this method run
this.displayedIds.push(id);
}
},
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app">
<ul class="list-group">
<li v-for="item in invoices" class="list-group-item">
<div class="row">
...
<div class="col-md-7">
...
<a href="javascript:"
class="toggle-show"
aria-expanded="false"
data-toggle="collapse"
:data-target="'#' + item.id"
@click="show(item.id)">
Show <span class="caret"></span>
</a>
</div>
</div>
<div class="collapse" :id="item.id" v-if="displayedIds.includes(item.id)">
<order-collapse :id="item.id"></order-collapse>
</div>
</li>
</ul>
</div>
<template id="oc">
<table class="table table-bordered table-collapse">
<!-- this is used to display detail by id -->
<tr>
<td>ID: {{ id }}</td>
</tr>
</table>
</template>
Finally, if you find easier to reason, the display
flag could be in each item
itself, as a property.
In this case, the show
should receive the whole item
(not just item.id
):
<a ... @click="show(item)">
The display would use just item.displayed
:
<div ... v-if="item.displayed">
<order-collapse :id="item.id"></order-collapse>
</div>
And the method:
methods: {
show(item) {
// orderCollapse executed if this method run
this.$set(item, 'displayed', true);
}
},
Demo:
Vue.component('order-collapse', {
template: "#oc",
name: 'order-collapse',
props: ['id'],
mounted() {
console.log('order-collapsed mounted for id', this.id);
}
});
new Vue({
el: '#app',
data() {
return {
invoices: [{id: 1},{id: 2},{id: 3}]
}
},
methods: {
show(item) {
// orderCollapse executed if this method run
this.$set(item, 'displayed', true);
// you could use `item.displayed = true` if you declared `displayed: false` in each item at data
}
},
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app">
<ul class="list-group">
<li v-for="item in invoices" class="list-group-item">
<div class="row">
...
<div class="col-md-7">
...
<a href="javascript:"
class="toggle-show"
aria-expanded="false"
data-toggle="collapse"
:data-target="'#' + item.id"
@click="show(item)">
Show <span class="caret"></span>
</a>
</div>
</div>
<div class="collapse" :id="item.id" v-if="item.displayed">
<order-collapse :id="item.id"></order-collapse>
</div>
</li>
</ul>
</div>
<template id="oc">
<table class="table table-bordered table-collapse">
<!-- this is used to display detail by id -->
<tr>
<td>ID: {{ id }}</td>
</tr>
</table>
</template>
Which approach is the recommended? It boils down to taste. If you can/don't mind appending the displayed
prop to each item, I think it is the simplest solution. Otherwise I'd go with the first (displayedIds
as an object), unless it gives you the yikes, in which case I'd pick the array solution.
Upvotes: 1