I'm aware this has been discussed previously -
Neither of which seem to lead to a solution that allows me to not tightly couple two not-necessarily related components.
I'm trying to use a datepicker component inside a datatable component as a filter, and there can be a dynamic number of datepicker components depending on what data we're piping into the datatable. I'm also integrating this into an existing (large) codebase so I'm trying to keep all the state local to the components (no Vuex or global Vue data at all).
Ex.
<datatable api-endoint="/api/v1/orders.json">
<datepicker filter-on="signupDate" title="Signed Up"></datepicker>
<datepicker filter-on="lastActiveDate" title="Last Active"></datepicker>
// etc
</datatable>
I'd love for this datepicker to be a generic date picker that can emit a "data-selected" event with the date the user selected and then wherever it's used in the application the parent can simply listen for the event and take action (in this case re-querying the data with a date filter). Right now I have to tightly couple the datepicker to the datatable component to fire off the datatable's filtering.
I'm just not sure I've seen a coherent argument for _not_ allowing the parent to listen for the events.
What I've seen as a response seems to be singularly that slots won't necessarily be a single element, but why's that an issue?
It can end up rendering anything: text, plain element, multiple nodes... the behavior will be unpredictable.
and
As explained in #4332, it doesn't make sense to add listeners on
because doesn't always render only a single element.
It should be no different than in Javascript with addEventListener that can be triggered from any number of child nodes.
Allow a component with a
Thanks for your interesting. However, nothing changes regarding existing arguments. An event is always bound to a single element/component.
To me, it looks like what you need is to pass a function in a scoped slot and let the parent use that function, but your example is unclear. That could be done like this:
<datatable>
<template slot-scope="{ doSomething }">
<datepicker @event="doSomething" title="a"/>
<datepicker @event="doSomething" title="b"/>
</template>
</datatable>
Could you clarify what is exactly problematic, or what are you trying to write in your template?
@posva Here's a bare bones Git repo of what I'm trying to do, with your suggestion of using slot-scope which I still can't get to work - what am I doing wrong here?
What I'm hoping to achieve is -
Edit - (Also I apologize, when creating this example I renamed "Datepicker" to "DateFilter", but the idea is the same)
https://github.com/theianjohnson/vue-datatable-example
/src/App.vue
<template>
<div id="app">
<Datatable>
<template slot-scope="{ queryData }">
<DateFilter v-on:dates-selected="queryData()"></DateFilter>
</template>
</Datatable>
</div>
</template>
<script>
import Datatable from './components/Datatable.vue'
import DateFilter from './components/DateFilter.vue'
export default {
name: 'app',
components: {
Datatable,
DateFilter,
}
}
</script>
/src/components/Datatable.vue
<template>
<div>
<slot></slot>
<table border="1">
<tr>
<td>Table cell 1</td>
<td>Table cell 2</td>
<td>Table cell 3</td>
</tr>
</table>
</div>
</template>
<script>
export default {
data() {
return {}
},
methods: {
queryData() {
// eslint-disable-next-line
console.log('Querying data from server (calling api endpoint, etc)');
}
}
}
</script>
/src/components/DateFilter.vue
<template>
<div>
<a @click.prevent="$emit('dates-selected', {start: '2018-07-15', end: '2018-08-15'})" href="#">Select 2018-07-15 to 2018-08-15</a>
</div>
</template>
<script>
export default {
data() {
return {}
},
}
</script>
<style scoped>
</style>
So yeah, this is solved using the function. I cannot check a repository right now but I could help you on a codesandbox 🙂
@posva I'm not sure I understand how this is solved when I tried what you recommended and couldn't get it to work - I even said that in my response.
Can we reopen this until we get a working solution?
You are asking a question, that's why I closed it, it should be asked in the forums or discord server. I, however, did offer my help if you provide a codesandbox....
Is there any rationale for allowing Vue components to be composed _almost_ like HTML elements, but yet not be able to listen to events in the same way? That was the crux of my request - that if we can create HTML "elements" (Vue components) and nest them in the same manner as HTML - to also allow events to work in the same way (as Javascript's addEventListener).
Over these three issues I still don't understand why this has anything to do with single or multiple components in a slot.
In HTML we can listen to events from any number of child elements, ie. this works -
<div>
<a href="#">Link 1</a>
<a href="#">Link 2</a>
</div>
<script>
document.querySelector('div').addEventListener('click', function(e) {
e.preventDefault();
console.log('Clicked');
});
</script>
Yet in Vue we can _compose_ the elements in the same way, but I can't listen to an event emitted in the same way, ie. <datatable> can't listen to an event emitted by a slot'd <dateFilter>
<datatable>
<dateFilter></dateFilter>
</datatable>
The relationship is still clearly there since some of the workarounds called this.$parent.$emit('some-event-name') (then listening on the parent, but that still tightly couples the two) so what's stopping us from bubbling an event up through the slot to the parent and keeping the mental model of events in line with HTML and Javascript?
As for the codesandbox, any help would be appreciated - https://codesandbox.io/s/llowx57kj7
@posva @yyx990803 Should I open a new ticket with this request? It's a request for Vue to match the event bubbling composition of HTML, since it's already so similar.
No, please don't open a new issue again 😅
We precisely don't do bubbling because it's implicit an leads to other
problems
On Mon 20 Aug 2018 at 18:17, Ian notifications@github.com wrote:
@posva https://github.com/posva @yyx990803
https://github.com/yyx990803 Should I open a new ticket with this
request? It's a request for Vue to match the event bubbling composition of
HTML, since it's already so similar.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue/issues/8656#issuecomment-414375228, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAoica508I6l5O2a_wNyAvfvIa9atX7lks5uSuEUgaJpZM4V9OpJ
.>
Eduardo San Martin Morote
@posva Can you help me understand what you mean?
We precisely don't do bubbling because it's implicit
Doesn't "implicit" mean that bubbling should be happening at some level (which it's not)?
@posva @yyx990803 Hey guys - is there anything you could even link me to so I can understand what you're meaning here? I've seen this event bubbling issue pop up on GitHub, StackOverflow, the Vue forum - but I've spent days searching for an explanation and am not finding anything at all that makes sense.
What does
We precisely don't do bubbling because it's implicit an leads to other
problems
mean?
I'm looking for some feature like that.
@theianjohnson "Implicit" refers to some default behavior that is not proactively asked for (as opposed to "explicit"), and that in many cases would lead to confusions and gotchas. E.g. JavaScript implicitly does type conversions when you use the "==" operator (0 == '' is true), and is generally considered a design flaw.
@fnlctrl Appreciate the explanation, but how's that relate to event bubbling and listening in Vue? If we're specifically firing an event from a component isn't that pretty explicit?
@posva @yyx990803 @fnlctrl Is there still no concrete answer as to why event bubbling in Vue components shouldn't act in line with HTML event bubbling? Or expanding on what other implicit problems it leads to?
Hey @theianjohnson, you can use Provide/Inject to provide some of the event handlers inside the <datatable> component that can be later injected into <dateFilter> component. Or any child components that chooses to inject those methods.
This might not be the solution you asked for, but at least it does not create a strong coupling like this.$parent.$emit. And with proper documentation, it’s pretty easy to manage.
Here’s an example: https://codesandbox.io/s/nny1m3j1j
As for event bubbling – events that are not caught by a parent bubble up to the root component. If for some reason you have another handler for the same event name – you’re in trouble. Makes it also much harder to see explicit connections, especially if the component tree changes so that a component is no longer an ancestor/child of a specific component.
@theianjohnson
there still no concrete answer as to why event bubbling in Vue components shouldn't act in line with HTML event bubbling? Or expanding on what other implicit problems it leads
Read other tickets like yours in #4781 @yyx990803 says:
As explained in #4332, it doesn't make sense to add listeners on
because doesn't always render only a single element. This is why a wrapper element is required.
Event names cannot be dashed,
https://github.com/ngVue/ngVue/issues/78
Most helpful comment
Thanks for your interesting. However, nothing changes regarding existing arguments. An event is always bound to a single element/component.
To me, it looks like what you need is to pass a function in a scoped slot and let the parent use that function, but your example is unclear. That could be done like this:
Could you clarify what is exactly problematic, or what are you trying to write in your template?