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
.
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
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>
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.
REPL
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With