Reputation: 251
I'm trying to create a Form component in Sveltejs.
This Form component will use <slot />
to let his parent (App.svelte) define which Inputs and Buttons should be inside the Form.
The Inputs and Buttons are components too and once the Input component's value changes I'd like to update an object inside the parent Form component and when Button is clicked I'd like to send data back to the App component.
I've tried to read Svelte documentation and solve this problem playing around with <slot let:name={value}>
but I can't find a way to update Form component when an Input component's value changes.
This is the structure of what I'm trying to do:
App.svelte
...
<Form on:submit={saveReceivedData}>
<Input name="..." value="..." />
<Button />
</Form>
...
Form.svelte
<script>
...
let data = {}
</script>
...
<form>
<slot />
</form>
...
Input.svelte
...
<input name={name} value={value} on:input={updateDataVariableOnFormComponent} />
...
Button.svelte
...
<button on:click={sendDataVariableToAppComponent}>Send</button>
...
I can try to send you a full code if needed. But, since my problem is not "what I'n doing wrong?" but "how can I do this?", I prefer to write my question in an abstract way.
Upvotes: 10
Views: 4175
Reputation: 251
This is what I wanted to do. I'm honestly not 100% happy about it, but I'm loving Svelte :D
App.svelte
<script>
import Form from "./Form.svelte"
import Input from "./Input.svelte"
import Button from "./Button.svelte"
let data = {
name: "John",
surname: "Smith"
}
const saveValues = (event) => {
data = event.detail
}
</script>
<Form data={data} let:saveMe let:updateMe on:save={saveValues}>
<Input
label="Name"
name="name"
value={data.name}
on:input={updateMe}
/>
<Input
label="Surname"
name="surname"
value={data.surname}
on:input={updateMe}
/>
<Button on:click={saveMe}>
Save
</Button>
</Form>
<h4>Saved data</h4>
<ul>
{#each Object.entries(data) as d}
<li>{d[0]}: {d[1]}</li>
{/each}
</ul>
Form.svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let data;
const saveMe = () => dispatch('save', data)
const updateMe = (e) => data = {...data, [e.detail.name]: [e.detail.value]}
</script>
<slot saveMe={saveMe} updateMe={updateMe} />
Input.svelte
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let name;
export let label = false;
export let value = "";
</script>
<div class="input-field">
<input
id={name}
name={name}
value={value}
on:input={(e) => {
value = e.target.value
dispatch('input', e.target)
}}
/>
{#if label}
<label
for={name}
class:active={value.length > 0}
>{label}</label>
{/if}
<slot />
</div>
Button.svelte
<button on:click>
<slot />
</button>
Upvotes: 0
Reputation: 29605
You can use a component binding to update data in the parent when the child component changes:
<Input name="..." bind:value={someValue} />
With that binding, someValue
in the parent will be synced to value
in the child.
To notify the parent about events in the child, as opposed to state changes, you can either use an event dispatcher...
<!-- Button.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('thinghappened', someData)}>Send</button>
...in which case you could listen for the thinghappened
event...
<Button on:thinghappened={e => doSomethingWith(e)}/>
...or you can simply forward the DOM event with <button on:click>Send</button>
and listen for the click event in the parent with <Button on:click={handler}/>
.
Upvotes: 4