Question

I'm basically trying to create a File Manager for my rails app similar to how WordPress's 'Media' section functions. I currently have a model called Asset which is where users go to upload various images. In various other models I have a field for images which is just a text field. I'm hoping that when a user clicks on the text field, it will open up an 'asset manager' in a modal window with all of the images contained within Asset showing. When a user clicks one of the images, it should close the modal and populate the text field with the URL of the selected image.

I have a model called Events which contains a text field that I was talking about. In the new action I respond to js, in which I load a partial that contains all of the assets in a modal window, just like I'd expect it to. My only problem is that I'm doing this via a $.getScript call and I can't call any additional javascript to load the image url back into the text field, I'm guessing it's because the objects don't exist yet. Anyway, on to the code:

controller

def new
  @event = Event.new
  @asset = Asset.all
end
respond_to do |f|
  f.html
  f.js
end

new.js

$('.acontainer').html('<%= render @asset %>');

page.js

//when a user clicks the image field, show the asset partial
$('.image-field').click(function() {
    $.getScript('edit.js');
});

// when a user clicks an image, add it's src to the image field <-- does nothing
$('.actonainer img').click(function() {
    $('.image-field').val($(this).attr('src'));
});

Any ideas would be much appreciated, especially if anyone knows of a better way to do this. :-)

Was it helpful?

Solution

Looks like you may be binding the click event to the .acontainer img DOM elements that don't exist at the time, since you retrieve them when you click .image-field.

In general, you should use jQuery.on and bind your events to the document root. This is called delegation. When you click on a link, the event will bubble up and hit the document node, and it will handle the event. This way, as you add DOM elements via ajax or directly in the DOM, they will still get the event handler you intended.

$(function() {
  $(document).on('click', '.acontainer img', function(event) {
    $('.image-field').val($(this).attr('src'));
  })
});

However, there's a bigger issue here. It sounds like you have multiple .image-field elements on the page. What you may need to do in your .image-field handler is return HTML rather than js, so you can bind an event to the root element of the newly added HTML. That block will identify the specific input field, whose value it can set once an image is clicked.

$(function() {
    $(document).on('click', '.image-field',
    function(event) {
        var field = $(this);
        $.ajax('/edit', {
            success: function(data, textStatus, jqXHR) {
                var blah = $(Dialog.new(data));
                // Create a modal and return its root DOM element
                blah.on('click', 'img',
                function(event) {
                    field.val($(this).attr('src'));
                });
            }
        })
    });
});

A final note, you might want to consider using a RESTful controller for your Assets. You might not want to fetch one resource (an Asset) from a controller named EventsController, especially using the edit action. You want GET AssetsController::index to return a list of assets.

OTHER TIPS

Nothing is happening when you click the images because the initial query for ".acontainer img" isn't returning any elements, and so the event handler isn't attached to anything. They haven't been loaded yet.

You may want to try jQuery's live method. It attaches handlers to elements present at page load and elements later added to the DOM.


Update: As @aceofspades points out, live is deprecated in jQuery 1.7. Use on instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top