mirror of https://github.com/sveltejs/svelte
parent
7310efc809
commit
0c7b30603e
@ -0,0 +1,295 @@
|
||||
---
|
||||
title: Bindings
|
||||
---
|
||||
|
||||
|
||||
### Bindings
|
||||
|
||||
As we've seen, data can be passed down to elements and components with attributes and [props](guide#props). Occasionally, you need to get data back *up*; for that we use bindings.
|
||||
|
||||
|
||||
#### Component bindings
|
||||
|
||||
Component bindings keep values in sync between a parent and a child:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<Widget bind:childValue=parentValue/>
|
||||
```
|
||||
|
||||
Whenever `childValue` changes in the child component, `parentValue` will be updated in the parent component and vice versa.
|
||||
|
||||
If the names are the same, you can shorten the declaration:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<Widget bind:value/>
|
||||
```
|
||||
|
||||
> Use component bindings judiciously. They can save you a lot of boilerplate, but will make it harder to reason about data flow within your application if you overuse them.
|
||||
|
||||
|
||||
#### Element bindings
|
||||
|
||||
Element bindings make it easy to respond to user interactions:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Element bindings' } -->
|
||||
<h1>Hello {name}!</h1>
|
||||
<input bind:value=name>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
name: 'world'
|
||||
}
|
||||
```
|
||||
|
||||
Some bindings are *one-way*, meaning that the values are read-only. Most are *two-way* — changing the data programmatically will update the DOM. The following bindings are available:
|
||||
|
||||
| Name | Applies to | Kind |
|
||||
|-----------------------------------------------------------------|----------------------------------------------|----------------------|
|
||||
| `value` | `<input>` `<textarea>` `<select>` | <span>Two-way</span> |
|
||||
| `checked` `indeterminate` | `<input type=checkbox>` | <span>Two-way</span> |
|
||||
| `group` (see note) | `<input type=checkbox>` `<input type=radio>` | <span>Two-way</span> |
|
||||
| `currentTime` `paused` `played` `volume` | `<audio>` `<video>` | <span>Two-way</span> |
|
||||
| `buffered` `duration` `seekable` | `<audio>` `<video>` | <span>One-way</span> |
|
||||
| `offsetWidth` `offsetHeight` `clientWidth` `clientHeight` | All block-level elements | <span>One-way</span> |
|
||||
| `scrollX` `scrollY` | `<svelte:window>` | <span>Two-way</span> |
|
||||
| `online` `innerWidth` `innerHeight` `outerWidth` `outerHeight` | `<svelte:window>` | <span>One-way</span> |
|
||||
|
||||
> 'group' bindings allow you to capture the current value of a [set of radio inputs](repl?demo=binding-input-radio), or all the selected values of a [set of checkbox inputs](repl?demo=binding-input-checkbox-group).
|
||||
|
||||
Here is a complete example of using two way bindings with a form:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Form bindings' } -->
|
||||
<form on:submit="handleSubmit(event)">
|
||||
<input bind:value=name type=text>
|
||||
<button type=submit>Say hello</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleSubmit(event) {
|
||||
// prevent the page from reloading
|
||||
event.preventDefault();
|
||||
|
||||
const { name } = this.get();
|
||||
alert(`Hello ${name}!`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
name: "world"
|
||||
}
|
||||
```
|
||||
|
||||
> 'two way' bindings allow you to update a value in a nested property as seen in [checkbox input](repl?demo=binding-input-checkbox).
|
||||
|
||||
### Actions
|
||||
|
||||
Actions let you decorate elements with additional functionality. Actions are functions which may return an object with lifecycle methods, `update` and `destroy`. The action will be called when its element is added to the DOM.
|
||||
|
||||
Use actions for things like:
|
||||
* tooltips
|
||||
* lazy loading images as the page is scrolled, e.g. `<img use:lazyload data-src='giant-photo.jpg'/>`
|
||||
* capturing link clicks for your client router
|
||||
* adding drag and drop
|
||||
|
||||
```html
|
||||
<!-- { title: 'Actions' } -->
|
||||
<button on:click="toggleLanguage()" use:tooltip="translations[language].tooltip">
|
||||
{language}
|
||||
</button>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.textContent = text;
|
||||
|
||||
Object.assign(tooltip.style, {
|
||||
position: 'absolute',
|
||||
background: 'black',
|
||||
color: 'white',
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '12px',
|
||||
pointerEvents: 'none',
|
||||
transform: 'translate(5px, -50%)',
|
||||
borderRadius: '2px',
|
||||
transition: 'opacity 0.4s'
|
||||
});
|
||||
|
||||
function position() {
|
||||
const { top, right, bottom } = node.getBoundingClientRect();
|
||||
tooltip.style.top = `${(top + bottom) / 2}px`;
|
||||
tooltip.style.left = `${right}px`;
|
||||
}
|
||||
|
||||
function append() {
|
||||
document.body.appendChild(tooltip);
|
||||
tooltip.style.opacity = 0;
|
||||
setTimeout(() => tooltip.style.opacity = 1);
|
||||
position();
|
||||
}
|
||||
|
||||
function remove() {
|
||||
tooltip.remove();
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', append);
|
||||
node.addEventListener('mouseleave', remove);
|
||||
|
||||
return {
|
||||
update(text) {
|
||||
tooltip.textContent = text;
|
||||
position();
|
||||
},
|
||||
|
||||
destroy() {
|
||||
tooltip.remove();
|
||||
node.removeEventListener('mouseenter', append);
|
||||
node.removeEventListener('mouseleave', remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleLanguage() {
|
||||
const { language } = this.get();
|
||||
|
||||
this.set({
|
||||
language: language === 'english' ? 'latin' : 'english'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
language: "english",
|
||||
translations: {
|
||||
english: {
|
||||
tooltip: "Switch Languages",
|
||||
},
|
||||
latin: {
|
||||
tooltip: "Itchsway Anguageslay",
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Classes
|
||||
|
||||
Classes let you toggle element classes on and off. To use classes add the directive `class` followed by a colon and the class name you want toggled (`class:the-class-name="anExpression"`). The expression inside the directive's quotes will be evaluated and toggle the class on and off depending on the truthiness of the expression's result. You can only add class directives to elements.
|
||||
|
||||
This example adds the class `active` to `<li>` elements when the `url` property matches the path their links target.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Classes' } -->
|
||||
<ul class="links">
|
||||
<li class:active="url === '/'"><a href="/" on:click="goto(event)">Home</a></li>
|
||||
<li class:active="url.startsWith('/blog')"><a href="/blog/" on:click="goto(event)">Blog</a></li>
|
||||
<li class:active="url.startsWith('/about')"><a href="/about/" on:click="goto(event)">About</a></li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goto(event) {
|
||||
event.preventDefault();
|
||||
this.set({ url: event.target.pathname });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.links {
|
||||
list-style: none;
|
||||
}
|
||||
.links li {
|
||||
float: left;
|
||||
padding: 10px;
|
||||
}
|
||||
/* classes added this way are processed with encapsulated styles, no need for :global() */
|
||||
.active {
|
||||
background: #eee;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
"url": "/"
|
||||
}
|
||||
```
|
||||
|
||||
Classes will work with an existing class attribute on your element. If you find yourself adding multiple ternary statements inside a class attribute, classes can simplify your component. Classes are recognized by the compiler and <a href="guide#scoped-styles">scoped correctly</a>.
|
||||
|
||||
If your class name is the same as a property in your component's state, you can use the shorthand of a class binding which drops the expression (`class:myProp`).
|
||||
|
||||
Note that class names with dashes in them do not usually make good shorthand classes since the property will also need a dash in it. The example below uses a computed property to make working with this easier, but it may be easier to not use the shorthand in cases like this.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Classes shorthand' } -->
|
||||
<div class:active class:is-selected class:isAdmin>
|
||||
<p>Active? {active}</p>
|
||||
<p>Selected? {isSelected}</p>
|
||||
</div>
|
||||
<button on:click="set({ active: !active })">Toggle Active</button>
|
||||
<button on:click="set({ isSelected: !isSelected })">Toggle Selected</button>
|
||||
<button on:click="set({ isAdmin: !isAdmin })">Toggle Admin</button>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
// Because shorthand relfects the var name, you must use component.set({ "is-selected": true }) or use a computed
|
||||
// property like this. It might be better to avoid shorthand for class names which are not valid variable names.
|
||||
"is-selected": ({ isSelected }) => isSelected
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div {
|
||||
width: 300px;
|
||||
border: 1px solid #ccc;
|
||||
background: #eee;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.active {
|
||||
background: #fff;
|
||||
}
|
||||
.is-selected {
|
||||
border-color: #99bbff;
|
||||
box-shadow: 0 0 6px #99bbff;
|
||||
}
|
||||
.isAdmin {
|
||||
outline: 2px solid red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
"active": true,
|
||||
"isSelected": false,
|
||||
"isAdmin": false,
|
||||
}
|
||||
```
|
@ -1,99 +0,0 @@
|
||||
---
|
||||
title: Plugins
|
||||
---
|
||||
|
||||
Svelte can be extended with plugins and extra methods.
|
||||
|
||||
### Transition plugins
|
||||
|
||||
The [svelte-transitions](https://github.com/sveltejs/svelte-transitions) package includes a selection of officially supported transition plugins, such as [fade](https://github.com/sveltejs/svelte-transitions-fade), [fly](https://github.com/sveltejs/svelte-transitions-fly) and [slide](https://github.com/sveltejs/svelte-transitions-slide). You can include them in a component like so:
|
||||
|
||||
```html
|
||||
<!-- { title: 'svelte-transitions' } -->
|
||||
<label>
|
||||
<input type=checkbox bind:checked=visible> visible
|
||||
</label>
|
||||
|
||||
{#if visible}
|
||||
<!-- use `in`, `out`, or `transition` (bidirectional) -->
|
||||
<div transition:fly="{y:20}">hello!</div>
|
||||
{/if}
|
||||
|
||||
<script>
|
||||
import { fly } from 'svelte-transitions';
|
||||
|
||||
export default {
|
||||
transitions: { fly }
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
visible: true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Extra methods
|
||||
|
||||
The [svelte-extras](https://github.com/sveltejs/svelte-extras) package includes a handful of methods for tweening (animating), manipulating arrays and so on.
|
||||
|
||||
```html
|
||||
<!-- { title: 'svelte-extras' } -->
|
||||
<input bind:value=newTodo placeholder="buy milk">
|
||||
<button on:click="push('todos', newTodo)">add todo</button>
|
||||
|
||||
<ul>
|
||||
{#each todos as todo, i}
|
||||
<li>
|
||||
<button on:click="splice('todos', i, 1)">x</button>
|
||||
{todo}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li button {
|
||||
color: rgb(200,0,0);
|
||||
background: rgba(200,0,0,0.1);
|
||||
border-color: rgba(200,0,0,0.2);
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { push, splice } from 'svelte-extras';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
newTodo: '',
|
||||
todos: []
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
push,
|
||||
splice
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
todos: [
|
||||
"wash the car",
|
||||
"take the dog for a walk",
|
||||
"mow the lawn"
|
||||
]
|
||||
}
|
||||
```
|
@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Transitions
|
||||
---
|
||||
|
||||
|
||||
### Transitions
|
||||
|
||||
Transitions allow elements to enter and leave the DOM gracefully, rather than suddenly appearing and disappearing.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transitions' } -->
|
||||
<script>
|
||||
import { fade } from 'svelte/transition.js';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fade>fades in and out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Transitions can have parameters — typically `delay` and `duration`, but often others, depending on the transition in question. For example, here's the `fly` transition from the [svelte-transitions](https://github.com/sveltejs/svelte-transitions) package:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transition with parameters' } -->
|
||||
<script>
|
||||
import { fly } from 'svelte-transitions';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fly="{{y: 200, duration: 1000}}">flies 200 pixels up, slowly</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
An element can have separate `in` and `out` transitions:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transition in/out' } -->
|
||||
<script>
|
||||
import { fade, fly } from 'svelte-transitions';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p in:fly="{y: 50}" out:fade>flies up, fades out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Transitions are simple functions that take a `node` and any provided `parameters` and return an object with the following properties:
|
||||
|
||||
* `duration` — how long the transition takes in milliseconds
|
||||
* `delay` — milliseconds before the transition starts
|
||||
* `easing` — an [easing function](https://github.com/rollup/eases-jsnext)
|
||||
* `css` — a function that accepts an argument `t` between 0 and 1 and returns the styles that should be applied at that moment
|
||||
* `tick` — a function that will be called on every frame, with the same `t` argument, while the transition is in progress
|
||||
|
||||
Of these, `duration` is required, as is *either* `css` or `tick`. The rest are optional. Here's how the `fade` transition is implemented, for example:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Fade transition' } -->
|
||||
<script>
|
||||
function fade(node, { delay = 0, duration = 400 }) {
|
||||
const o = +getComputedStyle(node).opacity;
|
||||
|
||||
return {
|
||||
delay,
|
||||
duration,
|
||||
css: t => `opacity: ${t * o}`
|
||||
};
|
||||
}
|
||||
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fade>fades in and out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
> If the `css` option is used, Svelte will create a CSS animation that runs efficiently off the main thread. Therefore if you can achieve an effect using `css` rather than `tick`, you should.
|
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Actions
|
||||
---
|
||||
|
||||
|
||||
### Actions
|
||||
|
||||
Actions let you decorate elements with additional functionality. Actions are functions which may return an object with lifecycle methods, `update` and `destroy`. The action will be called when its element is added to the DOM.
|
||||
|
||||
Use actions for things like:
|
||||
|
||||
* tooltips
|
||||
* lazy loading images as the page is scrolled, e.g. `<img use:lazyload data-src='giant-photo.jpg'/>`
|
||||
* capturing link clicks for your client router
|
||||
* adding drag and drop
|
||||
|
||||
```html
|
||||
<!-- { title: 'Actions' } -->
|
||||
<button on:click={toggleLanguage} use:tooltip={translations[language].tooltip}>
|
||||
{language}
|
||||
</button>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.textContent = text;
|
||||
|
||||
Object.assign(tooltip.style, {
|
||||
position: 'absolute',
|
||||
background: 'black',
|
||||
color: 'white',
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '12px',
|
||||
pointerEvents: 'none',
|
||||
transform: 'translate(5px, -50%)',
|
||||
borderRadius: '2px',
|
||||
transition: 'opacity 0.4s'
|
||||
});
|
||||
|
||||
function position() {
|
||||
const { top, right, bottom } = node.getBoundingClientRect();
|
||||
tooltip.style.top = `${(top + bottom) / 2}px`;
|
||||
tooltip.style.left = `${right}px`;
|
||||
}
|
||||
|
||||
function append() {
|
||||
document.body.appendChild(tooltip);
|
||||
tooltip.style.opacity = 0;
|
||||
setTimeout(() => tooltip.style.opacity = 1);
|
||||
position();
|
||||
}
|
||||
|
||||
function remove() {
|
||||
tooltip.remove();
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', append);
|
||||
node.addEventListener('mouseleave', remove);
|
||||
|
||||
return {
|
||||
update(text) {
|
||||
tooltip.textContent = text;
|
||||
position();
|
||||
},
|
||||
|
||||
destroy() {
|
||||
tooltip.remove();
|
||||
node.removeEventListener('mouseenter', append);
|
||||
node.removeEventListener('mouseleave', remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleLanguage() {
|
||||
const { language } = this.get();
|
||||
|
||||
this.set({
|
||||
language: language === 'english' ? 'latin' : 'english'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
language: "english",
|
||||
translations: {
|
||||
english: {
|
||||
tooltip: "Switch Languages",
|
||||
},
|
||||
latin: {
|
||||
tooltip: "Itchsway Anguageslay",
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,70 @@
|
||||
---
|
||||
title: Classes
|
||||
---
|
||||
|
||||
Like any attribute, the `class` attribute can be set using regular JavaScript. Suppose we had an `active` class that we wanted to apply to an element when `isActive` is true — we could do it like this:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Dynamic classes using ternaries' } -->
|
||||
<script>
|
||||
let isActive = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1 class="{isActive ? 'active' : ''}">red if active</h1>
|
||||
|
||||
<label>
|
||||
<input type=checkbox bind:checked={isActive}> isActive
|
||||
</label>
|
||||
```
|
||||
|
||||
That's a little verbose though, so the `class:` directive gives you a simpler way to achieve the same thing:
|
||||
|
||||
```diff
|
||||
<!-- { title: 'Dynamic classes using directives' } -->
|
||||
<script>
|
||||
let isActive = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
-<h1 class="{isActive ? 'active' : ''}">red if active</h1>
|
||||
+<h1 class:active={isActive}>red if active</h1>
|
||||
|
||||
<label>
|
||||
<input type=checkbox bind:checked={isActive}> isActive
|
||||
</label>
|
||||
```
|
||||
|
||||
As with any directive, you can use any JavaScript expression. If it's a variable name that matches the class name, you can use a shorthand:
|
||||
|
||||
```diff
|
||||
<!-- { title: 'Dynamic classes using directives' } -->
|
||||
<script>
|
||||
- let isActive = false;
|
||||
+ let active = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
-<h1 class:active={isActive}>red if active</h1>
|
||||
+<h1 class:active>red if active</h1>
|
||||
|
||||
<label>
|
||||
- <input type=checkbox bind:checked={isActive}> isActive
|
||||
+ <input type=checkbox bind:checked={active}> active
|
||||
</label>
|
||||
```
|
Loading…
Reference in new issue