Ask A Question

Notifications

You’re not receiving notifications from this thread.

CoffeeScript Class + Partial remotly reloaded + Binding

Thomas M asked in General

Hi,

I'm currently rewriting my js file following the class encapsulation episode.
It works well but I'm struggling on with the binding/response for my image uploader.

My view contains a form with 4 file-fields, each for a specific image.
While uploading, a progress bar appears then a success message.
If a picture was already uploaded, a preview and a 'Remove' link appears.

When I click the remove link, image1#destroy is colled remotly and attachement is cleaned, a new one is built and the partial containing the file field is reloaded.
However, no matter what I try, I can't not figure how to make the new file field considered by the uploader, the consequence being that I lost my progress bar and preview.

Here is the relevant code :

users/update.html.erb

<%= form_for(resource, as: resource_name, method: :put, url: registration_path(resource_name),
html: { class: 'form-horizontal directUpload black', role: 'form', multipart: true,
data: { 'form-data' => (@s3_direct_post.fields), 'url' => @s3_direct_post.url }
}) do |f| %>

        <div data-behavior="image-profile-container">
          <%= render partial: "documents/vignette", locals: { resource: @user, attachment: @user.avatar } %>
        </div>

        <div data-behavior="image-logo-container">
          <%= render partial: "documents/vignette", locals: { resource: @user, attachment: @user.logo } %>
        </div>

...
... submit
<% end %>

documents/_vignette.html.erb

<%= fields_for resource do |r| %>
  <%= r.fields_for attachment.class.name.underscore.to_sym do |v| %>
    <%= v.file_field(:attachment, accept: "image/gif,image/png,image/jpg,image/jpeg", class: "vignette_file", data: { behavior: "upload-image-#{resource.class.name.underscore}-#{attachment.class.name.underscore}-#{attachment.id}" } ) %>
  <% end %>
<% end %>
<% if attachment && attachment.attachment_file_size != nil %>
  <%= link_to t("vignettes.delete"), { controller: "#{attachment.class.name.underscore.pluralize}", action: 'destroy', user_id: current_user }, { method: :delete, confirm: "Are you sure?", data: {confirm: "Are you sure?", behavior: "delete-image" }, remote: true } %>
  <%= t "vignettes.delete" %>
<% end %>

views/avatars/destroy.js

$("[data-behavior='image-profile-container']").html("<%= j render partial: 'documents/vignette', locals: { resource: @user, attachment: @user.profile } %>");
-- I tried to add new UploadImage() here

js/image_uploader.coffee

class UploadImage
  constructor: (elem) ->
    fileInput = $(elem)
    form = $(fileInput.parents("form:first"))
    submitButton = form.find 'input[type="submit"]'
    progressBar = $("<div class='bar'></div>")
...
$(document).on "page:change", ->
  $(".directUpload").find("input:file").each (i, elem) ->
    console.log "input is: " + elem
    new UploadImage(elem)

Everything seems to work, except that the new file field isn't considered by my uploader.
What is the best way to deal with the binding of a new element in the dom ?
I already tried live, ready, a[data-remote], to call UploadImage passing the elem, with no luck.
Thank you for your help

Reply

I found my mistake : you have to declare your coffeeScript class globally to allow a call from js.

In case of someone encounters the same problem, I solved it like that :

assets/javascripts/myclassfile.coffee

  @ClassName (notice the @, you can use window.ClassName too)
    constructor: (elem) ->

views/users/destroy.js

  $("[data-behavior=image-profile-container]").html("<%= j render: 'your_partial' %>");
    new UploadFile($("[data-behavior='upload-image-user-avatar']"));
Reply

Hey Thomas,

This is a good question. The basic trouble (if I'm understanding correctly) is that when you delete the file, you're re-rendering the partial but there is no UploadImage for that new element that you rendered.

I think you're correct in that you should be doing a new UploadImage(elem) in the destroy.js. My guess is that class isn't accessible globally, causing that line of code to fail. One of the issues with the JS response from a remote call is that it often hides the errors that happen, so it's harder to debug.

If it actually is that the UploadImage class isn't globally accessible, then you should need to simply change the class UploadImage to class @UploadImage to make that globally available.

Give that a shot and see if that works for you!

Also this is an okay solution for debugging that JS that you return: https://www.alfajango.com/blog/rails-js-erb-remote-response-not-executing/ You basically inspect the response in Chrome, run the JS manually in the console, and fix the errors. Not ideal, but hey, it works.

Reply

Hey, thank you for your answer.
I came to the same solution, using @ClassName or window.ClassName (and detailing the problem & solution in a forum post). Everything is working fine now :)

Reply

Awesome! Glad that worked. :D

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 81,842+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more.

    © 2024 GoRails, LLC. All rights reserved.