Full SvelteKit Component JavaScript and Markup

In the last blog post I built the functionality to handle form submission for the file upload itself, but we still didn't have a way to actually handle the files. In this post I'll set up the very basic markup as well as the accompanying script functionality required.

Dependencies

The only extra dependency needed for this section, as mentioned in the last post, is Svelte File Dropzone, which can be installed by simply typing npm i -D svelte-file-dropzone.

Script Setup

I'm going to use basic functionality for Dropzone, as indicated in the documentation with a couple of extra things:

  • I will be adding a filter to not add files that include names already in the list of accepted files
  • I will be wrapping the Dropzone in a <form> tag because that just feels more natural to me.
  • I will be adding functionality to remove one or all of the accepted files

The script from the last blog post just had the files object and the handleSubmit method. Now I'll add a few more things.

// /src/routes/index.svelte

<script>
  let files = {
    accepted: [],
    rejected: []
  };

  const accept = ['application/pdf'];
  const maxSize = 2500000;
  let loading = false;

  const handleFilesSelect = (e) => {
    const { acceptedFiles, fileRejections } = e.detail;
    files.accepted = [
      ...files.accepted
        .filter((file) => !acceptedFiles.map((f) => f.name).includes(file.name)),
      ...acceptedFiles
    ];
  files.rejected = [...files.rejected, ...fileRejections];
  };

  const handleRemoveFile = (e, index) => {
    files.accepted.splice(index, 1);
    files.accepted = [...files.accepted];
  };

  const handleRemoveAll = () => {
    files.accepted = [];
  };
</script>

Markup

The markup for this is going to be wholly unspectacular in that I will not be including any styling whatsoever, but functionally this will work.

<main>
  <form on:submit|preventDefault={handleSubmit}>
    <Dropzone
      {accept}
      {maxSize}
      on:drop={handleFilesSelect}
    />
    {#if files.accepted.length > 0}
      <div>
        <h3>Files to be Uploaded</h3>
        <ul>
          {#each files.accepted as item, index}
            <li>
              {item.name}
              <button type="button" on:click={(e) => handleRemoveFile(e, index)}>
                x
              </button>
            </li>
          {/each}
        </ul>
      </div>
      <div>
        <button type="submit" disabled={loading}>
          {#if loading}
            ...Loading
          {:else}
            <span>Submit</span>
          {/if}
        </button>
        <button type="button" disabled={loading} on:click={handleRemoveAll}>
          Remove All
        </button>
      </div>
    {/if}
  </form>
</div>

Style

As an aside, I also added a little bit of styling to make it centered

<style>
    main {
        display: grid;
        place-content: center;
        height: 100vh;
    }
</style>

The resulting page doesn't look great, but if you click on the box and choose the files you want to upload it should look like this Screen Shot 2022-06-22 at 3.27.10 PM.png Then if you click submit you'll see, once the process is completed, that the files show up in the assigned folder Screen Shot 2022-06-22 at 3.29.39 PM.png

Summary

That's it. The functionality is nothing terribly complicated when you look at everything in the aggregate, but all of the moving pieces can be a bit complex. Like I mentioned, I spent quite a bit of time trying to figure all of this out for the first time, but now that I've gotten it working I wanted to share it with you all. See you next time.