Binding to file inputs with Aurelia

Not too long ago file input binding was added to Aurelia by @PWKad. In this post I'm going to cover how you would use file input binding.

HTML File Input

The markup for a typical file input looks like this:

<input type="file">  

If you want to allow users to choose multiple files, add the multiple attribute:

<input type="file" multiple>

You can restrict allowable file types using the accept attribute:

accept
If the value of the type attribute is file, this attribute indicates the types of files that the server accepts; otherwise it is ignored. The value must be a comma-separated list of unique content type specifiers:

  • A file extension starting with the STOP character (U+002E). (E.g.: ".jpg,.png,.doc")
  • A valid MIME type with no extensions
  • audio/* representing sound files HTML5
  • video/* representing video files HTML5
  • image/* representing image files HTML5

- MDN

Example:

<input type="file" accept="image/*">
<input type="file" accept=".txt,.csv">

Binding to a file input

Aurelia supports binding to an input's files property, which is of type FileList:

<input type="file" files.bind="selectedFiles">

In this example, when the user selects a file the value of the input's files property would be assigned to the view-model's selectedFiles property.

Displaying the selected files

Consider an file input that accepts image files:

<input type="file" multiple accept="image/*" files.bind="selectedFiles">

How would we display the selected files?

Aurelia's repeat.for binding command supports arrays, maps and numbers, but not FileList objects. We can use a converter to translate a FileList to an Array:

export class FileListToArrayValueConverter {
  toView(fileList) {
    let files = [];
    if (!fileList) {
      return files;
    }
    for(let i = 0; i < fileList.length; i++) {
      files.push(fileList.item(i));
    }
    return files;
  }
}

Items in the FileList object are of type File, which inherits from the Blob object. We need to convert the blob to a data url so we can display it in an img tag:

export class BlobToUrlValueConverter {
  toView(blob) {
    return URL.createObjectURL(blob);
  }
}

Bringing it all together, here's what the view and view-model look like:

<template>
  <input type="file" multiple accept="image/*" files.bind="selectedFiles">

  <ul>
    <li repeat.for="file of selectedFiles | fileListToArray">
      <h1>${file.name}: ${file.type} ${file.size / 1000} kb</h1>
      <img src.bind="file | blobToUrl"><img>
      Last Modified: ${file.lastModifiedDate}
    </li>
  </ul>
</template>
export class App {
  selectedFiles;
}

Here's a live example: