Reputation: 507
As the title suggests, I am trying to pass a method from a parent component to a child component.
For example,
App.html
<div>
<TodoItem
done={todo.done}
toggle={toggle}
/>
</div>
<script>
import TodoItem from './TodoItem.html';
export default {
components: {
TodoItem,
},
methods: {
toggle(index) {
console.log(index);
},
},
};
</script>
TodoItem.html
<div>
<button on:click="toggle(0)"></button>
</div>
<script>
export default {
methods: {
toggle(index) {
// a guess. this works if you pass in console.log
this.options.data.toggle(index)
},
},
};
</script>
The desired functionality is that TodoItem calls the parent's method with its data.
This example breaks, the console logs TypeError: this.options.data.toggle is not a function
.
Upvotes: 15
Views: 25620
Reputation: 1311
Since Svelte 3, you can pass down the parent method directly to the child component just like in React. From this answer:
There is no significant difference between callback props and events. The only difference is semantic. Callback props used to be quite tricky to use in svelte 2 so events used to be the preferred method. The redesign in v3 made callback props much simpler to use but we also still have events. So having both is partly historic. Events are a little bit easier to forward up the tree but this is a pretty minor benefit, if at all.
Upvotes: 0
Reputation: 2376
Seems like "fire" was part of svelte v2 but in svelte v3 it's changed with createEventDispatcher
e.g -
child.svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
parent.svelte
<script>
import Inner from './child.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>
for more info - please visit : https://svelte.dev/tutorial/component-events
Upvotes: 30
Reputation: 4515
This worked for me.
From the SOURCE
Useful when the component can't live without a parent, and the parent can't live without the component
App.svelte
<script>
import Child from './Child.svelte'
const handleSubmit = value => {
console.log(value)
}
</script>
<Child {handleSubmit}/>
Child.svelte
<script>
export let handleSubmit
let value = ''
const onSubmit = e => {
e.preventDefault()
handleSubmit(value)
}
</script>
<form on:submit={onSubmit}>
<input type="text" bind:value/>
</form>
Another solution is to use context
Useful when all the children and grandchildren components may or may not call the function. The function allows all children and grandchildren components to update the state / make changes to the common parent component.
App.svelte
<script>
import { getContext, setContext } from 'svelte';
import Child1 from './Child1.svelte';
import Child2 from './Child2.svelte';
let counter = 10;
setContext('counter', { increment, decrement });
function increment(delta) {
counter += delta;
}
function decrement(delta) {
counter -= delta;
}
</script>
<Child1 />
<Child2 />
<button on:click={() => { increment(10); }}>
Increment x10
</button>
<div>{counter}</div>
Child1.svelte
<script>
import { getContext } from 'svelte';
const { increment } = getContext('counter');
</script>
<button on:click={() => increment(1)}>Increment</button>
Child2.svelte
<script>
import { getContext } from 'svelte';
const { decrement } = getContext('counter');
</script>
<button on:click={() => decrement(1)}>Decrement</button>
Upvotes: 4
Reputation: 29615
It's possible to pass methods down to child components, but it's a little awkward. A more idiomatic approach is to fire an event from the child component and listen for that event from the parent component:
App.html
<div>
<TodoItem
{todo}
on:toggle="toggle(todo)"
/>
</div>
<script>
import TodoItem from './TodoItem.html';
export default {
components: {
TodoItem,
},
methods: {
toggle(todo) {
todo.done = !todo.done;
const { todos } = this.get();
this.set({ todos });
}
}
};
</script>
TodoItem.html
<div>
<button on:click="fire('toggle')">{todo.description}</button>
</div>
If you need to pass an event up through multiple levels of components you can just refire the event...
<TodoItem on:toggle="fire('toggle', event)">...</TodoItem>
...but there's a shorthand for doing so that means the same thing:
<TodoItem on:toggle>...</TodoItem>
Upvotes: 15