more tutorial content

pull/2143/head
Richard Harris 7 years ago
parent 4f6ffc4612
commit 991e46422b

@ -12,6 +12,8 @@ First, you'll need to integrate Svelte with a build tool. Popular choices are:
Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process. Don't worry if you're relatively new to web development and haven't used these tools before. We've prepared a simple step-by-step guide, [Svelte for new developers](blog/svelte-for-new-developers), which walks you through the process.
You'll also want to configure your text editor to treat `.svelte` files the same as `.html` for the sake of syntax highlighting. [Read this guide to learn how](blog/setting-up-your-editor).
Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`: Then, once you've got your project set up, using Svelte components is easy. The compiler turns each component into a regular JavaScript class — just import it and instantiate with `new`:
```js ```js

@ -15,3 +15,5 @@ If you need to loop over lists of data, use an `each` block:
> The expression (`cats`, in this case) can be any array or array-like object (i.e. it has a `length` property). You can loop over generic iterables with `each [...iterable]`. > The expression (`cats`, in this case) can be any array or array-like object (i.e. it has a `length` property). You can loop over generic iterables with `each [...iterable]`.
If you prefer, you can use destructuring — `each cats as { id, name }` — and replace `cat.id` and `cat.name` with `id` and `name`. If you prefer, you can use destructuring — `each cats as { id, name }` — and replace `cat.id` and `cat.name` with `id` and `name`.
> In some cases, you will need to use *keyed each blocks*. We'll learn about those [later](tutorial/keyed-each-blocks).

@ -0,0 +1,5 @@
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42}/>

@ -0,0 +1,5 @@
<script>
let answer;
</script>
<p>The answer is {answer}</p>

@ -0,0 +1,5 @@
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42}/>

@ -0,0 +1,5 @@
<script>
export let answer;
</script>
<p>The answer is {answer}</p>

@ -0,0 +1,15 @@
---
title: Declaring props
---
So far, we've dealt exclusively with internal state — that is to say, the values are only accessible within a given component.
In any real application, you'll need to pass data from one component down to its children. To do that, we need to declare *properties*, generally shortened to 'props'. In Svelte, we do that with the `export` keyword. Edit the `Nested.svelte` component:
```html
<script>
export let answer;
</script>
```
> Just like `$:`, this may feel a little weird at first. That's not how `export` normally works in JavaScript modules! Just roll with it for now — it'll soon become second nature.

@ -0,0 +1,5 @@
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42}/>

@ -0,0 +1,5 @@
<script>
export let answer;
</script>
<p>The answer is {answer}</p>

@ -0,0 +1,6 @@
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42}/>
<Nested/>

@ -0,0 +1,5 @@
<script>
export let answer = 'a mystery';
</script>
<p>The answer is {answer}</p>

@ -0,0 +1,18 @@
---
title: Default values
---
We can easily specify default values for props:
```html
<script>
export let answer = 'a mystery';
</script>
```
If we now instantiate the component without an `answer` prop, it will fall back to the default:
```html
<Nested answer={42}/>
<Nested/>
```

@ -0,0 +1,16 @@
<script>
let m = { x: 0, y: 0 };
function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<div>
The mouse position is {m.x} x {m.y}
</div>

@ -0,0 +1,16 @@
<script>
let m = { x: 0, y: 0 };
function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>

@ -0,0 +1,11 @@
---
title: DOM events
---
As we briefly saw in an [earlier chapter](tutorial/reactive-assignments), you can listen to any event on an element with the `on:` directive:
```html
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>
```

@ -0,0 +1,16 @@
<script>
let m = { x: 0, y: 0 };
function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>

@ -0,0 +1,11 @@
<script>
let m = { x: 0, y: 0 };
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>

@ -0,0 +1,15 @@
---
title: Inline handlers
---
You can also declare event handlers inline:
```html
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>
```
The quote marks are optional, but they're helpful for syntax highlighting in some environments.
> In some frameworks you may see recommendations to avoid inline event handlers for performance reasons, particularly inside loops. That advice doesn't apply to Svelte — the compiler will always do the right thing, whichever form you choose.

@ -0,0 +1,9 @@
<script>
function handleClick() {
alert('clicked')
}
</script>
<button on:click={handleClick}>
Click me
</button>

@ -0,0 +1,9 @@
<script>
function handleClick() {
alert('no more alerts')
}
</script>
<button on:click|once={handleClick}>
Click me
</button>

@ -0,0 +1,27 @@
---
title: Event modifiers
---
DOM event handlers can have *modifiers* that alter their behaviour. For example, a handler with a `once` modifier will only run a single time:
```html
<script>
function handleClick() {
alert('no more alerts')
}
</script>
<button on:click|once={handleClick}>
Click me
</button>
```
The full list of modifiers:
* `preventDefault` — calls `event.preventDefault()` before running the handler. Useful for e.g. client-side form handling
* `stopPropagation` — calls `event.stopPropagation()`, preventing the event reaching the next element
* `passive` — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
* `capture` — fires the handler during the *capture* phase instead of the *bubbling* phase
* `once`
You can chain modifiers together, e.g. `on:click|once|capture={...}`.

@ -0,0 +1,9 @@
<script>
import Inner from './Inner.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>

@ -0,0 +1,11 @@
<script>
// setup code goes here
function sayHello() {
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>

@ -0,0 +1,9 @@
<script>
import Inner from './Inner.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>

@ -0,0 +1,15 @@
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>

@ -0,0 +1,21 @@
---
title: Component events
---
Components can also dispatch events. To do so, they must create an event dispatcher. Update `Inner.svelte`:
```html
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
```
> `createEventDispatcher` must be called when the component is first instantiated — you can't do it later inside e.g. a `setTimeout` callback. This links `dispatch` to the component instance.

@ -0,0 +1,9 @@
<script>
import Outer from './Outer.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Outer on:message={handleMessage}/>

@ -0,0 +1,15 @@
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>

@ -0,0 +1,5 @@
<script>
import Inner from './Inner.svelte';
</script>
<Inner/>

@ -0,0 +1,9 @@
<script>
import Outer from './Outer.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Outer on:message={handleMessage}/>

@ -0,0 +1,15 @@
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>

@ -0,0 +1,5 @@
<script>
import Inner from './Inner.svelte';
</script>
<Inner on:message/>

@ -0,0 +1,34 @@
---
title: Event forwarding
---
Unlike DOM events, component events don't *bubble*. If you want to listen to an event on some deeply nested component, the intermediate components must *forward* the event.
In this case, we have the same `App.svelte` and `Inner.svelte` as in the [previous chapter](tutorial/component-events), but there's now an `Outer.svelte` component that contains `<Inner/>`.
One way we could solve the problem is adding `createEventDispatcher` to `Outer.svelte`, listening for the `message` event, and creating a handler for it:
```html
<script>
import Inner from './Inner.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function forward(event) {
dispatch('message', event.detail);
}
</script>
<Inner on:message={forward}/>
```
But that's a lot of code to write, so Svelte gives us an equivalent shorthand — an `on:message` event directive without a value means 'forward all `message` events'.
```html
<script>
import Inner from './Inner.svelte';
</script>
<Inner on:message/>
```

@ -0,0 +1,9 @@
<script>
import FancyButton from './FancyButton.svelte';
function handleClick() {
alert('clicked');
}
</script>
<FancyButton on:click={handleClick}/>

@ -0,0 +1,15 @@
<style>
button {
font-family: 'Comic Sans MS';
font-size: 2em;
padding: 0.5em 1em;
color: royalblue;
background: gold;
border-radius: 1em;
box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
<button>
Click me
</button>

@ -0,0 +1,9 @@
<script>
import FancyButton from './FancyButton.svelte';
function handleClick() {
alert('clicked');
}
</script>
<FancyButton on:click={handleClick}/>

@ -0,0 +1,15 @@
<style>
button {
font-family: 'Comic Sans MS';
font-size: 2em;
padding: 0.5em 1em;
color: royalblue;
background: gold;
border-radius: 1em;
box-shadow: 2px 2px 4px rgba(0,0,0,0.5);
}
</style>
<button on:click>
Click me
</button>

@ -0,0 +1,13 @@
---
title: DOM event forwarding
---
Event forwarding works for DOM events too.
We want to get notified of clicks on our `<FancyButton>` — to do that, we just need to forward `click` events on the `<button>` element in `FancyButton.svelte`:
```html
<button on:click>
Click me
</button>
```

@ -27,6 +27,8 @@ Outline (subject to change):
Side-quest: create a 'Svelte for new developers' blog post that assumes no knowledge beyond HTML, CSS and JS (i.e. CLI, Node and npm, degit, build tools) Side-quest: create a 'Svelte for new developers' blog post that assumes no knowledge beyond HTML, CSS and JS (i.e. CLI, Node and npm, degit, build tools)
Another one should cover how to set up an editor for syntax highlighting.
## Reactivity ## Reactivity
@ -39,23 +41,28 @@ Side-quest: create a 'Svelte for new developers' blog post that assumes no knowl
* [x] If blocks * [x] If blocks
* [x] Else/elseif blocks * [x] Else/elseif blocks
* [ ] Each blocks * [x] Each blocks
* [ ] Keyed each blocks (maybe? kind of need to cover transitions before we can make this obvious)
* [ ] Await blocks * [ ] Await blocks
* [ ] Keyed each blocks (maybe? kind of need to cover transitions before we can make this obvious)
## Props ## Props
* [ ] `export let foo` * [x] `export let foo`
* [ ] `export let foo = 1` * [x] `export let foo = 1`
* [ ] `export function foo(){...}` * [ ] `export function foo(){...}`
## Events ## Events
* [ ] `createEventDispatcher` and `dispatch` * [x] `on:blah`
* [ ] `on:blah` * [x] DOM event modifiers
* [ ] DOM event modifiers * [x] `createEventDispatcher` and `dispatch`
* [x] shorthand events
## Bindings ## Bindings
@ -96,6 +103,7 @@ Side-quest: create a 'Svelte for new developers' blog post that assumes no knowl
* [ ] Custom JS transitions * [ ] Custom JS transitions
* [ ] `in` * [ ] `in`
* [ ] `out` * [ ] `out`
* [ ] `on:introstart` etc
## Animations ## Animations
@ -133,3 +141,12 @@ Side-quest: create a 'Svelte for new developers' blog post that assumes no knowl
* Keyed each blocks * Keyed each blocks
* Debug tags * Debug tags
* `context="module"`
---
## Bugs etc
* 'Show me' should be greyed out if current state is identical to target state
* Clicking 'Show me' shouldn't change the file you're looking at

@ -284,7 +284,7 @@
</style> </style>
<div class="iframe-container"> <div class="iframe-container">
<iframe title="Result" bind:this={refs.child} sandbox="allow-scripts allow-popups allow-forms allow-pointer-lock allow-top-navigation allow-modals" class="{error || pending || pendingImports ? 'greyed-out' : ''}" srcdoc=' <iframe title="Result" bind:this={refs.child} sandbox="allow-popups-to-escape-sandbox allow-scripts allow-popups allow-forms allow-pointer-lock allow-top-navigation allow-modals" class="{error || pending || pendingImports ? 'greyed-out' : ''}" srcdoc='
<!doctype html> <!doctype html>
<html> <html>
<head> <head>

@ -80,7 +80,7 @@
margin: 0 calc(var(--side-nav) * -1); margin: 0 calc(var(--side-nav) * -1);
box-sizing: border-box; box-sizing: border-box;
display: grid; display: grid;
grid-template-columns: 400px 1fr; grid-template-columns: 1fr 2fr;
grid-auto-rows: 100%; grid-auto-rows: 100%;
} }
@ -115,6 +115,10 @@
color: white; color: white;
} }
.chapter-markup :global(a) {
text-decoration: underline;
}
.chapter-markup :global(ul) { .chapter-markup :global(ul) {
padding: 0 0 0 2em; padding: 0 0 0 2em;
} }
@ -173,8 +177,9 @@
background: rgba(255,255,255,0.2); background: rgba(255,255,255,0.2);
} }
.next { a.next {
float: right; float: right;
text-decoration: none;
} }
</style> </style>

Loading…
Cancel
Save