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

  • durationReadonly: Total video duration, in seconds
  • buffered(readonly) :{start, end}An array of objects
  • seekable(read-only) : same as above
  • played(read-only) : same as above
  • seeking(read only) : Boolean value
  • ended(read only) : Boolean value

And five bidirectional bindings:

  • currentTime: Current time point in the video, in seconds
  • playbackRate: How fast can the video play,1“Normal”
  • paused: This should be self-evident
  • volume: Volume, value between 0 and 1
  • muted: 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

. In these cases, you need to measure the parent element instead.

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

element:

<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