inertia icon indicating copy to clipboard operation
inertia copied to clipboard

Add events to `use:inertia` directive

Open pedroborges opened this issue 5 years ago • 5 comments

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!

pedroborges avatar Sep 25 '20 17:09 pedroborges

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 avatar Sep 26 '20 10:09 reinink

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.

reinink avatar Sep 26 '20 10:09 reinink

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.

pedroborges avatar Sep 26 '20 15:09 pedroborges

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

claudiodekker avatar Sep 28 '20 08:09 claudiodekker

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 avatar Sep 28 '20 14:09 pedroborges

Closing this in favor of #1344.

reinink avatar Nov 26 '22 14:11 reinink