7

Building a Full Stack Application From Scratch with Svelte and Node (PART 5) — C...

 3 years ago
source link: https://tahazsh.com/fullstack-app-with-svelte-and-node-part-5
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

To create a story, you need to provide a title, a type, and a link or description (based on the type).

Let's start by implementing the form component, and then use that form on the create and edit story pages.

Creating story form

Since we're working on the story section, create _StoryForm.svelte inside the routes/story directory. And then add the following (explained below):

<script>
  import { goto } from '@sapper/app'
  import ButtonGroup from '../../components/ButtonGroup.svelte'

  export let story

  let inProgress = false

  async function submit () {
    inProgress = true
    if (story._id) {
      console.log('Update story with id', story._id)
    } else {
      console.log('Create a new story')
    }
    // redirect to the story you just created/updated
    goto(`/story/123`)
  }
</script>

<form
  class="story-form"
  on:submit|preventDefault="{submit}"
>
  <input
    class="text-input"
    bind:value="{story.title}"
    type="text"
    placeholder="Title"
    required
  >
  <ButtonGroup
    bind:value="{story.type}"
    disabled="{story._id}"
    buttons="{[
      {
        value: 'link',
        title: 'Link'
      },
      {
        value: 'text',
        title: 'Text'
      }
    ]}"
  />
  {#if story.type === 'link'}
    <input
      class="text-input link-input content-input"
      bind:value="{story.content}"
      type="url"
      placeholder="URL"
      required
    >
  {:else}
    <textarea
      class="content-text-input content-input"
      bind:value="{story.content}"
      placeholder="Text (optional)"
    ></textarea>
  {/if}
  <button
    class="primary-button submit-button"
    disabled="{inProgress}"
  >
    {#if story._id}
      SAVE
    {:else}
      POST
    {/if}
  </button>
</form>

Since we're going to use the same form for creation and editing stories, we're accepting story data as prop.

If story contains _id field, then this form is for editing it. Look how we're using this check in:

  • submit function: which will either send an update request or a create request.
  • ButtonGroup component: to disable it if we're in editing page — the user shouldn't be able to change the story type once it's created.
  • submit button: to either display "SAVE" or "POST" on the button.

Since we don't have our backend server created yet, we're not sending any requests. We're instead logging a message to the console to indicate whether we're updating or creating a story.

The inProgress variable here is used to disable the submit button when it's currently submitting so we prevent the user from accidentally submitting the form more than once.

After the form has been submitted successfully, we redirect the user to the created/updated story view page. For that, we're using sapper's goto function.

ButtonGroup is a custom component to allow users to choose a single value from multiple options. (You can use a simple radio button or a dropdown instead, but I found this to look nicer!)

The user can either enter a link or text for a story. So based on the current value of story.type (which is controlled by ButtonGroup) we either display a text input for the link or a textarea for the text.

Note how we're importing this component at the top while we don't have it in our project. So let's add src/components/ButtonGroup.svelte with the following code:

<script>
  export let buttons = []
  export let value
  export let disabled = false
</script>

<div
  class="button-group"
  class:disabled="{disabled}"
>
  {#each buttons as button}
    <button
      class="button"
      type="button"
      class:active="{button.value === value}"
      on:click="{() => !disabled ? value = button.value : ''}"
    >
      {button.title}
    </button>
  {/each}
</div>

Adding story create page

Create create.svelte in route/story and add this:

<svelte:head>
  <title>Create a new story</title>
</svelte:head>

<script>
  import StoryForm from './_StoryForm.svelte'

  let story = { title: '', type: 'link', content: '' }
</script>

<StoryForm {story}/>

Since the story variable doesn't have _id, the story form will be displayed for creating stories.

Now if you open http://localhost:3000/story/create in your browser, you should see this:

1.png

Adding story edit page

The URL for this page would be: /story/123/edit. This means we need create the edit page inside /routes/story/[id]/edit.svelte. But you may say: "But we already have [id].svelte".

Right, we're using that component for the story view page. So to keep it as that, we can rename it to index.svelte and move it into [id] directory.

So create [id] directory inside routes/story and rename [id].svelte to index.svelte and move it there.

For that to work, we have to fix import paths inside [id]/index.svelte like so:

<script>
  import Story from '../../_components/Story.svelte'
  import CommentContainer from '../_CommentContainer.svelte'
</script>

Now we can create our edit page inside [id] and name it edit.svelte — so it is src/routes/story/[id]/edit.svelte.

Next, let's write the following inside edit.svelte:

<script>
  import StoryForm from '../_StoryForm.svelte'

  let story = { _id: 1, title: 'Story Title', type: 'link', content: 'https://svelte.dev/' }
</script>

<svelte:head>
  <title>Edit your story</title>
</svelte:head>

<StoryForm {story}/>

Note how we're using mocked data for the story variable. When our backend server is ready, we'll fetch the story from the database using the id in the url.

To test this page, open http://localhost:3000/story/1/edit in the browser. (Note how the button group element is disabled.)

What's next?

Our next part is a quick and easy one. It'll be about creating the nav bar along with the login and signup pages.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK