learn.svelte.dev icon indicating copy to clipboard operation
learn.svelte.dev copied to clipboard

[tutorial errata]

Open warren-bank opened this issue 1 year ago • 14 comments

  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

warren-bank avatar Feb 01 '24 21:02 warren-bank

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.

Conduitry avatar Feb 05 '24 21:02 Conduitry

  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 avatar Feb 07 '24 00:02 warren-bank

  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 avatar Feb 10 '24 22:02 warren-bank

  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:

  • nevermind..
    I commented before reading far enough to learn that this error was intentional

warren-bank avatar Feb 11 '24 08:02 warren-bank

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 avatar Feb 11 '24 15:02 warren-bank

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 avatar Feb 11 '24 16:02 warren-bank

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 avatar Feb 12 '24 00:02 warren-bank

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 avatar Feb 12 '24 01:02 warren-bank

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 avatar Feb 13 '24 07:02 warren-bank

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 avatar Feb 13 '24 21:02 warren-bank

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 avatar Feb 13 '24 22:02 warren-bank

  1. Part 4 / Advanced routing / Breaking out of layouts
  • tutorial
  • example given:
    • src/routes/a/b/c/[email protected]
      • 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/[email protected]
        • src/routes/a/b/c/+page.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/[email protected]
            • src/routes/a/b/c/d/+layout.svelte

warren-bank avatar Feb 14 '24 18:02 warren-bank

  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 avatar Feb 14 '24 20:02 warren-bank

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));
      

warren-bank avatar Feb 15 '24 00:02 warren-bank