# Dynamic FileAttachment issue

**URL:** <https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401>\
**Category:** Help\
**Created:** [June 4, 2020, 11:46am UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401 "2020-06-04T11:46:01Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![MAKIO135](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/makio135/32/2220_2.png) [@MAKIO135](https://talk.observablehq.com/u/MAKIO135)\
**Post date:** [June 4, 2020, 11:46am UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/1 "2020-06-04T11:46:01Z")

</div>

Hello there 👋

I’d like to use `select` from `@jashkenas/inputs` to switch between 2 images:

```auto
viewof selection = select({
  description: "Select image",
  options: ["image1.png", "image2.png"],
  value: "image1.png"
})

```

then use this to display an image:

```auto
FileAttachment(selection).image()
// also tried String casting:
// FileAttachment(`${selection}`).image()

```

But I get the following error:  
`SyntaxError: FileAttachment() requires a single literal string as its argument.`  
So, is it that `FileAttachment` do not like reactive argument ?  
And what alternative could I use ?

I created a notebook for this issue here: [Observable](https://observablehq.com/@makio135/dynamic-fileattachment-issue)

Thanks 🙏

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [June 4, 2020, 2:57pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/2 "2020-06-04T14:57:43Z")

</div>

Here’s the best I can do – at least it works 🙂 [I hope…]

> **[Observable](https://observablehq.com/compare/d28221a4135b4c5a@49...853dc4ff3e70e5e2@79)**
>
> Explore and visualize data. Share and publish your insights. Discover and be inspired.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [June 4, 2020, 3:08pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/3 "2020-06-04T15:08:13Z")

</div>

Yep, Observable prevents dynamic arguments in FileAttachments:

> <https://github.com/observablehq/parser/blob/c19f77afd4bcd12baec8e32ba935a70cc7d37844/src/file-attachments.js#L17-L32>

You’ll have to create an index of your attachments (either in a separate cell or within your widget):

```auto
files = [
  FileAttachment("image1.png"),
  FileAttachment("image2.png"),
].reduce((o, f) => (o[f.name] = f, o), {})

```

… then fetch the keys:

```auto
options: Object.keys(files),

```

… and finally use:

```auto
files[selection].image()

```

---

<div class="post-metadata">

**Author:** ![radames](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/radames/32/1699_2.png) [@radames](https://talk.observablehq.com/u/radames)\
**Post date:** [June 4, 2020, 3:10pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/4 "2020-06-04T15:10:50Z")

</div>

I’ve came across this problem once. As noted by @mootari, if you read on the doc about FileAttachment you can find this paragraph

> **References to files are parsed statically.** We use static analysis to determine which files a notebook uses so that we can automatically publish referenced files when a notebook is published (and _only_ referenced files), and similarly copy only referenced files when forking a notebook. The FileAttachment function thus accepts only literal strings; code such as
> 
> ```auto
> FileAttachment("my" + "file.csv")
> 
> ```
> 
> or similar dynamic invocation is invalid syntax. For details on how this is implemented, see [our parser](https://github.com/observablehq/parser).

> **[File attachments | Observable documentation](https://observablehq.com/documentation/data/files/file-attachments)**
>
> All your Observable questions answered

If you want the selection to come from the same cell you could try this pattern

```js
viewof selection = {
  const el = DOM.element("div");
  const selection = select({
    description: "Select image",
    options: [
      {
        label: "image1.png",
        value: await FileAttachment("image1.png").url()
      },
      {
        label: "image2.png",
        value: await FileAttachment("image2.png").url()
      }
    ],
    value: "image1.png"
  });
  el.appendChild(selection);

  const loadImage = url =>
    new Promise(resolve => {
      const image = new Image();
      image.onload = () => {
        el.value = image;
        el.dispatchEvent(new CustomEvent("update"));
        resolve(image);
      };
      image.src = url;
    });
  selection.onchange = evt => loadImage(evt.target.value); // load onselection change, passing url
  el.value = await loadImage(selection.input.value); // load initial value
  return el;
}

```

> **[Dynamic FileAttachment issue](https://observablehq.com/d/b0ca8317a432dde6)**
>
> I have 2 images: image1.png image2.png I'd like to use \\\`select\\\` from @jashkenas/inputs to have an UI to choose between image1.png and image2.png I get my value: But I get the following error:

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [June 4, 2020, 3:17pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/5 "2020-06-04T15:17:39Z")

</div>

Here’s a compact example which will return the actual FileAttachment instance:

```auto
viewof selection = {
  const files = new Map([
    FileAttachment("image1.png"),
    FileAttachment("image2.png")
  ].map(f => [f.name, f]));
  
  const form = select({
    description: "Select image",
    options: Array.from(files.keys()),
    value: "image2.png"
  });
 
  return Object.defineProperty(html`<div>${form}`, 'value', {get() { return files.get(form.value) }});
}

```

… then (e.g.):

```auto
selection.image()

```

---

<div class="post-metadata">

**Author:** ![MAKIO135](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/makio135/32/2220_2.png) [@MAKIO135](https://talk.observablehq.com/u/MAKIO135)\
**Post date:** [June 4, 2020, 4:16pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/6 "2020-06-04T16:16:24Z")

</div>

Wow 😮  
Thanks @aaronkyle @mootari @radames for your quick and really insightful answers ! 🙏

---

<div class="post-metadata">

**Author:** ![radames](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/radames/32/1699_2.png) [@radames](https://talk.observablehq.com/u/radames)\
**Post date:** [June 4, 2020, 4:55pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/7 "2020-06-04T16:55:21Z")

</div>

😲 ohhh this is so much better! using FileAttachment instance great idea!

---

<div class="post-metadata">

**Author:** ![Steve](https://avatars.discourse-cdn.com/v4/letter/s/f19dbf/32.png) [@Steve](https://talk.observablehq.com/u/Steve)\
**Post date:** [June 4, 2020, 8:11pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/8 "2020-06-04T20:11:19Z")

</div>

Great stuff !

Do you think we can get the Content-Length from headers associated with the FileAttachment response?

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [June 5, 2020, 5:50pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/9 "2020-06-05T17:50:28Z")

</div>

> [@Steve](#):
>
> Do you think we can get the Content-Length from headers associated with the FileAttachment response

There is [no direct method](https://github.com/observablehq/stdlib#file-attachments), but you can fetch the size by other means:

- via `.url()`:

```auto
+(await fetch(
  await FileAttachment('image.png').url(), {method: 'head'})
).headers.get('Content-Length')

```

_Note: You can also access the URL directly:_

```auto
+(await fetch(FileAttachment('image.png')._url, {method: 'head'}))
  .headers.get('Content-Length')

```

- via `.blob()`:

```auto
(await FileAttachment('image.png').blob()).size

```

- via `.arrayBuffer()`:

```auto
(await FileAttachment('image.png').arrayBuffer()).byteLength

```

---

<div class="post-metadata">

**Author:** ![JohnMulligan](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/johnmulligan/32/3702_2.png) [@JohnMulligan](https://talk.observablehq.com/u/JohnMulligan)\
**Post date:** [September 29, 2020, 10:38pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/11 "2020-09-29T22:38:20Z")

</div>

I could be doing this wrong, but I am getting a runtime error: “select is not defined”

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [September 30, 2020, 5:43pm UTC](https://talk.observablehq.com/t/dynamic-fileattachment-issue/3401/12 "2020-09-30T17:43:30Z")

</div>

> [@JohnMulligan](#):
>
> I am getting a runtime error: “select is not defined”

You need to import the `select` cell from Jeremy Ashkenas’ omnipresent [input widgets](https://observablehq.com/@jashkenas/inputs) into your notebook:

```auto
import {select} from '@jashkenas/inputs'

```
