Skip to main content

CoffeeScript Class + Partial remotly reloaded + Binding

General • Asked by Thomas M

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


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']"));

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.


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 :)


Awesome! Glad that worked. :D


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 20,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.