Svelte: How to pass action to component?
Asked Answered
U

2

5

There is a similar question asked here but I do not believe the answer applies to my use case.

I'm using Svelte MaterialUI and attempting to extend the DataTable component with the ability to drag and drop rows. I'm using the svelte-dnd-action module to support the drag and drop behaviors.

The following works just fine. I'm able to drag and drop rows of the table.

<table>
  <thead>...</thead>
  <tbody use:dndzone{...opts}>
     ...data
  <tbody>
</table>

However, when attempting to plug the module into a Material UI Component, I receive an error stating "actions can only be applied to DOM elements, not components."

<DataTable>
  <Head>...</Head>
  <Body use:dndzone={...opts}>
    ...Data
  </Body>
</DataTable>

The definition of the Body component looks like this:

<tbody
  use:useActions={use}
  use:forwardEvents
  class="mdc-data-table__content {className}"
  {...exclude($$props, ['use', 'class'])}
><slot></slot></tbody>

<script>
  import {setContext} from 'svelte';
  import {get_current_component} from 'svelte/internal';
  import {forwardEventsBuilder} from '@smui/common/forwardEvents.js';
  import {exclude} from '@smui/common/exclude.js';
  import {useActions} from '@smui/common/useActions.js';
  const forwardEvents = forwardEventsBuilder(get_current_component());
  export let use = [];
  let className = '';
  export {className as class};
  setContext('SMUI:data-table:row:header', false);
</script>

Is there a way to forward my Action to this component? Or a better way to handle this use case? Thank you in advance.

Ulrike answered 10/2, 2021 at 17:9 Comment(0)
H
5

The general answer is that you cannot pass actions to a component. That is, unless the component has this exposed for you.

Luckily the library you mention has it, as is written in their documentation:

You can add actions to the components with use={[Action1, [Action2, action2Props], Action3]}.

So in your case the code I believe would be

<Body use={[[dndzone, opts]]}>
Hebdomadal answered 10/2, 2021 at 17:36 Comment(1)
FYI, the tag could look like this: <Body use={[[dndzone, {items}]]} on:consider={handleConsider} on:finalize={handleFinalize} >Tinctorial
C
9

Action can only be applied to DOM element. However, it's possible to pass a function by property to a component, and this component can use this property in a "use" directive.

An example:

<script>
  function myAction() {
    ...
  }
</script>

<!-- pass myAction in a property 'action' -->
<MyComponent action={myAction}/>

<!-- MyComponent.svelte -->
<script>
  export let action;
</script>

<div use:action/>

If you look at the smui library, you'll see that every component export an 'use' property, and apply the content of this property to a dom element. use:useActions={use} inject the action defined in the use property as actions.

In other words, in smui, you can pass actions to components by using the use property.

<Body use={myAction}/>
Civics answered 10/2, 2021 at 17:34 Comment(1)
Thank you. I'm curious, would it be possible that MyComponent.svelte accepts an action if passed any, and not, if not. In my case, when I don't pass an action it throws an errorDaina
H
5

The general answer is that you cannot pass actions to a component. That is, unless the component has this exposed for you.

Luckily the library you mention has it, as is written in their documentation:

You can add actions to the components with use={[Action1, [Action2, action2Props], Action3]}.

So in your case the code I believe would be

<Body use={[[dndzone, opts]]}>
Hebdomadal answered 10/2, 2021 at 17:36 Comment(1)
FYI, the tag could look like this: <Body use={[[dndzone, {items}]]} on:consider={handleConsider} on:finalize={handleFinalize} >Tinctorial

© 2022 - 2024 — McMap. All rights reserved.