Rails: 5.2.0, Ruby: 2.4.1p111, Browser: Chrome 66.0.3359.181, jquery-rails gem: 4.3.3, Turbolinks disabled.
I am following this AJAX on Rails with Unobtrusive JS tutorial and have reached the end, but have some strange behavior that I can't figure out.
The app uses UJS to allow a user to add new records to the bottom of list via AJAX without having to update the entire page like Rails normally would. Adding new records works fine, but when I click on 'Delete', the JS function in application.js is not triggered so nothing seems to happen, but when I refresh the page the record is gone. If I add a new record, refresh the page and then try and delete the record JS is triggered and the record is deleted/removed from the list immediately.
The web page is very simple:
<!–– index.html.erb ––>
<h1>Tutorials</h1>
<ul id=tutorials>
<%= render @tutorials %>
</ul>
<%= form_with model: Tutorial.new, data: {'js-tutorial-form' => true} do |form| %>
Title <%= form.text_field :title %><br>
URL <%= form.text_field :url %><br>
<%= form.submit %>
<% end %>
with a small partial:
<!–– _tutorial.html.erb ––>
<li data-js-tutorial-id=<%= tutorial.id %>>
<%= tutorial.title %> <%= link_to "Delete", tutorial, remote: true, method: :delete %>
</li>
all the JS is in the application.js file
$(document).ready(function() {
$('[data-js-tutorial-form]').on("ajax:success", function(event, data, status, xhr){
var tutorial = $(xhr.responseText).hide();
$('#tutorials').append(tutorial);
tutorial.fadeIn(2000);
});
$('[data-js-tutorial-id]').on("ajax:success", function(event, data, status, xhr){
var tutorial_id = xhr.responseJSON.id;
$('[data-js-tutorial-id=' + tutorial_id + ']').hide();
});
});
the only other file is the controller
class TutorialsController < ApplicationController
def index
@tutorials = Tutorial.all
end
def create
@tutorial = Tutorial.new(params[:tutorial].permit(:title, :url))
if @tutorial.save
render partial: "tutorial", locals: {tutorial: @tutorial}
end
end
def destroy
@tutorial = Tutorial.find(params[:id])
@tutorial.destroy
render json: @tutorial
end
end
What I don't get is that the HTML source code is identical before and after the page is refreshed, but I have confirmed using Chrome developer tools that the $('[data-js-tutorial-id]') is not being called until unless the page is refreshed. I have used byebug in the controller to confirm that is stepping through correctly. Any ideas?
Your problem is the javascript listener is bound to the <li>
tag, not to the actual link. The listener never had any chance to fire.
Personally I'd change the destroy
method, why return the tutorial you just deleted?
def destroy
@tutorial = Tutorial.find(params[:id])
@tutorial.destroy
render json: {success: true}
end
Secondly, your partial looks wrong, didn't test though
<!–– _tutorial.html.erb ––>
<li>
<%= tutorial.title %> <%= link_to "Delete", tutorial, remote: true, method: :delete, "data-js-delete": tutorial.id %>
</li>
which of course needs a change to the javascript:
$('[data-js-delete]').on("ajax:success", function(event, data, status, xhr){
$(event.target).closest('li').hide();
});