Web-Apps with Svelte

A Practical Introduction

The Trainer

 

  • Nils Röhrig

 

  • Software Engineer @ Loql

 

  • Frontend focus

 

  • Svelte user since 2019

 

  • Speaker at conferences and meetups

Agenda

Part II

- Nesting

- Props

- Component Slots

- Event Dispatching

- Event Forwarding

- Events vs. Callbacks

Part I

- Framework

- Local State

- Directives

- Control Structures

- CSS Support

- SVG Support

- Reactivity

Part III

- Svelte Stores

- Custom  Stores

- Svelte Context

- Stores in Context

Part IV

- Working with Promises

- Transitions

- Svelte Actions

- DOM References

Coffee Break
10:00 - 10:30

Lunch Break
12:00 - 13:00

Coffee Break
14:30 - 15:00

Agenda

Teil II

- Verschachtelung

- Props

- Komponenten- Slots

- Event Dispatching

- Event Forwarding

- Events oder Callbacks

Teil I

- Framework

- Lokaler State

- Direktiven

- Kontrollstrukturen

- CSS Support

- SVG Support

- Reaktivität

Teil III

- Svelte Stores

- Custom  Stores

- Svelte Kontext

- Stores im Kontext

Teil IV

- Arbeit mit Promises

- Transitionen

- Svelte Actions

- DOM-Referenzen

Kaffepause
10:30 - 11:00

Mittagspause
12:30 - 13:30

Kaffepause
15:00 - 15:30

Part I

Teil I

- Framework

- Local State

- Directives

- Control Structures

- CSS Support

- SVG Support

- Reactivity

Svelte is a tool for building web applications. Like other user interface frameworks, it allows you to build your app declaratively out of components that combine markup, styles and behaviours.

Svelte is a component framework

Svelte is a component framework

Logo
Register
Home
E-Mail
Submit

etc. pp...

Component

Component

Component

These components are compiled into small, efficient JavaScript modules that eliminate overhead traditionally associated with UI frameworks.

Svelte is a compiler

Svelte is a compiler

<h1>Simple Component</h1>

Component.svelte

Svelte is a compiler

<h1>Simple Component</h1>

Svelte is a compiler

<h1>Simple Component</h1>

Svelte is a compiler

<h1>Simple Component</h1>
import { SvelteComponent, detach, element, init,
         insert, noop, safe_not_equal } from "svelte/internal";

function create_fragment(ctx) {
  let h1;

  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Simple Component";
    },
    m(target, anchor) {
      insert(target, h1, anchor);
    },
    p: noop,
    i: noop,
    o: noop,
    d(detaching) {
      if (detaching) detach(h1);
    },
  };
}

class SimpleComponent extends SvelteComponent {
  constructor(options) {
    super();
    init(this, options, null, create_fragment, safe_not_equal, {});
  }
}

export default SimpleComponent;

Svelte is a compiler

<h1>Simple Component</h1>
import { [...] } from "svelte/internal";
function create_fragment(ctx) {
  let h1;  
  return {
    c() {
      h1 = element("h1");
      h1.textContent = "Simple Component";
    },
    m(target, anchor) {
      insert(target, h1, anchor);
    },
    p: noop, i: noop, o: noop,
    d(detaching) { if (detaching) detach(h1); },
  };
}
class Component extends SvelteComponent {
  constructor(options) {
    super();
    init(this, options, null, create_fragment, 
		safe_not_equal, {});
  }
}
export default Component;

Component.js

Svelte is a compiler

<h1>Simple Component</h1>
import Component from "./Component.js";

const app = new Component({
  target: document.body,
});

export default app;

main.js

Anatomy of a component

Anatomy of a component

<script>
  // STATE & BEHAVIOR
</script>

<!-- DECLARATIVE MARKUP -->
<h1>Hello enterJS!</h1>
<p>{"Put your JS expressions in curly braces."}</p>

<style>
  /* PRESENTATION */
</style>

Component.svelte

Local State

Local State

  • The local component state in Svelte consists of local variables.
     
  • It describes the state of the component and is not shared with other components.
     
  • The local state is reactive in the Svelte sense, meaning that only the affected parts of the component are updated when changes occur.

Directives

Directives

  • Svelte directives can be used as attributes in Svelte elements.
     
  • They can control the behavior of elements, such as binding data or handling events.
     
  • Svelte provides various directives that can control the behavior of elements, such as on: and bind:.

Control Structures

Control Structures

  • Control structures are an integral part of Svelte markup.
     
  • They control the program flow and are known from other programming languages.
     
  • Svelte provides a block syntax for control structures that supports various expressions like if, else, each, and others.

Changing the value of an {#each} block

Changing the value of an {#each} block

  • If the value of an {#each} block in Svelte is changed, elements are removed or added only at the end of the block.
     
  • Changed elements are updated at the same position in the DOM.
     
  • Using unique IDs can avoid unwanted effects if rendered elements are not directly or reactively dependent on the change of the value.

Keyed Each Block

Local Constants

Reactivity

Reactivity

  • Svelte integrates reactivity as a core element.
     
  • The = operator and its variants are used in Svelte to establish data binding between variables and UI elements.
     
  • Svelte tracks dependencies between variables and only updates the parts of the DOM that are affected by them.
     
  • By tracking assignments at build time, the code is optimized.
     
  • Svelte provides language support such as Reactive Statements.

CSS Support

CSS Support

  • CSS is part of the Single-File-Component approach in Svelte.
     
  • Styles are automatically scoped to avoid conflicts.
     
  • Directives allow for binding of classes.
     
  • There are also directives for selectively setting individual CSS properties.

SVG Support

SVG Support

  • Svelte offers full support for SVG graphics.
     
  • SVG elements can be directly included in Svelte components.
     
  • Svelte allows for dynamically updating SVG attributes and properties.

Live Coding

Exercise

Exercise

  1. Add filters for transactions:
    • All time: All transactions
    • Monthly: Current month
    • Weekly: Current calendar week
  2. The filtering should be reflected in
    • the StatCards
    • the transaction table

 

Notes

  • The implementation method is freely selectable
  • Modules in src/lib can be used

Tasks

git checkout tags/part_1_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Preparation

Repository

Proposed Solution

Part II

Part II

- Nesting

- Props

- Component Slots

- Event Dispatching

- Event Forwarding

- Events vs. Callbacks

Nesting of components

Nesting of components

E-Mail
Submit
Register
Home
Logo

Nesting of components

E-Mail
Submit
Register
Home
Logo
Logo
Register
Home
E-Mail
Submit
Logo
Register
Home
E-Mail
Submit
Logo
Register
Home
E-Mail
Submit
E-Mail
Submit
Register
Home
Logo
Logo
Register
Home
E-Mail
Submit
Logo
Register
Home
E-Mail
Submit
E-Mail
Submit
Register
Home
Logo

<Logo>

<Header>

<App>

<NavItem>

<NavItem>

<NavBar>

<Label>

<Input>

<Form>

<Button>

Props

Props

Register
Home

Props

<NavItem>

<NavItem>

Register
Home

Props

<NavItem                          >

<NavItem                              >

label="Home"

label="Register"

Register
Home

Props

<script>
    const str = "a string";
    const num = 12345;
    const bool = true;
    const obj = {key: "value"};
    const arr = [1, 2, 3, 4, 5];
    
    function callback() {
        console.log("callback");
    }
</script>

<AnyComponent 
    stringProp={str}
    numberProp={num}
    booleanProp={bool}
    objectProp={obj}
    arrayProp={arr}
    {callback} 
/>

Props

<script>
    const str = "a string";
    const num = 12345;
    const bool = true;
    const obj = {key: "value"};
    const arr = [1, 2, 3, 4, 5];
    
    function callback() {
        console.log("callback");
    }
</script>

<AnyComponent 
    stringProp={str}
    numberProp={num}
    booleanProp={bool}
    objectProp={obj}
    arrayProp={arr}
    {callback} 
/>
<script>
  export let stringProp = "";
  export let numberProp = 0;
  export let booleanProp = true;
  export let objectProp = {key: "value"};
  export let arrayProp = [1, 2, 3];
  export let callback = () => undefined;
</script>

<p>{stringProp}</p>
<p>{numberProp}</p>
<p>{booleanProp}</p>

{#each Object.entries(objectProp) as [key, value]}
  <p>{key}: {value}</p>
{/each}

{#each arrayProp as value}
  <p>{value}</p>
{/each}

<button on:click={callback}>Call back</button>

$$props & $$restProps

Event Dispatching

Event Dispatching

E-Mail
Submit
Register
Home
Logo

Event Dispatching

E-Mail
Submit
Logo
Register
Home
E-Mail
Submit

<App>

<Form>

Event Dispatching

E-Mail
Submit
Logo
Register
Home
E-Mail
Submit

<App>

<Form>

How?

Event Dispatching

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  async function submitForm() {
    await postForm();
    dispatch("form:posted", { /* any information to share */ });
  }
</script>

<form on:submit={submitForm}>
  <!-- form content -->
</form>

Form.svelte

Event Dispatching

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  async function submitForm() {
    await postForm();
    dispatch("form:posted", { /* any information to share */ }
  }
</script>

<form on:submit={submitForm}>
  <!-- form content -->
</form>

Form.svelte

<script>
  import Form from './Form.svelte';
  
  function handleSubmit(event) {
    
    /* here the shared information can be found, 
     * as Svelte events are just instances of 
     * CustomEvent */
    const { detail } = event;
    console.log({detail});
  }
</script>

<Form on:form:posted={handleSubmit} />

App.svelte

Event Forwarding

Event Forwarding

  • Event forwarding forwards events from child components to parent components.
     
  • This allows parent components to react to user actions in their child components.
     
  • It promotes coordination of data and actions across the component hierarchy.

Event Forwarding

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  async function submitForm() {
    await postForm();
    dispatch("submit", { /* any information to share */ }
  }
</script>

<form on:submit={submitForm}>
  <!-- form content -->
</form>

Form.svelte

<script>
  import Form from './Form.svelte';
  
  function handleSubmit(event) {    
    /* here the shared information can be found, 
     * as Svelte events are just instances of 
     * CustomEvent */
    const { detail } = event;
    console.log({detail});
  }
</script>

<Form on:submit={handleSubmit} />

App.svelte

Event Forwarding

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  async function submitForm() {
    await postForm();
    dispatch("submit", { /* any information to share */ }
  }
</script>

<form on:submit={submitForm}>
  <!-- form content -->
</form>

Form.svelte

<script>
  import Form from './Form.svelte';
  
  function handleSubmit(event) {    
    /* here the shared information can be found, 
     * as Svelte events are just instances of 
     * CustomEvent */
    const { detail } = event;
    console.log({detail});
  }
</script>

<Form on:submit={handleSubmit} />

App.svelte

Event Forwarding

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();
  
  async function submitForm() {
    await postForm();
    dispatch("submit", { /* any information to share */ }
  }
</script>

<form on:submit={submitForm}>
  <!-- form content -->
</form>

Form.svelte

<script>
  import Form from './Form.svelte';
  
  function handleSubmit(event) {    
    /* here the shared information can be found, 
     * as Svelte events are just instances of 
     * CustomEvent */
    const { detail } = event;
    console.log({detail});
  }
</script>

<Form on:submit={handleSubmit} />

App.svelte

Event Forwarding

<form on:submit>
  <!-- form content -->
</form>

Form.svelte

<script>
  import Form from './Form.svelte';
  
  async function handleSubmit(event) {
    const data = new FormData(event.target);
    await postData(data);
  }
</script>

<Form on:submit={handleSubmit} />

App.svelte

Events vs. Callbacks

Events vs. Callbacks

<script>
  import AnotherComponent from './AnotherComponent.svelte';
  
  function callback() {
    console.log("I am a callback. Arguments: ", ...arguments);
  }
</script>

<AnotherComponent {callback} />
<script>
  export let callback;
</script>

<button on:click={callback}>
  Click me!
</button>

Component.svelte

AnotherComponent.svelte

Events vs. Callbacks

<script>
  import Button from './Button.svelte';
  
  function handleButtonElementClick() {
    console.log("Button element clicked.");
  }
  
  function handleButtonComponentClick() {
    console.log("Button component clicked.");
  }
</script>

<button on:click={handleButtonElementClick}>Button element</button>
<Button onClick={handleButtonComponentClick}>Button Component</Button>

Component Slots

Component Slots

  • Slots in Svelte allow for the dynamic injection of content into components.
     
  • They are defined in components using the <slot> element.
     
  • Slot contents are provided by parent components.
     
  • Slot contents can contain any valid Svelte markup.

Component Slots

Dialog

Text that is shown in the dialog. Potentially expanded by further content.

Okay

Component Slots

<strong>Dialog</strong>

<p>Text that is shown in the dialog. Potentially expanded by further content.</p>

<button>Okay</button>

Live Coding

Exercise

Exercise

  1. Create a detail page for transactions
     
  2. The detail page should contain all information about a transaction
     
  3. On each detail page, the respective transaction can be deleted

 

Notes

  • The implementation method is freely selectable
     
  • The example app uses modified StatCards

Tasks

Repository

git checkout tags/part_2_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Preparation

Proposed Solution

Part III

Part III

- Svelte Stores

- Custom  Stores

- Svelte Context

- Stores in Context

Svelte Stores

Svelte Stores

  • Stores are JavaScript objects that encapsulate values and can be subscribed to using the .subscribe method.
     
  • Subscribers are notified of updates.
     
  • Svelte provides language support for using stores.
     
  • There are different types of built-in stores, namely readable, writable, and derived.

Svelte Store Contract

Svelte Store Contract

  • A Store must contain a .subscribe method that accepts a subscription function as an argument to create a subscription.
     
  • The subscription function must be called immediately and synchronously with the current value of the Store when .subscribe is called.
     
  • All active subscription functions of the Store must be called synchronously later when the value of the Store changes.
     
  • .subscribe must return an unsubscribe function. Calling it must end the subscription and the corresponding subscription function must not be called again by the Store.
     
  • A store can optionally include a .set method, which accepts a new value as an argument and updates the store with it. Such a store is referred to as a writable store.

Custom Stores

Svelte Context

Svelte Context

  • The context is a key-value store that enables data transfer from a component to all of its hierarchically subordinate components without explicit props.
     
  • setContext() is used to create and provide the context in the parent component.
     
  • getContext() is used to retrieve the context in the child components.
     
  • The context can contain arbitrary values, such as configuration data, functions, or stores.
     
  • The context is not reactive in the Svelte sense.

Stores in Context

Live Coding

Exercise

Exercise

  1. Create a mechanism for synchronizing transactions in the browser's local storage


Notes

  • The type of implementation is up to you
     
  • Stores can be subscribed to

Tasks

Repository

git checkout tags/part_3_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Preparation

Proposed Solution

Part IV

Part IV

- Working with Promises

- Transitions

- Svelte Actions

- DOM References

Working with Promises

Working with Promises

Render Component

Fetch Data

Update View

Arbeit mit Promises

Render Component

Fetch Data

Update View

Transitions

Transitions

  • Transitions in Svelte allow for animated transitions when adding or removing DOM elements.
     
  • They are used through the transition directive and allow for the use of built-in or custom transitions.
     
  • Svelte already includes transition effects such as fade, slide, or scale.
     
  • Custom function effects follow a unified interface, which can be found in the Svelte documentation.

Transitionen

Refs & Actions

Refs & Actions

  • Refs are a way to reference DOM elements.
     
  • They are created using the bind:this={variable} directive and can be used and shared like other variables.
     
  • Actions are functions that can be directly attached to DOM elements without creating an explicit reference.
     
  • They are used with the use:actionName={params} directive.
     
  • Actions are executed when added to the DOM.
     
  • They can optionally return a function that is executed when removed from the DOM.

Live Coding

Übung

Exercise

  1. Create a visualization for income and expenses using Chart.js


Notes

  • Chart.js is already listed in the dependencies
     
  • Use Svelte Actions to bring visualizations into the DOM
     
  • Chart type, implementation method, etc. are freely selectable

Tasks

Repository

git checkout tags/part_4_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Preparation

LinkedIn:

Xing:

X:

Thank You!

Web-Apps with Svelte - A Practical Introduction (iJS Munich 2023)

By Nils Röhrig

Web-Apps with Svelte - A Practical Introduction (iJS Munich 2023)

  • 501