1

How can I use a "writable store" to create a "blob"?

I am new to Svelte. I am trying to code a simple markdown editor for visually impaired users. The content of the "textarea" is stored in a "writable store" for further use:

  • speech-synthesis
  • markdown-preview
  • the content of an existing text file is stored in it
  • save text to file

But saving the text content via download to a file does not work.

<script>
  let blob = new Bob([$textStore], type:"text/plain");
  let url = URL.createObjectURL(blob);
</script>

<button>
  <a href={url} download="untitled.md" id="link">Download 
  </a>
</button>

An empty file is saved or it has the content "[object Object]", when I use curley brackets:

let blob = new Blob ([{$textStore}], {type: 'text/plain'});

File for testing:

// TextStore.js        
import { writable } from "svelte/store";
    
export const textStore = writable('');
<!-- EditorTest.svelte -->
<script>
  import { textStore } from "./TextStore.js";

  let blob = new Blob ([{$textStore}], {type: 'text/plain'});
  let url = URL.createObjectURL(blob);
</script>


<h1>Blob-Test</h1>
<textarea bind:value={$textStore} cols="20" rows="5"></textarea>
<hr>
<pre>{$textStore}</pre>
<br>
<button>
  <a href={url} download="untitled.md" id="link">Save</a>
</button>

Can someone help, thanks a lot already.

H.B.
  • 166,899
  • 29
  • 327
  • 400
Henriette
  • 13
  • 3

1 Answers1

3

The main issue is, that the code is not reacting to the changes. The Blob is created once at the beginning, when the store is still empty. You could make it reactive using $:, but that is not a great idea, as it unnecessarily re-creates the Blob over and over.

I would recommend using a click handler on the button (the link inside it is invalid HTML anyway), and then creating the Blob there:

<button on:click={onDownload}>
    Save
</button>
function onDownload() {
    const blob = new Blob([$textStore], {type: 'text/plain'});
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.download = 'file.md';
    link.href = url;
    link.click();
    URL.revokeObjectURL(url); // Object URLs should be revoked after use
}
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • (The REPL does not support downloads like this, probably due to the iframe, so i did not provide one.) – H.B. Jun 04 '22 at 21:01
  • Ah, is that why my [REPL](https://svelte.dev/repl/ebe75806eb9b4765b2e16230c372e564?version=3.48.0) (inefficient solution) worked in Safari but not in Chrome or Firefox – Bob Fanger Jun 04 '22 at 21:09
  • @BobFanger Definitely possible. Question then is whether the behavior is defined in the spec and if so, which implementation is correct (entirely possible that this is up the client implementor though). – H.B. Jun 04 '22 at 21:16
  • 1
    Thank you very much, especially for the explanation. For days I try around without success, but now it works fine! – Henriette Jun 04 '22 at 22:27