sveltejs / learn.svelte.dev

A soup-to-nuts interactive tutorial on how to build apps with Svelte
https://learn.svelte.dev
MIT License
529 stars 243 forks source link

[tutorial errata] #571

Open warren-bank opened 10 months ago

warren-bank commented 10 months ago
  1. Part 1 / Stores / Store bindings
    • tutorial
    • git
    • comments:
      • following:
      • The $name += '!' assignment is equivalent to name.set($name + '!').
      • addition:
        1. a note that this is also equivalent to:
          name.update(n => n + '!')
        2. a note about whether this is also equivalent to:
          name.update($name => $name + '!')
          • does $name have any special meaning in this context,
            or can it be used (for readability) as the name of this parameter?
        3. a note about whether there is any performance benefit in choosing one form vs. another
Conduitry commented 10 months ago

I don't know that these sorts of things belong at this point in the tutorial. To answer some of these specific points: It's only equivalent to name.update(n => n + '!') if your store also implements the .update() method. The stores created by the exports from svelte/store so, but this is not guaranteed by the store contract. $name += '!' compiles to name.set($name + '!') which is guaranteed to work for all stores that satisfy the contract.

$name does not have special meaning when it's been shadowed by a local variable like a function parameter.

.set might be microscopically faster, but I don't think it would make any difference in the context of a real application.

warren-bank commented 10 months ago
  1. Part 2 / Advanced bindings / Contenteditable bindings

    • tutorial
    • git
    • comments:

      • I may be nitpicking..
        but I believe that this example doesn't fully illustrate the concept
      • am I wrong to think that a better example might be:

        <script>
        let html = '<p>Write <b><i>some</i></b> text!</p>';
        </script>
        
        <div bind:textContent={html} contenteditable />
        
        <div>{@html html}</div>
        
        <style>
        [contenteditable] {
         padding: 0.5em;
         border: 1px solid #eee;
         border-radius: 4px;
         margin-bottom: 1em;
        }
        </style>
warren-bank commented 9 months ago
  1. Part 3 / Routing / Pages

    • tutorial
    • git:
      1. on:change
      2. function set_iframe_src(src)
    • comments:
      • the theme configured for the tutorial is always used by the sample app that is rendered in an iframe
      • the tutorial allows the user to specify the relative URL (ie: route) that is rendered by the app
      • this relative URL should allow the user to override the global theme by specifying a ?theme= querystring parameter
      • ex: /about?theme=dark
      • however, this querystring value is updated by set_iframe_src immediately before the iframe is rendered
    • suggestion:

      /** @param {string} src */
      function set_iframe_src(src) {
       if (!iframe) return; // HMR
      
       // To prevent iframe flickering.
       // Set to `visible` by calling `set_iframe_visible` function
       // from iframe on:load or setTimeout
       iframe.style.visibility = 'hidden';
       setTimeout(set_iframe_visible, 1000);
      
       // removing the iframe from the document allows us to
       // change the src without adding a history entry, which
       // would make back/forward traversal very annoying
       const parentNode = /** @type {HTMLElement} */ (iframe.parentNode);
       parentNode?.removeChild(iframe);
      
       const url = new URL(src);
      
       if (!url.searchParams.has('theme'))
         url.searchParams.set('theme', $theme.current);
      
       iframe.src = url.href;
       parentNode?.appendChild(iframe);
      }
    • where:
      • the only change is the addition of a conditional test:
          if (!url.searchParams.has('theme'))
        before updating the theme querystring parameter
warren-bank commented 9 months ago
  1. Part 3 / Shared modules / The $lib alias
    • tutorial
    • git
    • bug:
      • the following line:
        import { message } from '../../../../../../lib/message.js';
      • should be:
        import { message } from '../../../../../lib/message.js';
    • comments:
      • remove one instance of: ../
      • as is:
        500
        Internal Error
        Failed to resolve import

update:

warren-bank commented 9 months ago

note (to self):

  1. Part 3 / Forms / Validation

    • tutorial
    • comments:

      • +page.server.js
        export const actions = {
        create: async ({ cookies, request }) => {
         return {success: 'saved OK'}
        },
        delete: async ({ cookies, request }) => {
         return {success: 'removed OK'}
        }
        }
      • +page.svelte

        <script>
        export let form;
        </script>
        
        {#if form?.success}
        <p class="error">{form.success}</p>
        {/if}
warren-bank commented 9 months ago

note (to self):

  1. Part 3 / Forms / Customizing use:enhance

    • tutorial
    • comments:

      • +page.server.js

        import {fail} from '@sveltejs/kit'
        
        export const actions = {
        delete: async () => {
         await new Promise((fulfil) => setTimeout(fulfil, 1000))
        
         return fail(500, {
           error: 'Database Error'
         })
        }
        }
warren-bank commented 9 months ago

note (to self):

  1. Part 3 / Stores / page

    • tutorial
    • comments:

      • src/routes/+layout.svelte

        <script>
        import { page } from '$app/stores';
        
        const nav = [{
         name: 'home',
         path: '/'
        },{
         name: 'about',
         path: '/about'
        }]
        </script>
        
        <nav>
        {#each nav as item}
         <a href="{item.path}" aria-current={$page.url.pathname === item.path}>
           {item.name}
         </a>
        {/each}
        </nav>
        
        <slot />
warren-bank commented 9 months ago

note (to self):

  1. Part 3 / Stores / navigating

    • tutorial
    • comments:

      • src/routes/+layout.svelte

        <script>
        import { page, navigating } from '$app/stores';
        
        $: console.log( JSON.stringify($page,       null, 2) );
        $: console.log( JSON.stringify($navigating, null, 2) );
        </script>
warren-bank commented 9 months ago

note (to self):

  1. Part 4 / Hooks / handle

    • tutorial
    • comments:

      • src/hooks.server.js

        import { redirect } from '@sveltejs/kit';
        
        export async function handle({ event, resolve }) {
        console.log( JSON.stringify(event, null, 2) );
        
        switch(event.url.pathname) {
         case '/ping':
           throw redirect(307, '/pong')
        
         case '/pong':
           return new Response('pong')
        }
        
        return await resolve(event)
        }
warren-bank commented 9 months ago

note (to self):

  1. Part 4 / Hooks / handleFetch

    • tutorial
    • comments:

      • src/hooks.server.js

        export async function handleFetch({ event, request, fetch }) {
        
        console.log('01. ' + (typeof event.request     === typeof request))
        console.log('02. ' + (typeof event.fetch       === typeof fetch))
        console.log('03. ' + (typeof event.request.url === typeof request.url))
        
        console.log('04. ' + (event.request     === request))
        console.log('05. ' + (event.fetch       === fetch))
        console.log('06. ' + (event.request.url === request.url))
        
        console.log('07. typeof event.url   => ' + (typeof event.url)   + ((event.url   instanceof URL) ? ' (instanceof URL)' : ''))
        console.log('08. typeof request.url => ' + (typeof request.url) + ((request.url instanceof URL) ? ' (instanceof URL)' : ''))
        
        console.log('09. event.request.url => ' + event.request.url)
        console.log('10.       request.url => ' + request.url)
        
        return await fetch(request);
        }
      • logs:

        01. true
        02. true
        03. true
        
        04. false
        05. false
        06. false
        
        07. typeof event.url   => object (instanceof URL)
        08. typeof request.url => string
        
        09. event.request.url => http://localhost:5173/?theme=light
        10.       request.url => http://localhost:5173/a
warren-bank commented 9 months ago

note (to self):

  1. Part 4 / Page options / trailingSlash

    • tutorial
    • comments:

      • src/routes/+layout.svelte

        <nav>
        <a href="/always">/always</a>
        <a href="/always/">/always/</a>
        <a href="/ignore">/ignore</a>
        <a href="/ignore/">/ignore/</a>
        <a href="/never">/never</a>
        <a href="/never/">/never/</a>
        </nav>
        
        <slot />
        
        <hr />
        <h3>themes:</h3>
        <ul data-sveltekit-reload>
        <li><a href="./?theme=dark">dark</a></li>
        <li><a href="./?theme=light">light</a></li>
        </ul>
warren-bank commented 9 months ago
  1. Part 4 / Advanced routing / Breaking out of layouts
    • tutorial
    • example given:
      • src/routes/a/b/c/+page@.svelte
      • uses:
        • src/routes/+layout.svelte
      • ignores:
        • src/routes/a/b/c/+layout.svelte
    • comments:
    • the individual page resets its layout hierarchy to the root layout
    • should it not then also inherit the layout in its own directory?
    • questions:
    • can a layout hierarchy be reset for all descendent pages?
      • example:
      • src/routes/a/b/c/+layout@.svelte
      • src/routes/a/b/c/+page.svelte
        • such that it uses:
        • src/routes/+layout.svelte
        • src/routes/a/b/c/+layout@.svelte
      • src/routes/a/b/c/d/+layout.svelte
      • src/routes/a/b/c/d/+page.svelte
        • such that it uses:
        • src/routes/+layout.svelte
        • src/routes/a/b/c/+layout@.svelte
        • src/routes/a/b/c/d/+layout.svelte
warren-bank commented 9 months ago
  1. Docs: CORE CONCEPTS / Loading data / Using parent data

    • docs
    • git
    • comments:

      • fair warning… to avoid waterfalls
      • the given implementation does not do that
      • 2 Promises are resolved synchronously
      • the order doesn't matter
      • a better implementation might more closely resemble:

        export async function load({ params, parent }) {
        const [parentData, data] = await Promise.all([
         parent(),
         getData(params)
        ]);
        
        return {
         ...data
         meta: { ...parentData.meta, ...data.meta }
        };
        }
warren-bank commented 9 months ago

note (to self):

  1. Part 4 / Environment variables / $env/static/private

    • tutorial
    • comments:

      • src/routes/+page.server.js

        import * as env from '$env/static/private';
        
        console.log(JSON.stringify(env, null, 2));