Check out a free preview of the full Svelte Fundamentals course
The "Advanced Bindings" Lesson is part of the full, Svelte Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
Rich demonstrates some more advanced uses of binding, including content-editable elements, inside each blocks, media elements, dimensions of block-level elements, this, component bindings, and component instances.
Transcript from the "Advanced Bindings" Lesson
[00:00:00]
>> All right we're gonna learn some more about bindings before we used bindings for form elements like inputs and select and text areas, but we can use them in other contexts too. Here we're gonna use it in the context of a content editable element. So this is an element that has content editable, true.
[00:00:20]
Right, this is just an attribute you can add to an element and it allows you to type stuff into it. Right, but that is not being reflected anywhere because we're not binding anything. On this element because we're using content editable, we can bind to the HTML inside that element.
[00:00:41]
Gonna bind the inner HTML to our HTML value here. And you'll see that, the div is now populated with the content, and if we start typing in here, that is reflected in the element immediately below. It is possible to use a binding inside an each block. Here we've got another todo list And we have some inputs inside that todo list.
[00:01:16]
And if we change these values to bindings, Then when we interact with these inputs, it will actually mutate the data. So below we've got a list of the number of todos that are remaining, and that is calculated from the todos alongside a filter function in a reactive declaration.
[00:01:45]
And if I check these items, you'll see that the remaining value updates automatically. As I said, it's mutating the array. That might not be a thing that you want, you might prefer to use immutable data structures. And if that's the case, then you should avoid using these bindings, and you should use event handlers instead.
[00:02:05]
But as I say, mutation is fine as long as it's local and as long as you're not mutating objects that you don't own, so generally this sort of thing is okay. We can also bind to the properties of media elements like audio and video. Here we've got some custom UI for this audio player which is defined in this AudioPlayer.svelte file.
[00:02:32]
And we're gonna finish implementing this by adding the audio element itself along with some bindings. So the source attribute is being passed in from the app. We're using the spread operator because each of these tracks has the source in the data. And then we're gonna add some bindings to the values that we're declaring up here, time, duration and this paused flag.
[00:03:25]
For duration and time I'm just gonna use the shorthand property because this is kind of unnecessary, we can get rid of that. And you'll notice that at least for the audio elements that have loaded, one of them seemed to fail to load, it's now showing the duration of the audio down here.
[00:03:44]
It's loading the audio, and once it figures out how long the piece is, it binds that back to the state. And finally, bind paused. And so to play the audio, we need to add an event listener to the button that has the play icon that will just toggle that paused value.
[00:04:09]
Very familiar by now, on:click, and then we pass in a function. And it's just gonna invert the value of paused. So if we've done this right, we should be able to listen to some music now, okay? So our audio player now has some basic functionality. We wanna add the ability to seek to a specific part of the audio by dragging these sliders next to the time indicator.
[00:04:45]
If we scroll down to the slider element, we have this pointer down handler which lets us seek to a specific moment. And we just need to finish implementing that seek function. We're calculating a value of p which is a value between zero and one, and we just need to multiply that by the duration to get the correct time for the audio.
[00:05:10]
And we can just assign that directly to the time variable. So this time when we start playing, we can drag that to the good bit.
[00:05:26]
[MUSIC]
[00:05:30]
And it's updating all the user interface for us. And we can add another event handler on the audio element itself, so that when the audio finishes, We reset the time right back to the beginning.
[00:05:55]
[MUSIC]
[00:05:59]
So we've got the last few seconds of Gymnopédie here, and then when it finishes you'll see that go back to zero. So we've covered a few of the bindings that are available on media elements, but there are more. We've got a bunch of read-only bindings, duration, buffered, seekable, played, seeking, ended.
[00:06:16]
And five two way bindings, which means that they will update the state in your component, but also if you update the state in your component, it will affect the media element. In addition to these videos, have a videoWidth and a videoHeight binding, which you can use to control the positioning of other elements and so on.
[00:06:36]
Does anyone have any questions about the stuff that we've just learned? Okay. So every-block level element in the DOM has clientWidth, clientHeight, offsetWidth, and offsetHeight bindings, and we can use that in our component state. Up here, we have width and height declared, and we can add these bindings to our div bind:clientWidth={w}, bind:clientHeight={h}.
[00:07:18]
And now you can see that in the label below the edit this textbook, is actually showing the current dimensions of that element. And if we make this larger or smaller, that updates automatically. Similarly if we add more text it's updating the width of that. This works with some crazy hacks that only work with block level elements.
[00:07:47]
So if you wanna use this with something like a canvas, which cannot have children, then you will need to use a wrapper element around it. In the future, we're gonna switch this to use resize observer binding so that you will be able to use it anywhere. All right, you'll remember this exercise from earlier where we were manipulating the contents of a canvas by querying the DOM for it with document.querySelector('canvas').
[00:08:16]
And that works, this call here works, but it's really not ideal because if you had more than one of this component on the page, then every time one of those components mounted they would get a reference to the same canvas. Using document.querySelector is not really a good way to interact with DOM if you don't control the entire page.
[00:08:38]
So instead of using document.querySelector, we can use an element binding inside the component. First of all, we need to declare a variable, let canvas. And then rather than using that query selector, we're gonna bind the value of "this", to that value. And get rid of the query selector, and the app continues to work.
[00:09:11]
And we can do the same thing that we do with elements with components, we can have bindings on components. Here we have a keypad component, which is you can think of in the same way that you would have an input element or a text element. You can think of this as a very custom version of one of those.
[00:09:31]
And we can bind to the state inside the keypad in the same way that we'd bind to the value of an input element. So inside App, we'll add the binding, and we're gonna bind the value of the keypad to the PIN value that is declared inside app.svelte. Now if I enter my PIN, It can use that state inside this view reactive declaration, which is then used inside the DOM.
[00:10:09]
So this is handy, but it is something that you should generally use sparingly. As we've talked about before, in general, you want your data to flow down from the top of the app, you don't want your data to go back up. But in some locally confined scenarios, it's much more convenient to be able to bind to a value of a component, rather than having to deal with event handlers.
[00:10:36]
Finally, just as we can bind to DOM elements, we can also bind to component instances themselves using the same bind:this directive. This is useful in the rare cases that you need to interact with a component programmatically, as opposed to by providing it with new state. So if we take the drawing app that we looked at a few moments ago, it'd be nice to add a button to clear the screen, and at the moment that's quite difficult.
[00:11:01]
So let's add a clear method inside the canvas component. Let's put that here, we're gonna export function clear. And when this gets called, we'll do context.clearRect, Just to clear the entire canvas. And then in app.svelte, we need to get a reference to this component. And then finally, we need to bind that value to the component instance.
[00:11:55]
So now all we need to do in order to interact with the canvas component programmatically, is call that clear function inside an event handler. So down here where we've got the controls, we have this button here which is that close button that you can see in the top left.
[00:12:11]
We add another button. Give it a click handler. And when we click on that element, we're gonna call canvas.clear. So now we can draw something in here. And we can clear the canvas if we make a mistake.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops