I am writing a simple tooltip component. The contents of the tooltip are defined with a vue slot. I want the contents of the slot to be able to signal a close of the tooltip, the same way the tooltip can close itself from its own template.
Here’s example code:
JavaScript
x
37
37
1
Vue.component('vtip', {
2
created: function() {
3
this.$on('closeDialog', function(a,b,c) {
4
console.log('[closeDialog in vtip]', a,b,c,this);
5
this.closeDialog(); // idfk
6
});
7
},
8
props: ['button_text'],
9
template: '#vtip_template',
10
methods: {
11
closeDialog: function(event) {
12
var $dialog = $(this.$refs.dialog);
13
$dialog.fadeToggle('fast');
14
},
15
toggleDialog: function(event) {
16
var $button = $(this.$refs.button);
17
var $dialog = $(this.$refs.dialog);
18
19
var button_left = $button.position().left;
20
var button_top = $button.position().top;
21
22
var extras = 2;
23
var arrow_size = 10;
24
var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};
25
26
$dialog.css(new_pos);
27
$dialog.fadeToggle('fast');
28
},
29
}
30
});
31
32
var v_app;
33
$(document).ready(function () {
34
v_app = new Vue({
35
el: '#v_app',
36
});
37
});
JavaScript
1
27
27
1
body {
2
background:#eee;
3
padding:1em;
4
font-family:Verdana;
5
font-size:11pt;
6
}
7
8
.dialog {
9
background:#222;
10
color:#eee;
11
border:2px solid white;
12
display:none;
13
14
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
15
border-radius:4px;
16
17
position:absolute;
18
}
19
20
.dialog .item-body .item-body {
21
padding:1em 1em;
22
}
23
24
.dialog .item-body {
25
min-width:10em;
26
padding:0.5em 1em;
27
}
JavaScript
1
28
28
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
2
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
3
4
<script id="vtip_template" type="x-template">
5
<span style="position:relative;">
6
<button @click="toggleDialog" v-text="button_text" ref="button"></button>
7
<div ref="dialog" class="dialog">
8
<div class='item-body'>
9
<a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
10
<!-- i hate slots -->
11
<slot></slot>
12
</div>
13
</div>
14
</span>
15
</script>
16
17
<DIV id="v_app">
18
19
1. Click Hey.<br>
20
2. How do I make the "Yes!" button close the component like the "x" link does?<br>
21
22
<vtip button_text="hey">
23
<!-- how do i make it so this slot child can close the parent component -->
24
Are you sure?
25
<button>Yes!</button> <!-- @click="closeDialog" does not work (and should not) -->
26
</vtip>
27
28
</DIV>
Advertisement
Answer
You could add a ref
attribute to the vtip
component tag, and then reference the component’s closeDialog
method via the $refs
object:
JavaScript
1
5
1
<vtip button_text="hey" ref="tip">
2
Are you sure?
3
<button @click="$refs.tip.closeDialog()">Yes!</button>
4
</vtip>
5
JavaScript
1
37
37
1
Vue.component('vtip', {
2
created: function() {
3
this.$on('closeDialog', function(a,b,c) {
4
console.log('[closeDialog in vtip]', a,b,c,this);
5
this.closeDialog(); // idfk
6
});
7
},
8
props: ['button_text'],
9
template: '#vtip_template',
10
methods: {
11
closeDialog: function(event) {
12
var $dialog = $(this.$refs.dialog);
13
$dialog.fadeToggle('fast');
14
},
15
toggleDialog: function(event) {
16
var $button = $(this.$refs.button);
17
var $dialog = $(this.$refs.dialog);
18
19
var button_left = $button.position().left;
20
var button_top = $button.position().top;
21
22
var extras = 2;
23
var arrow_size = 10;
24
var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};
25
26
$dialog.css(new_pos);
27
$dialog.fadeToggle('fast');
28
},
29
}
30
});
31
32
var v_app;
33
$(document).ready(function () {
34
v_app = new Vue({
35
el: '#v_app',
36
});
37
});
JavaScript
1
27
27
1
body {
2
background:#eee;
3
padding:1em;
4
font-family:Verdana;
5
font-size:11pt;
6
}
7
8
.dialog {
9
background:#222;
10
color:#eee;
11
border:2px solid white;
12
display:none;
13
14
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
15
border-radius:4px;
16
17
position:absolute;
18
}
19
20
.dialog .item-body .item-body {
21
padding:1em 1em;
22
}
23
24
.dialog .item-body {
25
min-width:10em;
26
padding:0.5em 1em;
27
}
JavaScript
1
28
28
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
2
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
3
4
<script id="vtip_template" type="x-template">
5
<span style="position:relative;">
6
<button @click="toggleDialog" v-text="button_text" ref="button"></button>
7
<div ref="dialog" class="dialog">
8
<div class='item-body'>
9
<a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
10
<!-- i hate slots -->
11
<slot></slot>
12
</div>
13
</div>
14
</span>
15
</script>
16
17
<DIV id="v_app">
18
19
1. Click Hey.<br>
20
2. How do I make the "Yes!" button close the component like the "x" link does?<br>
21
22
<vtip button_text="hey" ref="tip">
23
<!-- how do i make it so this slot child can close the parent component -->
24
Are you sure?
25
<button @click="$refs.tip.closeDialog()">Yes!</button> <!-- @click="closeDialog" does not work (and should not) -->
26
</vtip>
27
28
</DIV>
Alternatively, you could also use a scoped slot, passing the closeDialog
method as a click handler property in the props
.
In the slot definition, bind a clickHandler
property:
JavaScript
1
2
1
<slot :clickHandler="closeDialog"></slot>
2
Then, in your parent component, you can access the clickHandler
via the scoped props
like so:
JavaScript
1
7
1
<vtip button_text="hey">
2
<template scope="props">
3
Are you sure?
4
<button @click="props.clickHandler">Yes!</button>
5
</template>
6
</vtip>
7
JavaScript
1
37
37
1
Vue.component('vtip', {
2
created: function() {
3
this.$on('closeDialog', function(a,b,c) {
4
console.log('[closeDialog in vtip]', a,b,c,this);
5
this.closeDialog(); // idfk
6
});
7
},
8
props: ['button_text'],
9
template: '#vtip_template',
10
methods: {
11
closeDialog: function(event) {
12
var $dialog = $(this.$refs.dialog);
13
$dialog.fadeToggle('fast');
14
},
15
toggleDialog: function(event) {
16
var $button = $(this.$refs.button);
17
var $dialog = $(this.$refs.dialog);
18
19
var button_left = $button.position().left;
20
var button_top = $button.position().top;
21
22
var extras = 2;
23
var arrow_size = 10;
24
var new_pos = {left:button_left+'px',top:button_top+$button.height()+arrow_size+extras+'px'};
25
26
$dialog.css(new_pos);
27
$dialog.fadeToggle('fast');
28
},
29
}
30
});
31
32
var v_app;
33
$(document).ready(function () {
34
v_app = new Vue({
35
el: '#v_app',
36
});
37
});
JavaScript
1
27
27
1
body {
2
background:#eee;
3
padding:1em;
4
font-family:Verdana;
5
font-size:11pt;
6
}
7
8
.dialog {
9
background:#222;
10
color:#eee;
11
border:2px solid white;
12
display:none;
13
14
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
15
border-radius:4px;
16
17
position:absolute;
18
}
19
20
.dialog .item-body .item-body {
21
padding:1em 1em;
22
}
23
24
.dialog .item-body {
25
min-width:10em;
26
padding:0.5em 1em;
27
}
JavaScript
1
29
29
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
2
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
3
4
<script id="vtip_template" type="x-template">
5
<span style="position:relative;">
6
<button @click="toggleDialog" v-text="button_text" ref="button"></button>
7
<div ref="dialog" class="dialog">
8
<div class='item-body'>
9
<a @click="closeDialog" style="float:right;color:white;padding:1em;" href="#">x</a>
10
<!-- i hate slots -->
11
<slot :clickHandler="closeDialog"></slot>
12
</div>
13
</div>
14
</span>
15
</script>
16
17
<DIV id="v_app">
18
19
1. Click Hey.<br>
20
2. How do I make the "Yes!" button close the component like the "x" link does?<br>
21
22
<vtip button_text="hey">
23
<template scope="props">
24
Are you sure?
25
<button @click="props.clickHandler">Yes!</button>
26
</template>
27
</vtip>
28
29
</DIV>