<form id="my_form" action="/pokemons/1/upload_image" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="OXX9vC6NU6A9zhuC6vgYOpAuBa9uPydPUaivN2lhCYNMB29euJuUjix2B-qO5Ngxviu_A9mdv_WC0j2LY-5Qxg" autocomplete="off" /> <input value="my_form" autocomplete="off" type="hidden" name="form_id" id="form_id" /></form><turbo-frame id="pokemon_image_upload"> <div class="lui-input-file-container" id="looposui-inputs-file-single_pokemon_1_image_5793709857" data-controller="input-file" data-input-file-form-value="my_form" data-input-file-delete-path-value="/pokemons/1/delete_image"> <div class="lui-input-file"> <label class="lui-button lui-button--neutral--secondary lui-button--size-tiny w-fit w-fit" data-controller="lui--button" for="looposui-inputs-file-single_pokemon_1_image_5793709857-file-input"> <i class="lui-button__icon lui-button__icon--tiny fa-regular fa-file-arrow-up" data-lui--button-target="leadingIcon"></i> <span class="lui-button__text" data-lui--button-target="text"> Select file </span> </label> <input name="pokemon[image]" data-input-file-target="file" data-action="change->input-file#upload" id="looposui-inputs-file-single_pokemon_1_image_5793709857-file-input" class="hidden" type="file" form="my_form" accept="text/csv text/comma-separated-values text/plain application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"> <div class="flex flex-1 min-w-0 justify-end items-center gap-[2px]"> <span class="lui-input-file__text"> No file chosen </span> <span class="lui-input-file__spinner"> <i class="fa-regular fa-spinner"></i> </span> </div> </div> </div></turbo-frame>File Input Usage
The LooposUi::Inputs::File component is designed for flexible file uploads with support for multiple files, custom endpoints, and advanced UI interactions.
Basic Example
<%= render LooposUi::Inputs::File.new( name: "my_file", upload_path: file_test_upload_path, download_path: file_test_download_path, delete_path: file_test_remove_path, multiple: false, mode: :form) %>Multiple Files Example
<%= render LooposUi::Inputs::File.new( name: "attachments", upload_path: file_test_upload_path, download_path: file_test_download_path, delete_path: file_test_remove_path, multiple: true, files: [OpenStruct.new(name: "example.pdf")], mode: :autosubmit) %>Integration Notes
- The component works best with the
input-fileStimulus controller for auto-submit and UI feedback. - Use the
filesoption to display already uploaded files (for example, after a page reload). - The
delete_pathshould point to an endpoint that accepts afile_nameparameter for deletion. - The
download_pathshould point to an endpoint that accepts afile_nameparameter for download.
UI Features
- Modal confirmation for file deletion.
- Spinner and feedback for upload progress.
- Customizable with additional data attributes.
<%= form_with id: "my_form", url: upload_image_pokemon_path(Pokemon.first), method: :post do |f| %> <%= f.hidden_field :form_id, value: f.id %><% end %><%= turbo_frame_tag "pokemon_image_upload" do %> <%= render LooposUi::Inputs::File.new( model: Pokemon.first, attribute: :image, delete_path: delete_image_pokemon_path(Pokemon.first), form_id: "my_form" ) %><% end %>No notes provided.
No params configured.
File Input Component
Note: You must create and use your own controller to handle file uploads, downloads, and deletions. The
delete_pathoption should point to your custom endpoint. This component does yet not provide backend logic for file handling.
The LooposUi::Inputs::File component provides a file input for uploading, downloading, and deleting files.
It supports single and multiple file selection. It is to be used only with Models and attributes.
NOTE: multiple file uploader is currently disabled. Will be enabled in a future release (task LOOPOS-32331)
Options
| Option | Type | Default | Description |
|---|---|---|---|
model |
ActiveRecord::Base | — | Model object for file association. |
attribute |
Symbol | — | The name of the hasoneattached or hasmanyattached relation |
form_id |
String | — | The form element's ID to associate the input with. Use if your form input is outside the form |
delete_path |
String | — | Path to delete files. |
accept |
Array of Symbols/Strings | [:csv, :txt, :xlsx] |
Accepted file types (e.g., [:csv, :png], ['image/*'], or :all). |
accept: Can be a list of string or symbols. Will be interpreted as file extensions (eg: :png -> .png) or MIME types (eg: image/png)
Note: Depending on the attribute, the input will be renderd as a multiple file uploader or a single file uploader.
Any form inputs you have in your form will be also sent in the payload.
Usage
Example usage in the view:
<%# Single file uploader %><%= form_with url: upload_image_pokemon_path(Pokemon.first), method: :post do |f| %> <%= turbo_frame_tag "pokemon_image_upload" do %> <%= render LooposUi::Inputs::File.new( model: Pokemon.first, attribute: :image, delete_path: delete_image_pokemon_path(Pokemon.first), ) %> <% end %><% end %><%# Multiple file uploader %><%= form_with url: upload_images_pokemon_path(Pokemon.first), method: :post do |f| %> <%= turbo_frame_tag "pokemon_images_upload" do %> <%= render LooposUi::Inputs::File.new( model: Pokemon.first, attribute: :images, delete_path: delete_images_pokemon_path(Pokemon.first), ) %> <% end %><% end %><%# With form_id %><%= form_with id: "my_form", url: upload_image_pokemon_path(Pokemon.first), method: :post do |f| %> <%= f.hidden_field :form_id, value: f.id %><% end %><%= turbo_frame_tag "pokemon_image_upload" do %> <%= render LooposUi::Inputs::File.new( model: Pokemon.first, attribute: :image, delete_path: delete_image_pokemon_path(Pokemon.first), form_id: "my_form" ) %><% end %>And in your controller:
include ActiveStorage::SetCurrent def upload_image pokemon_params = params.require(:pokemon).permit(:image) if @pokemon.update(pokemon_params) render(file_uploader) else # In a real app, you would also want to handle errors here render(file_uploader) end end def delete_image # No need to attachment management here, only one image per pokemon @pokemon.image.purge render(file_uploader) end def upload_images pokemon_params = params.require(:pokemon).permit(images: []) if @pokemon.update(pokemon_params) render(file_uploader_multiple) else # In a real app, you would also want to handle errors here render(file_uploader_multiple) end end def delete_images blob = ActiveStorage::Blob.find_signed(params[:attachment_signed_id]) blob.attachments.each(&:purge) render(file_uploader_multiple(context: :action)) endIn the response (which will be a turbo request) you shuold render the component again:
# Could've been a partial instead of a methoddef file_uploader { turbo_stream: turbo_stream.replace( params[:uploader_id], LooposUi::Inputs::File.new( model: @pokemon, attribute: :image, delete_path: delete_image_pokemon_path(@pokemon), form_id: params[:form_id], # used in the outisde_form preview ), ), }end# Could've been a partial instead of a methoddef file_uploader_multiple(**kwargs) { turbo_stream: turbo_stream.replace( params[:uploader_id], LooposUi::Inputs::File.new( model: @pokemon, attribute: :images, delete_path: delete_images_pokemon_path(@pokemon), **kwargs, ), ), }endIn a real app, you probably will want to use the same partial for the initial render and for the response.