Web-Apps mit Svelte

Ein praktischer Einstieg

Der Trainer


  • Nils Röhrig


  • Software Engineer @ Loql


  • Frontend-Fokus


  • Svelte-Nutzer seit 2019


  • Speaker auf Konferenzen und Meetups

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

Teil I

Teil I

- Framework

- Lokaler State

- Direktiven

- Kontrollstrukturen

- CSS Support

- SVG Support

- Reaktivität

Svelte ist ein Komponenten-Framework

Svelte ist ein Komponenten-Framework

Logo
Register
Home
E-Mail
Submit

etc. pp...

Komponente

Komponente

Komponente

Svelte ist ein Compiler

Svelte ist ein Compiler

<h1>Simple Component</h1>

Component.svelte

Svelte ist ein Compiler

<h1>Simple Component</h1>

Svelte ist ein Compiler

<h1>Simple Component</h1>

Svelte ist ein 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 ist ein 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 ist ein Compiler

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

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

export default app;

main.js

Dateistruktur

Dateistruktur

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

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

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

Component.svelte

Lokaler State

Lokaler State

  • Der lokale Komponenten-State in Svelte besteht aus lokalen Variablen.
     
  • Er beschreibt den Zustand der Komponente und ist nicht mit anderen Komponenten geteilt.
     
  • Der lokale State ist reaktiv im Svelte-Sinne, d.h. bei Änderungen werden nur die betroffenen Teile der Komponente aktualisiert.

Direktiven

Direktiven

  • Svelte-Direktiven sind als Attribute in Svelte-Elementen verwendbar.
     
  • Sie können das Verhalten von Elementen steuern, z.B. durch das Binden von Daten oder das Behandeln von Ereignissen.
     
  • Svelte bietet verschiedene Direktiven an, die das Verhalten von Elementen steuern können, wie z.B. on: und bind:.

Kontrollstrukturen

Kontrollstrukturen

  • Kontrollstrukturen sind ein integraler Bestandteil des Svelte-Markups.
     
  • Sie regeln den Programmfluss und sind aus anderen Programmiersprachen bekannt.
     
  • Svelte bietet eine Block-Syntax für Kontrollstrukturen, die verschiedene Ausdrücke wie if, else, each, und andere unterstützt.

Veränderung des Wertes eines {#each}-Blocks

Veränderung des Wertes eines {#each}-Blocks

  • Wenn der Wert eines {#each}-Blocks in Svelte geändert wird, werden nur am Ende des Blocks Elemente entfernt oder hinzugefügt.
     
  • Geänderte Elemente werden an derselben Stelle im DOM aktualisiert.
     
  • Verwendung von eindeutigen IDs kann unerwünschte Effekte vermeiden, wenn gerenderte Elemente nicht direkt oder reaktiv von der Änderung des Werts abhängig sind.

Keyed Each Block

Lokale Konstanten

Reaktivität

Reaktivität

  • Svelte integriert Reaktivität als Kernelement.
     
  • Der =-Operator und seine Varianten werden in Svelte verwendet, um Datenbindung zwischen Variablen und UI-Elementen herzustellen.
     
  • Svelte verfolgt Abhängigkeiten zwischen Variablen und aktualisiert nur die Teile des DOM, die davon betroffen sind.
     
  • Durch Build-Time-Tracking der Zuweisungen wird der Code optimiert.
     
  • Svelte bietet Sprachunterstützung wie Reactive Statements.

CSS Support

CSS Support

  • CSS ist Teil des Single-File-Component-Ansatzes in Svelte.
     
  • Styles sind automatisch scopiert, um Konflikte zu vermeiden.
     
  • Direktiven ermöglichen das Binding von Klassen.
     
  • Es gibt auch Direktiven zum gezielten Setzen einzelner CSS-Properties.

SVG Support

SVG Support

  • Svelte bietet vollständigen Support für SVG-Grafiken.
     
  • SVG-Elemente können direkt in Svelte-Komponenten eingebunden werden.
     
  • Svelte ermöglicht das dynamische Aktualisieren von SVG-Attributen und -Eigenschaften.

Live Coding

Übung

Übung

  1. Füge Filter für Transaktionen hinzu:
    • All time: Alle Transaktionen
    • Monthly: Aktueller Monat
    • Weekly:  Aktuelle Kalenderwoche
  2. Die Filterung sollte sich widerspiegeln in
    • den StatCards
    • der Transaktionstabelle

 

Hinweise

  • Die Art der Implementierung ist frei wählbar
  • Module in src/lib können verwendet werden

Aufgaben

git checkout tags/part_1_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Vorbereitung

Repository

Lösungsvorschlag

Teil II

Teil II

- Verschachtelung

- Props

- Event Dispatching

- Event Forwarding

- Events oder Callbacks

- Komponenten- Slots

Verschachtelung von Komponenten

Verschachtelung von Komponenten

E-Mail
Submit
Register
Home
Logo

Verschachtelung von Komponenten

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>

Wie?

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 leitet Ereignisse von Kindkomponenten an übergeordnete Komponenten weiter.
     
  • Dadurch können Elternkomponenten auf Benutzeraktionen in ihren Kindkomponenten reagieren.
     
  • Es fördert die Koordination von Daten und Aktionen über die Komponentenhierarchie hinweg.

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>

Komponenten-Slots

Komponenten-Slots

  • Slots in Svelte ermöglichen die dynamische Injektion von Inhalten in Komponenten.
     
  • Sie werden mithilfe des <slot>-Elements in Komponenten definiert.
     
  • Slotinhalte werden durch Elternkomponenten bereitgestellt
     
  • Slotinhalte können beliebiges gültiges Svelte-Markup enthalten.

Komponenten-Slots

Dialog

Text der im Dialog angezeigt wird. Potenziell ergänzt um weiteren Content.

Okay

Komponenten-Slots

<strong>Dialog</strong>

<p>Text der im Dialog angezeigt wird. Potenziell ergänzt um weiteren Content.</p>

<button>Okay</button>

Live Coding

Übung

Übung

  1. Erstelle eine Detailseite für Transaktionen
     
  2. Die Detailseite sollte alle Informationen zu einer Transaktion enthalten
     
  3. Auf jeder Detailseite kann die jeweilige Transaktion gelöscht werden

 

Hinweise

  • Die Art der Implementierung ist frei wählbar
     
  • Die Beispiel-App nutzt modifizierte StatCards

Aufgaben

Repository

git checkout tags/part_2_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Vorbereitung

Lösungsvorschlag

Teil III

Teil III

- Svelte Stores

- Custom  Stores

- Svelte Context

- Stores im Kontext

Svelte Stores

Svelte Stores

  • Stores sind JavaScript-Objekte, die Werte kapseln und über eine .subscribe-Methode abonniert werden können.
     
  • Abonnenten werden über Aktualisierungen benachrichtigt.
     
  • Svelte bietet Sprachunterstützung für die Verwendung von Stores.
     
  • Es gibt verschiedene Arten von mitgeliferten Stores, namentlich readable, writable und derived.

Svelte Store Contract

Svelte Store Contract

  • Ein Store muss eine .subscribe-Methode enthalten, die als Argument eine Subscription-Funktion akzeptiert, um ein Abo zu erstellen.
     
  • Die Subscription-Funktion muss sofort und synchron mit dem aktuellen Wert des Stores aufgerufen werden, wenn .subscribe aufgerufen wird.
     
  • Alle aktiven Subscription-Funktionen des Stores müssen später synchron aufgerufen werden, wenn sich der Wert des Stores ändert.
     
  • .subscribe muss eine Unsubscribe-Funktion zurückgeben. Deren Aufruf  muss das Abo beenden und die entsprechende Subscription-Funktion darf nicht erneut vom Store aufgerufen werden.
     
  • Ein Store kann optional eine .set-Methode enthalten, welche als Argument einen neuen Wert akzeptiert und den Store damit aktualisiert. Ein solcher Store wird als Writable Store bezeichnet.

Custom Stores

Svelte Kontext

Svelte Kontext

  • Der Kontext ist ein Key-Value-Store, der eine Datenübertragung von einer Komponente zu allen ihr hierarchisch untergeordneten Komponenten ohne explizite Props ermöglicht.
     
  • setContext() wird verwendet, um den Kontext in der übergeordneten Komponente zu erstellen und bereitzustellen.
     
  • getContext() wird verwendet, um den Kontext in den untergeordneten Komponenten abzurufen.
     
  • Der Kontext kann beliebige Werte enthalten, wie z.B. Konfigurationsdaten, Funktionen oder Stores.
     
  • Der Kontext ist nicht reaktiv im Svelte-Sinne.

Stores im Kontext

Live Coding

Übung

Übung

  1. Erstelle eine Mechanik zur Synchronisierung der Transaktionen in den Local Storage des Browsers


Hinweise

  • Die Art der Implementierung ist frei wählbar
     
  • Stores können abonniert werden

Aufgaben

Repository

git checkout tags/part_3_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Vorbereitung

Lösungsvorschlag

Teil IV

Teil IV

- Arbeit mit Promises

- Transitionen

- Refs & Actions

Arbeit mit Promises

Arbeit mit Promises

Komponente Rendern

Daten Abrufen

Anzeige Aktualisieren

Arbeit mit Promises

Komponente Rendern

Daten Abrufen

Anzeige Aktualisieren

Transitionen

Transitionen

  • Transitionen in Svelte ermöglichen animierte Übergänge beim Hinzufügen oder Entfernen von DOM-Elementen.
     
  • Sie werden über die transition-Direktive verwendet und ermöglichen die Nutzung eingebauter oder benutzerdefinierter Transitionen.
     
  • Svelte beinhaltet bereits Transitionseffekte wie fade, slide oder scale.
     
  • Benutzerdefinierte Funktionseffekte folgen einer einheitlichen Schnittstelle, die in der Svelte-Dokumentation zu finden ist.

Transitionen

Refs & Actions

Refs & Actions

  • Refs sind eine Möglichkeit zum referenzieren von DOM-Elementen.
     
  • Sie werden über die bind:this={variable}-Direktive erstellt und können wie andere Variablen verwendet und geteilt werden.
     
  • Actions sind Funktionen die direkt an DOM-Elementen geknüpft werden können, ohne eine explizite Referenz zu erstellen.
     
  • Sie werden über die use:actionName={params}-Direktive verwendet.
     
  • Actions werden beim Hinzufügen zum DOM ausgeführt.
     
  • Sie können optional eine Funktion zurückgeben, die beim Entfernen aus dem DOM ausgeführt wird.

Live Coding

Übung

Übung

  1. Erstelle eine Visualisierung für Einnahmen und Ausgaben mit Chart.js


Hinweise

  • Chart.js ist bereits in den Abhängigkeiten gelistet
     
  • Nutze Svelte Actions um Visualisierungen ins DOM zu bringen
     
  • Diagrammtyp, Art der Implementierung, usw. sind darüber hinaus frei wählbar

Aufgaben

Repository

git checkout tags/part_4_exercise -b <BRANCH_NAME>

npm ci

npm run dev

Vorbereitung

Web-Apps mit Svelte (JSDays Berlin 2023)

By Nils Röhrig

Web-Apps mit Svelte (JSDays Berlin 2023)

  • 936