In this article we are going to learn how to pass props to svelte components, passing arguments to function, event fowarding and dispatching and reactive values in svelte. In our last article we saw how to install and setup svelte, then we went on to see some basic svelte syntax like conditional rendering and looping. In this article we are going to see how we can;
- Pass props to our svelte components
- Passing arguments to event handler functions
- Forwarding an event from a child component to a parent component
- Dispatching and reacting to a custom events
- Using reactive values
Don't worry if that seems quite much, by the time we are true with this article your knowledge and understanding about the svelte framework will have increased, let's dive in.
Passing Props To Components
A good method of using most front-end frameworks is to make your components reusable, we can have a reusable list component and then we can pass different types of list to it, we could pass a list of name, or list of heroes or list of footballers. You get the idea, svelte provides a means to do this, let's see how below;
// Reusable HeroList component
<script>
export let heroes
</script>
<div>
<ul>
{#each heroes as hero}
<li> { hero } </li>
{/each}
</ul>
</div>
<style>
</style>
If we want to declare a variable as a property that is going to be set from outside the component we use the export keyword like in the example and now in our parent component we can import the reusable component and pass a prop to it, lets see how;
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
</script>
<div>
<HeroList heroes={heroes} />
</div>
<style>
</style>
In the parent component we just declare an attribute on the component tag and then pass in the prop as a value to the attribute, in this case we used heroes={heroes}, remember the name of the attribute has to match the name of the variable that was declared with the export keyword inside the reusable component. One trick, if the variable you are going to pass in to the reusable component has the same name as the props, you can simply remove the assignment operator and use only the name of the variable inside curl braces,
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
</script>
<div>
<HeroList {heroes}>
</HeroList>
</div>
<style>
</style>
Passing arguments to event handler functions
Remember how we declared functions and passed a reference to them inside an event handler using the on:event={function} syntax, although we automatically get the event object passed in as an argument to the function, how about passing in our own custom arguments? We can't do on:event={function(arg)} because we know that would fire our function immediately the DOM is loaded, however there is a means around this, we can wrap our function call inside an arrow function and that way it prevents our function from being called when the DOM is loaded, rather only when the event is fired, let's see a typical example;
// Reusable HeroList component
<script>
export let heroes
let logHero = (e, hero) => console.log(e, hero)
</script>
<div>
<ul>
{#each heroes as hero}
<li>
<div>
{ hero }
<button
on:click={(e) => logHero(e, hero)}>
log hero
</button>
</div>
</li>
{/each}
</ul>
</div>
<style>
</style>
We can also take in the event object on the arrow function and then pass it to our function, the anonymous arrow function will accept the event object, when we call our function we can pass it into out function like we did above.
Event Forwarding
There are certain scenarios when we want to call a function on a component when event is fired but somehow that function is defined inside the parent component rather than the component itself. We can get around this by forwarding the event from the component to the parent component where it is used and then react to that event by calling a function declared inside the parent component. How do we do this? we simply tack on:event inside the component but we don't pass in a function, instead we then go to the parent component and then on the child component we tack on on:event={function}, the event we are reacting to, has to match the one that was called on in the child component, let's see an exmple;
// Reusable HeroList component
<script>
export let heroes
</script>
<div>
<ul>
{#each heroes as hero}
<li> { hero } </li>
<button on:click>log hero</button>
{/each}
</ul>
</div>
<style>
</style>
In the parent component, we can react to that event like;
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
let logHeroes = () => console.log(heros)
</script>
<div>
<HeroList {heroes} on:click={logHeroes}>
</HeroList>
</div>
<style>
</style>
Dispatching custom events
Although there are lot's of events that we can react to in JavaScript, it will be useful if we can react to a custom event other normal traditional events. Every framework should provide a means of handling this gracefully and svelte does. First thing, we import the createEventDispatcher from the svelte library, and then we invoke the createEventDispatcher and store it in a variable, that variable can be used to dispatch a custom event and we can then react to that, let's see a typical use case; since our heroList is declared inside the App component it would only make sense if we can log heroes from the App component.
// Reusable HeroList component
<script>
import { createEventDispatcher } from 'svelte'
let dispatch = createEventDispatcher();
export let heroes
let handleClick = (hero) => {
dispatch('logHero', hero)
}
</script>
<div>
<ul>
{#each heroes as hero}
<li> { hero } </li>
<button on:click={() => handleClick(hero)}>log hero</button>
{/each}
</ul>
</div>
<style>
</style>
This will now emit a custom event called logHero and then inside the parent component we can then declare a function that will be called when logHero is fired. We use the on:event={function} syntax, where event is equal to the custom event we dispatched inside the child component. The dispatch function accepts two parameters, one is the name of the event we want to dispatch and it should be a string, while the second is any data we want to pass along with the even when it is fired;
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
let logHeroes = (e) => console.log(e.detail)
</script>
<div>
<HeroList {heroes} on:logHero={logHeroes}>
</HeroList>
</div>
<style>
</style>
The data that we passed on with the dispatch function is available on the event object and we can access it using event.detail, in this case we just log the hero to the console, you could do more with it.
Reactive Values and statements
It will be very useful if we can have some variable that will be updated anytime one or two things change or some statements that will be run when a variable changes, svelte provides a means for us to do this, we can use the dollar sign, a colon and then the variable name and then we set it equal to something. If at some point that thing changes, then our the value of our reactive statement is going to update itself automatically to reflect that change,it will also update the DOM,let's see a typical example;
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
$: DCU = heroes
let changeHeroes = () => {
console.log(DCU)
heroes = ['batman', 'aquaman', 'wonder woman']
console.log(DCU)
}
let logHeroes = () => console.log(heros)
</script>
<div>
<HeroList {heroes} on:click={logHeroes}>
</HeroList>
<button on:click={changeHeroes}>change Heroes</button>
</div>
<style>
</style>
You should see the that the first time DCU is logged we get the original array we set DCU equal to, once that changes we see that the value of DCU has been updated to reflect the new heroes inside the the heroes array. We can also have reactive statements,
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
$: DCU = heroes
let changeHeroes = () => {
heroes = ['batman', 'aquaman', 'wonder woman']
}
$: console.log(DCU)
let logHeroes = () => console.log(DCU)
</script>
<div>
<HeroList {heroes} on:click={logHeroes}>
</HeroList>
<button on:click={changeHeroes}>change Heroes</button>
</div>
<style>
</style>
Now once the value of DCU changes, automatically the console.log statement will be ran to show the updated value of DCU, and we can do more than console.log, we can have more than one statement and they would all behave reactive, we just need to wrap them in curly braces, like so;
// App component
<script>
import HeroList from './HeroList.svelte'
let heores = ['superman', 'cyborg', 'shazam']
$: DCU = heroes
let changeHeroes = () => {
heroes = ['batman', 'aquaman', 'wonder woman']
}
$: {
console.log(DCU)
console.log(heroes[0])
}
let logHeroes = () => console.log(heros)
</script>
<div>
<HeroList {heroes} on:click={logHeroes}>
</HeroList>
<button on:click={changeHeroes}>change Heroes</button>
</div>
<style>
</style>
The both console.log statements will run whenever the value of DCU or the first item in the heroes array changes. Whew! that was fast, well we have to stop here for today, hope enjoy it and you find it useful, do have a wonderful day.