Lion Logo Lion Fundamentals Guides Components Blog Toggle darkmode

Input File: Use Cases

Features

API calls to upload the selected files can be done in below two ways which is driven by uploadOnSelect property

  • On form submit
  • On file selection

Parameters

<lion-input-file
  multiple
  label="Attachments"
  help-text="Signature scan file"
  max-file-size="1024000"
  accept="application/PDF"
  upload-on-select
>
</lion-input-file>

modelValue

Array of File; Contains all the uploaded files

multiple

Boolean; setting to true allows selecting multiple files.

EnableDragAndDrop

Boolean; setting to true allows file upload through drag and drop.

uploadOnSelect

Boolean;

  • Set to true when API calls for file upload needs to be done on file selection
  • Set to false when API calls for file upload needs to be done on form submit.
  • Default is false.

uploadResponse

An array of the file-specific data to display the feedback of fileUpload

Data structure for uploadResponse array:

[
  {
    name: 'file1.txt', // name of uploaded file
    status: '', // SUCCESS | FAIL | LOADING
    errorMessage: '', // custom error message to be displayed
    id: '', // unique id for future execution reference
  },
  {
    name: 'file2.txt', // name of uploaded file
    status: '', // SUCCESS | FAIL | LOADING
    errorMessage: '', // custom error message to be displayed
    id: '', // unique id for future execution reference
  },
];

Events

model-value-changed

Fired when modelValue property changes.

file-list-changed

Fired when files are uploaded. Event detail gives list of newly added files in newFiles key

ev.detail.newFiles;

file-removed

Fired when a file is removed. Event detail gives

  • File objected for removed file in ev.detail.removedFile
  • Status of this file can be used as ev.detail.status
  • uploadResponse for this file as set using uploadResponse property. Can use used as ev.detail.uploadResponse

Usage

Basic File upload

When file has to be uploaded as soon as it is selected by the user. Use file-list-changed event to get the newly added file, upload it to your server and set the response back to component via uploadResponse property.

export const basicFileUpload = () => {
  return html`
    <lion-input-file
      label="Passport"
      max-file-size="1024000"
      accept=".jpg,.svg,.xml,image/svg+xml"
      @file-list-changed="${ev => {
        console.log('fileList', ev.detail.newFiles);
      }}"
    >
    </lion-input-file>
  `;
};

Validation

Accept

The accept attribute value is a string that defines the file types the file input should accept. This string is a comma-separated list of unique file type specifiers.

For more info please consult the MDN documentation for "accept".

export const acceptValidator = () => {
  return html`
    <lion-input-file
      accept=".jpg,.svg,.xml,image/svg+xml"
      label="Passport"
      enable-drop-zone
      @file-list-changed="${ev => {
        console.log(ev.detail.newFiles);
      }}"
    >
    </lion-input-file>
  `;
};

Maximum File Size

The max-file-size attribute sets the maximum file size in bytes.

export const sizeValidator = () => {
  return html`
    <lion-input-file
      max-file-size="2048"
      label="Passport"
      @file-list-changed="${ev => {
        console.log(ev.detail.newFiles);
      }}"
    >
    </lion-input-file>
  `;
};

Multiple file upload

When file has to be uploaded as soon as it is selected by the user. Use file-list-changed event to get the newly added files, upload it to your server and set the response back to component via uploadResponse property for each file.

export const multipleFileUpload = () => {
  return html`
    <lion-input-file
      label="Passport"
      name="upload"
      multiple
      max-file-size="1024000"
      @file-removed="${ev => {
        console.log('removed file details', ev.detail);
      }}"
      @file-list-changed="${ev => {
        console.log('file-list-changed', ev.detail.newFiles);
      }}"
      @model-value-changed="${ev => {
        console.log('model-value-changed', ev);
      }}"
    >
    </lion-input-file>
  `;
};

Pre-filled state

The pre-filled state of the file upload component shows the files that were being assigned by the user initially.

On every reload of page, the file upload component reverts to the initial state and using the uploadResponse property, shows the names(not links) of previously assigned files.

Note:

  • the list of prefilled files will not be available in modelValue or serializedValue. This is because value cannot be assigned to native input with type file
  • Only Required validation (if set on lion-file-input) is triggered for files which are prefilled.
export const prefilledState = () => {
  return html`
    <lion-input-file
      label="Passport"
      name="myFiles"
      multiple
      .validators="${[new Required()]}"
      .uploadResponse="${[
        {
          name: 'file1.txt',
          status: 'SUCCESS',
          errorMessage: '',
          id: '132',
        },
        {
          name: 'file2.txt',
          status: 'SUCCESS',
          errorMessage: '',
          id: 'abcd',
        },
      ]}"
    >
    </lion-input-file>
  `;
};

With form submit

Note: When lion-input-file is prefilled, the list of prefilled files will not be available in modelValue or serializedValue. This is because value cannot be assigned to native input with type file.

const myFormReset = ev => {
  const input = ev.target.querySelector('lion-input-file');
  input.reset();
  console.log(input.modelValue);
};

const myFormSubmit = ev => {
  ev.preventDefault();
  const input = ev.target.querySelector('lion-input-file');
  console.log(input.hasFeedbackFor);
  console.log(input.serializedValue);
  return false;
};

export const withIngForm = () => {
  class FilenameLengthValidator extends Validator {
    static get validatorName() {
      return 'FilenameLengthValidator';
    }

    static getMessage(data) {
      return `Filename length should not exceed ${data.params.maxFilenameLength} characters`;
    }

    // eslint-disable-next-line class-methods-use-this
    checkFilenameLength(val, allowedFileNameLength) {
      return val <= allowedFileNameLength;
    }

    execute(modelVal, { maxFilenameLength }) {
      const invalidFileIndex = modelVal.findIndex(
        file => !this.checkFilenameLength(file.name.length, maxFilenameLength),
      );
      return invalidFileIndex > -1;
    }
  }

  return html`
    <form @submit="${myFormSubmit}" @reset="${myFormReset}">
      <lion-input-file
        label="Passport"
        name="upload"
        multiple
        .validators="${[new Required(), new FilenameLengthValidator({ maxFilenameLength: 20 })]}"
        .uploadResponse="${[
          {
            name: 'file1.zip',
            status: 'SUCCESS',
            id: '132',
          },
          {
            name: 'file2.zip',
            status: 'SUCCESS',
            id: 'abcd',
          },
        ]}"
      >
      </lion-input-file>
      <button type="reset">Reset</button>
      <button>Upload</button>
    </form>
  `;
};

Without form submit

Set uploadOnSelect property to true. This option can be used when Server API calls are needed as soon as it is selected by the user.

For this scenario, the list of files is displayed based on the uploadResponse property which needs to maintained by the consumers of this component

Below is the flow:

When user uploads new file(s)

  1. file-list-changed event is fired from component with the newly added files
  2. Initiate the request to your backend API and set the status of the relevant files to LOADING in uploadResponse property
  3. Once the API request in completed, set the status of relevant files to SUCCESS or FAIL in uploadResponse property

When user deletes a file

  1. file-removed event is fired from component with the deleted file
  2. Initiate the delete request to your backend API and set the status of the relevant files as LOADING in uploadResponse property
  3. Once the API request is completed, delete the file object from uploadResponse property
export const uploadWithoutFormSubmit = () => {
  return html`
    <lion-input-file
      label="Passport"
      name="upload"
      multiple
      upload-on-select
      @file-removed="${ev => {
        ev.target.uploadResponse[
          ev.target.uploadResponse.findIndex(file => file.name === ev.detail.uploadResponse.name)
        ].status = 'LOADING';
        ev.target.uploadResponse = [...ev.target.uploadResponse]; // update uploadResponse after API calls are completed
        setTimeout(() => {
          ev.target.uploadResponse[
            ev.target.uploadResponse.findIndex(file => file.name === ev.detail.uploadResponse.name)
          ] = {};
          ev.target.uploadResponse = [...ev.target.uploadResponse]; // update uploadResponse after API calls are completed
        }, 1000);
      }}"
      @file-list-changed="${ev => {
        if (!ev.detail.newFiles[0]) {
          return;
        }
        ev.target.uploadResponse[
          ev.target.uploadResponse.findIndex(file => file.name === ev.detail.newFiles[0].name)
        ].status = 'LOADING';
        ev.target.uploadResponse = [...ev.target.uploadResponse]; // update uploadResponse after API calls are completed

        if (ev.detail.newFiles[1]) {
          ev.target.uploadResponse[
            ev.target.uploadResponse.findIndex(file => file.name === ev.detail.newFiles[1].name)
          ].status = 'LOADING';
          ev.target.uploadResponse = [...ev.target.uploadResponse]; // update uploadResponse after API calls are completed
        }
        setTimeout(() => {
          ev.target.uploadResponse[
            ev.target.uploadResponse.findIndex(file => file.name === ev.detail.newFiles[0].name)
          ].status = 'SUCCESS';
          ev.target.uploadResponse = [...ev.target.uploadResponse]; // update uploadResponse after API calls are completed
        }, 3000);

        setTimeout(() => {
          if (ev.detail.newFiles[1]) {
            const file1Status = {
              name: ev.detail.newFiles[1].name,
              status: 'FAIL',
              errorMessage: 'error from server',
            };
            ev.target.uploadResponse[
              ev.target.uploadResponse.findIndex(file => file.name === ev.detail.newFiles[1].name)
            ] = {
              name: ev.detail.newFiles[1].name,
              status: 'FAIL',
              errorMessage: 'error from server',
            };
            ev.target.uploadResponse = [...ev.target.uploadResponse];
          }
        }, 3000);
      }}"
    >
    </lion-input-file>
  `;
};

Drag and Drop

Set the enableDropZone parameter to true to use the drag and drop functionality in the component.

Drag and drop the files to be uploaded to the server.

export const dragAndDrop = () => {
  return html`
    <lion-input-file
      label="Passport"
      name="myFiles"
      accept=".png"
      max-file-size="1024000"
      enable-drop-zone
      multiple
      .validators="${[new Required()]}"
    >
    </lion-input-file>
  `;
};

Posting file to API using axios

You can retrieve the uploaded files in file-list-changed event or from modelValue property of this component

To submit files you can refer to the following code snippet:

const formData = new FormData();
const inputFile = document.querySelector('lion-input-file').modelValue;
formData.append('file', inputFile.querySelector('input').files);
axios.post('upload_file', formData, {
  headers: {
    'Content-Type': 'multipart/form-data',
  },
});