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

Add events to use:inertia directive #237

Closed
wants to merge 2 commits into from
Closed

Conversation

pedroborges
Copy link
Collaborator

@pedroborges pedroborges commented Sep 25, 2020

I was able to replicate #235 on the use:inertia directive like so:

<a
  href="/sample"
  use:inertia
  on:click={() => console.log('click')}
  on:cancelToken={event => console.log('cancelToken', event.detail.cancelToken)}
  on:start={event => console.log('start', event.detail.visit)}
  on:progress={event => console.log('progress', event.detail.progress)}
  on:finish={() => console.log('finish')}
  on:cancel={() => console.log('cancel')}
  on:success={event => console.log('success', event.detail.page)}>
  Sample link
</a>

Also works on the InertiaLink component:

<InertiaLink
  href="/sample"
  on:click={() => console.log('click')}
  on:cancelToken={event => console.log('cancelToken', event.detail.cancelToken)}
  on:start={event => console.log('start', event.detail.visit)}
  on:progress={event => console.log('progress', event.detail.progress)}
  on:finish={() => console.log('finish')}
  on:cancel={() => console.log('cancel')}
  on:success={event => console.log('success', event.detail.page)}>
  Sample link
</InertiaLink>

The main difference is that these are not simple callbacks, but event listeners. In Svelte you can't access event listeners like props during runtime because they are processed by Svelte during compilation. The solution is to register on* callbacks that fire custom events on the DOM node.

The only issue so far is that I couldn't find a way to either return false or prevent the on:start event and forward that response back to the onStart callback internally.

This PR also removes logic that was duplicated on the use:inertia directive and InertiaLink component. Now the component uses the directive internally. Double win!

@reinink
Copy link
Member

reinink commented Sep 26, 2020

Thanks for trying to make this work @pedroborges. 👍

The main difference is that these are not simple callbacks, but event listeners. In Svelte you can't access event listeners like props during runtime because they are processed by Svelte during compilation.

So, I wonder, can you not make them callbacks (plain function props), and not listeners? What pushed you to make them listeners? Simply because they started with "on"?

@reinink
Copy link
Member

reinink commented Sep 26, 2020

Also, one more quick question...does this actually run the event listener (callback)? From what I can tell from this code, it doesn't...it just fires the event. Which, at that point, you'd be better off just using the new event system directly.

@pedroborges
Copy link
Collaborator Author

pedroborges commented Sep 26, 2020

can you not make them callbacks (plain function props), and not listeners?

They could be function props on the InertiaLink component, not on the use:inertia directive. Function props wouldn't work with on:* syntax either, they would need to be named something like onStart, for example.

What pushed you to make them listeners? Simply because they started with "on"?

I have implemented it this way because from what @claudiodekker told me you can use v-on or @ in the Vue adapter.

does this actually run the event listener (callback)? From what I can tell from this code, it doesn't...it just fires the event.

<a use:inertia ... on:start={() => {}}> adds an event listener to the a element. Svelte does that during compilation. It's equivalent to el.addEventListener('start', () => {}).

Internally, the use:inertia action creates a callback for each event type.

options.onCancelToken = cancelToken => fireEvent('cancelToken', { detail: { cancelToken } })
options.onStart = visit => fireEvent('start', { cancelable: true, detail: { visit } })
options.onProgress = progress => fireEvent('progress', { detail: { progress } })
options.onFinish = () => fireEvent('finish')
options.onCancel = () => fireEvent('cancel')
options.onSuccess = page => fireEvent('success', { detail: { page } })

fireEvent is responsible for dispatching a custom event on the active node.

function fireEvent(name, eventOptions) {
  return node.dispatchEvent(new CustomEvent(name, eventOptions))
}

These callbacks are then passed as options to Inertia.visit() when the element is clicked.

Inertia.visit(href, options)

When onStart is called internally by Inertia, it will run the callback that fires the start custom event on the active DOM node.

Which, at that point, you'd be better off just using the new event system directly.

This is different because it doesn't fire a global event, only listeners attached to the active DOM node will be run.


I have done a bit more of research and couldn't find Svelte UI kits that use function props for handling component events. The ones I found all use this methods.

@claudiodekker
Copy link
Member

claudiodekker commented Sep 28, 2020

Alright, so, just to make sure I understand you correctly, this is my assumption of what's happening.
Does this sound correct?:


First, the <InertiaLink on:start={() ... adds an event listener to the underlying <a>.

Then, the Inertia.visit is called, with amongst others the options.onStart = event => fireEvent(..) etc. all being passed in as options.

Then, when the Inertia.visit actually progresses:

  • The global callbacks just get fired like the global listener (as per the original core implementation)
  • The local callbacks get fired

Because the 2nd step, when the local callbacks get fired, the fireEvent gets executed, and dispatches an event to the underlying <a> element, which then automatically calls the user-provided on:start callback listener defined in the 1st step.

This user-defined 'listener' method (step 1) then gets executed, returns it's value to the fireEvent, which then again returns that same value to to the onStart listener in Inertia.visit itself

@pedroborges
Copy link
Collaborator Author

That's all correct except for the last paragraph:

This user-defined 'listener' method (step 1) then gets executed, returns it's value to the fireEvent, which then again returns that same value to to the onStart listener in Inertia.visit itself

In my tests I wasn't able to forward the return value from user-defined listener back to Inertia. I read something on the Inertia Discord last week that this might be specific to Firefox, my default browser. Need more testing around this part, everything else works just fine.

@pedroborges pedroborges closed this Oct 3, 2020
@pedroborges pedroborges reopened this Oct 3, 2020
@claudiodekker claudiodekker changed the title [Svelte] Add events to use:inertia directive Add events to use:inertia directive Oct 16, 2020
@claudiodekker claudiodekker added the svelte Related to the svelte adapter label Oct 16, 2020
@claudiodekker claudiodekker added svelte Related to the svelte adapter and removed svelte Related to the svelte adapter labels Jan 4, 2022
@reinink
Copy link
Member

reinink commented Nov 26, 2022

Closing this in favor of #1344.

@reinink reinink closed this Nov 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
svelte Related to the svelte adapter
Projects
Status: Closed 🚪
Development

Successfully merging this pull request may close these issues.

3 participants