Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow components to listen to <slot> events #8656

Closed
theianjohnson opened this issue Aug 14, 2018 · 17 comments
Closed

Allow components to listen to <slot> events #8656

theianjohnson opened this issue Aug 14, 2018 · 17 comments

Comments

@theianjohnson
Copy link

theianjohnson commented Aug 14, 2018

What problem does this feature solve?

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.

What does the proposed API look like?

Allow a component with a to listen to events emitted from that

@posva
Copy link
Member

posva commented Aug 14, 2018

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?

@theianjohnson
Copy link
Author

theianjohnson commented Aug 15, 2018

@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 -

  1. In the DateFilter component emit an event with the two dates selected (start and end) when a user clicks "Apply" (this button isn't in this example)
  2. Have the Datatable component listen for that event and then call the server to request new data with those date filters

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>

@posva
Copy link
Member

posva commented Aug 15, 2018

So yeah, this is solved using the function. I cannot check a repository right now but I could help you on a codesandbox 🙂

@posva posva closed this as completed Aug 15, 2018
@theianjohnson
Copy link
Author

@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?

@posva
Copy link
Member

posva commented Aug 15, 2018

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....

@theianjohnson
Copy link
Author

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

@theianjohnson
Copy link
Author

@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.

@posva
Copy link
Member

posva commented Aug 20, 2018 via email

@theianjohnson
Copy link
Author

@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)?

@theianjohnson
Copy link
Author

theianjohnson commented Aug 22, 2018

@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?

@alexsandro-xpt
Copy link

I'm looking for some feature like that.

@fnlctrl
Copy link
Member

fnlctrl commented Aug 22, 2018

@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.

@theianjohnson
Copy link
Author

@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?

@theianjohnson
Copy link
Author

@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?

@shentao
Copy link
Member

shentao commented Oct 2, 2018

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.

@aledujke
Copy link

aledujke commented Nov 21, 2018

@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.

@gaganhn
Copy link

gaganhn commented Sep 10, 2019

Event names cannot be dashed,
ngVue/ngVue#78

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants