Input box binding
The general rule is that the data flow in Svelte is top-down, the parent component can set Props on the child component, and the component can set properties on the element, but not vice versa.
Sometimes we need to break this rule. Taking the element in the component as an example, we could add an on: Input event handler that sets the value of name to event.target.value, but that’s a bit… Is not flexible. As we’ll see, the situation is even worse for other formal elements.
Instead, we can use the bind:value directive:
<input bind:value={name}>
Copy the code
This means that changes to the name value not only update the input value, but changes to the input value also update the name.
In the DOM, everything is a string. This is not helpful when dealing with numeric input, such as type=”number” or type=”range”, because it means casting the input.value type before using it.
Using bind:value, Svelte will handle it automatically for you:
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
Copy the code
Complete code:
<script>
let a = 1;
let b = 2;
</script>
<label>
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
</label>
<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>
<p>{a} + {b} = {a + b}</p>
Copy the code
Selection box binding
Check boxes are used to switch between states. Instead of binding to input.value, we bind to input.checked:
<input type=checkbox bind:checked={yes}>
Copy the code
Complete code:
<script>
let yes = false;
</script>
<label>
<input type=checkbox bind:checked={yes}>
Yes! Send me regular email spam
</label>
{#if yes}
<p>Thank you. We will bombard your inbox and sell your personal details.</p>
{:else}
<p>You must opt in to continue. If you're not paying, you're the product.</p>
{/if}
<button disabled={! yes}>
Subscribe
</button>
Copy the code
You can use the bind:group and value attributes if you have multiple checkboxes for a value, such as gender, radio topics, multiple topics, and so on. Checkboxes in the same group are mutually exclusive; Check box inputs in the same group are stored as arrays.
Add bind:group to each input:
<input type=radio bind:group={scoops} value={1}>
Copy the code
In this case, we can simplify the code by moving the checkbox input to the each block. First, add a menu variable to the
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
Copy the code
And then:
<h2>Flavours</h2>
{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}
Copy the code
Now we can easily display our menu.
Complete code:
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
let menu = [
'Cookies and cream'.'Mint choc chip'.'Raspberry ripple'
];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>
<h2>Size</h2>
<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>
<h2>Flavours</h2>
{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}
Copy the code
Textarea binding
<textarea bind:value={value}></textarea>
Copy the code
In this case, where the variable name is the same as the attribute name of the bound element, syntactic sugar can be used:
<textarea bind:value></textarea>
Copy the code
Applies to all bindings, not just text bindings
Drop – down selection box binding
<select bind:value={selected} on:change="{() => answer = ''}">
Copy the code
Sample code:
<script>
let questions = [
{ id: 1.text: `Where did you go to school? ` },
{ id: 2.text: `What is your mother's name? ` },
{ id: 3.text: `What is another personal fact that an attacker could easily find with Google? `}];let selected;
let answer = ' ';
function handleSubmit() {
alert(`answered question ${selected.id} (${selected.text}) with "${answer}"`);
}
</script>
<h2>Insecurity questions</h2>
<form on:submit|preventDefault={handleSubmit}>
<select value={selected} on:change="{() => answer = ''}">
{#each questions as question}
<option value={question}>
{question.text}
</option>
{/each}
</select>
<input bind:value={answer}>
<button disabled={! answer} type=submit>
Submit
</button>
</form>
<p>selected question {selected ? selected.id : '[waiting...] '}</p>
<style>
input {
display: block;
width: 500px;
max-width: 100%;
}
</style>
Copy the code
Note that the value of
Since we did not set the initial value of selected, the binding automatically sets it to the default value (first in the list, first option). Be careful, though, that selected is still undefined until the binding is initialized, so we can’t blindly reference it, for example using select.id in a template.
Select multiple bindings from the drop-down box
The drop-down selection box can be set to multiple selections, in which case the selected content is stored as an array.
Sample complete code:
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
let menu = [
'Cookies and cream'.'Mint choc chip'.'Raspberry ripple'
];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>
<h2>Size</h2>
<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>
<h2>Flavours</h2>
<select multiple bind:value={flavours}>
{#each menu as flavour}
<option value={flavour}>
{flavour}
</option>
{/each}
</select>
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}
Copy the code
This is an example we wrote with a selection box, now rewritten with the multi-selection feature of a drop-down selection box.
Element bindings that support the contentedItable property
Elements with contenteditable=”true” attribute support textContent and innerHTML bindings:
<div
contenteditable="true"
bind:innerHTML={html}
></div>
Copy the code
Each block event binding
{#each todos as todo}
<input
type=checkbox
bind:checked={todo.done}
>
<input
placeholder="What needs to be done?"
bind:value={todo.text}
>
{/each}
Copy the code
Note that interacting with these elements changes the array values. If you prefer immutable groups, avoid these bindings and use event handlers instead.
Complete example:
<script>
let todos = [
{ done: false.text: 'finish Svelte tutorial' },
{ done: false.text: 'build an app' },
{ done: false.text: 'world domination'}];function add() {
todos = todos.concat({ done: false.text: ' ' });
}
function clear() {
todos = todos.filter(t= >! t.done); }$: remaining = todos.filter(t= >! t.done).length;</script>
<h1>Todos</h1>
{#each todos as todo}
<div class:done={todo.done}>
<input
type=checkbox
checked={todo.done}
>
<input
placeholder="What needs to be done?"
value={todo.text}
>
</div>
{/each}
<p>{remaining} remaining</p>
<button on:click={add}>
Add new
</button>
<button on:click={clear}>
Clear completed
</button>
<style>
.done {
opacity: 0.4;
}
</style>
Copy the code
Media elements
You can bind attributes to
<script>
// These values are bound to properties of the video
let time = 0;
let duration;
let paused = true;
let showControls = true;
let showControlsTimeout;
function handleMousemove(e) {
// Make the controls visible, but fade out after
// 2.5 seconds of inactivity
clearTimeout(showControlsTimeout);
showControlsTimeout = setTimeout(() = > showControls = false.2500);
showControls = true;
if(! (e.buttons &1)) return; // mouse not down
if(! duration)return; // video not loaded yet
const { left, right } = this.getBoundingClientRect();
time = duration * (e.clientX - left) / (right - left);
}
function handleMousedown(e) {
// we can't rely on the built-in click event, because it fires
// After a drag -- we have to listen for ourselves
function handleMouseup() {
if (paused) e.target.play();
else e.target.pause();
cancel();
}
function cancel() {
e.target.removeEventListener('mouseup', handleMouseup);
}
e.target.addEventListener('mouseup', handleMouseup);
setTimeout(cancel, 200);
}
function format(seconds) {
if (isNaN(seconds)) return '... ';
const minutes = Math.floor(seconds / 60);
seconds = Math.floor(seconds % 60);
if (seconds < 10) seconds = '0' + seconds;
return `${minutes}:${seconds}`;
}
</script>
<h1>Caminandes: Llamigos</h1>
<p>From <a href="https://cloud.blender.org/open-projects">Blender Open Projects</a>. CC-BY</p>
<div>
<video
poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
on:mousemove={handleMousemove}
on:mousedown={handleMousedown}
bind:currentTime={time}
bind:duration
bind:paused>
<track kind="captions">
</video>
<div class="controls" style="opacity: {duration && showControls ? 1:0}">
<progress value="{(time / duration) || 0}"/>
<div class="info">
<span class="time">{format(time)}</span>
<span>click anywhere to {paused ? 'play' : 'pause'} / drag to seek</span>
<span class="time">{format(duration)}</span>
</div>
</div>
</div>
<style>
div {
position: relative;
}
.controls {
position: absolute;
top: 0;
width: 100%;
transition: opacity 1s;
}
.info {
display: flex;
width: 100%;
justify-content: space-between;
}
span {
padding: 0.2 em 0.5 em;
color: white;
text-shadow: 0 0 8px black;
font-size: 1.4 em;
opacity: 0.7;
}
.time {
width: 3em;
}
.time:last-child { text-align: right }
progress {
display: block;
width: 100%;
height: 10px;
-webkit-appearance: none;
appearance: none;
}
progress::-webkit-progress-bar {
background-color: rgba(0.0.0.0.2);
}
progress::-webkit-progress-value {
background-color: rgba(255.255.255.0.6);
}
video {
width: 100%;
}
</style>
Copy the code
When you click on the video, it will update the time, Duration and Paused response. This means we can use them to build custom controls.
Typically, you use currentTime and listen for the timeUpdate event for tracing. But these events fire too infrequently, making the UI unstable. Svelte does better because currentTime uses requestAnimationFrame.
The full set of bindings
duration
Readonly: Total video duration, in secondsbuffered
(readonly) :{start, end}
An array of objectsseekable
(read-only) : same as aboveplayed
(read-only) : same as aboveseeking
(read only) : Boolean valueended
(read only) : Boolean value
And five bidirectional bindings:
currentTime
: Current time point in the video, in secondsplaybackRate
: How fast can the video play,1
“Normal”paused
: This should be self-evidentvolume
: Volume, value between 0 and 1muted
: a Boolean value, where true is mute
Video also has the read-only bindings videoWidth and videoHeight
Element size binding
Each block level element has clientWidth, clientHeight, offsetWidth, and offsetHeight bindings:
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
Copy the code
These are read-only bindings, and changing the values of w and h has no effect.
Measure elements using a similar technique. There is some overhead, so it is not recommended for large numbers of elements.
Display: inline does not measure elements in this way; Neither can elements that contain other elements, such as
Complete sample code:
<script>
let w;
let h;
let size = 42;
let text = 'edit me';
</script>
<input type=range bind:value={size}>
<input bind:value={text}>
<p>size: {w}px x {h}px</p>
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
<style>
input { display: block; }
div { display: inline-block; }
span { word-break: break-all; }
</style>
Copy the code
this
The read-only attribute this binding applies to each element (and component) and allows you to get a reference to the rendered element. For example, we can get a reference to the
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
Copy the code
Note that the canvas value is undefined before the component is installed, so we put the logic in the onMount lifecycle function.
Complete sample code:
<script>
import { onMount } from 'svelte';
let canvas;
onMount(() = > {
const ctx = canvas.getContext('2d');
let frame = requestAnimationFrame(loop);
function loop(t) {
frame = requestAnimationFrame(loop);
const imageData = ctx.getImageData(0.0, canvas.width, canvas.height);
for (let p = 0; p < imageData.data.length; p += 4) {
const i = p / 4;
const x = i % canvas.width;
const y = i / canvas.height >>> 0;
const r = 64 + (128 * x / canvas.width) + (64 * Math.sin(t / 1000));
const g = 64 + (128 * y / canvas.height) + (64 * Math.cos(t / 1000));
const b = 128;
imageData.data[p + 0] = r;
imageData.data[p + 1] = g;
imageData.data[p + 2] = b;
imageData.data[p + 3] = 255;
}
ctx.putImageData(imageData, 0.0);
}
return () = > {
cancelAnimationFrame(frame);
};
});
</script>
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
<style>
canvas {
width: 100%;
height: 100%;
background-color: # 666;
-webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
}
</style>
Copy the code
Component bindings
Just as you can bind to properties of a DOM element, you can also bind to props of a component. For example, we can bind the props for value in the Keypad component as if it were a form element:
<Keypad bind:value={pin} on:submit={handleSubmit}/>
Copy the code
The PIN value in the parent component is now updated immediately when the user interacts with the keyboard.
Use component bindings sparingly. If you have a lot of data flowing through your application, it can be difficult to keep track of the data flow around your application, especially if there is no “single source of facts.”
Sample complete code:
Keypad.svelte
<script>
import { createEventDispatcher } from 'svelte';
export let value = ' ';
const dispatch = createEventDispatcher();
const select = num= > () = > value += num;
const clear = () = > value = ' ';
const submit = () = > dispatch('submit');
</script>
<div class="keypad">
<button on:click={select(1)}>1</button>
<button on:click={select(2)}>2</button>
<button on:click={select(3)}>3</button>
<button on:click={select(4)}>4</button>
<button on:click={select(5)}>5</button>
<button on:click={select(6)}>6</button>
<button on:click={select(7)}>7</button>
<button on:click={select(8)}>8</button>
<button on:click={select(9)}>9</button>
<button disabled={! value} on:click={clear}>clear</button>
<button on:click={select(0)}>0</button>
<button disabled={! value} on:click={submit}>submit</button>
</div>
<style>
.keypad {
display: grid;
grid-template-columns: repeat(3.5em);
grid-template-rows: repeat(4.3em);
grid-gap: 0.5 em
}
button {
margin: 0
}
</style>
Copy the code
App.sevlte
<script>
import Keypad from './Keypad.svelte';
let pin;
$: view = pin ? pin.replace(/\d(? ! $)/g.', ') : 'enter your pin';
function handleSubmit() {
alert(`submitted ${pin}`);
}
</script>
<h1 style="color: {pin ? '#333' : '#ccc'}">{view}</h1>
<Keypad bind:value={pin} on:submit={handleSubmit}/>
Copy the code
This part of the content is more, but summed up, knowledge is not a lot of knowledge, however, the real knowledge of practice, go to the official website to practice it! Input box binding