Rails 7 - Podgląd uploadowanego obrazka
🖼️

Rails 7 - Podgląd uploadowanego obrazka

Rails 7 niosą ze sobą nie tyle powiew świeżości w budowaniu aplikacji webowych, co prawdziwą rewolucję. jQuery już dawno zostało wykurzone, teraz pora na mocne uniezależnienie się od frameworków frontendowych i miliona linii Javascript do ogarnięcia widoków.

Implementowałem ostatnio upload w aplikacji korzystającej z Hotwire i Stimulusa. Jestem more than happy, że aplikacja ma już dość złożony UI, a wciąż jest tam praktycznie kilkanaście linii JS.

Stanąłem przed problemem zrobienia podglądu uploadowanego obrazka jeszcze przed zapisaniem formularza. Scenariusz wygląd tak, że użytkownik klika Choose file, wybiera obrazek, który automatycznie wyświetla się w miejscu przeznaczonym na podgląd.

A tak to ogarnąłem z wykorzystaniem Stimulusa, którego zaczynam coraz bardziej lubić, ze względu na jego banalność.

Na potrzeby przykładu stworzyłem taki mega uproszczony widok.

<%= content_tag :main, class: "container mx-auto pt-6 h-screen" do %>
  <div class="flex flex-col">
    <label class="mb-4">Załącz obrazek</label>
    <%= file_field_tag :image %>
  </div>
  <div class="flex flex-col items-center justify-center border mt-12 h-4/5">
    <%= image_tag '', alt: "Tu będzie Image preview",
     class: 'h-5/6 w-5/6 object-contain object-center' %>
  </div>
<% end%>

Następnie utworzyłem Stimulus controller ImageUploadPreview.

Ustaliłem dwa targety:

  • input - czyli file input
  • preview - tag img, gdzie wyświetlam podgląd wybranego obrazka

Następnie w funkcji setImagePreview zawarłem całą logikę.

export default class ImageUploadPreview extends Controller {
  static targets = [ "input", "preview" ]
  
  setImagePreview() {
    const input = this.inputTarget
    const preview = this.previewTarget
    
    if (input.files && input.files[0]) {
      const reader = new FileReader()
      const image = input.files[0]

      reader.onload = () => {
        preview.src = reader.result
      }

      reader.readAsDataURL(image)
    }
  }
}

Po kolei:

  • kod sprawdza, czy jakikolwiek plik został wybrany
  • tworzy instancję FileReadera, dzięki której mogę pobrać lokalizację wybranego obrazka
  • tworzę callback, który podpinam do reader.onload - eventu, wywołanego, kiedy wybrany obrazek zostanie załadowany w readerze
  • w callbacku ustawiam src podglądu jako resultat z readera
  • wczytuję obrazek za pomocą readera, żeby dostać jego URL

Tyle, koniec magii.

Teraz tylko trzeba podpiąć controller, targety i akcję do elementów w widoku.

<%= content_tag :main, class: "container mx-auto pt-6 h-screen",
  data: { controller: 'image-upload-preview' } do %>
  <div class="flex flex-col">
    <label class="mb-4">Załącz obrazek</label>
    <%= file_field_tag :image,
      data: { 
       target: 'image-upload-preview.input', action: 'image-upload-preview#setImagePreview'
      } %>
  </div>
  <div class="flex flex-col items-center justify-center border mt-12 h-4/5">
    <%= image_tag '', alt: "Tu będzie Image preview",
     class: 'h-5/6 w-5/6 object-contain object-center',
     data: { target: 'image-upload-preview.preview' } %>
  </div>
<% end%>

  • data-controller jest ustawiony w kontenerze dla inputa i podglądu
  • file_field_tag i image_tag są oznaczone jako targety input i preview
  • do inputa podpięta jest akcja setImagePreview

Końcowa funkcjonalność wygląda tak:

image