My Rails app contains an Excel-like editable table, using best_in_place fields throughout. They work perfectly fine upon page load and I have no problem calling coffeescript functions on ajax:success after a user edits a best_in_place field.
Since this is an Excel-like table, users can click a link to add a new placeholder row to the table. My create.js.erb file adds the row without a page reload. The new row has several table cells with best_in_place functionality, and updating them updates the database, so far so good.
The problem is that when a user then edits a best_in_place fields in one of the new rows, ajax:success does not fire, therefore my coffeescript functions do not initiate.
I've confirmed the new table cells have the exact same class and data attributes as the existing table cells (ajax:success is read on the class name).
Question- in this scenario, how do I initiate my coffeescript function if ajax:success is not being recognized?
Milestone controller, both create and update actions
def create
#payment_schedule = PaymentSchedule.find(params[:id])
build_default_milestone # this builds a placeholder milestone
if #milestone.save
increment_milestone_positions # this relates to a sortable table
respond_to do |format|
format.js
format.html { redirect_to :back }
end
else
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
end
def update
#milestone = Milestone.find(params[:id])
respond_to do |format|
if #milestone.update(milestone_params)
update_milestone_amounts
format.html { redirect_to(:back) }
format.json { respond_with_bip(#milestone) }
else
format.html { redirect_to(:back) }
format.json { respond_with_bip(:back) }
end
end
end
Create.js.erb
$('#milestone-body').prepend('<%= escape_javascript(render partial: 'row_add', locals: { milestone: #milestone }) %>');
Coffeescript function
update-percent is the class name of the best_in_place field in question. This function fires perfectly for the existing rows, just not the new ones.
$('.update-percent').on 'ajax:success', (event, data, status, xhr) ->
...
Any suggestions on how to solve this?
The issue is resolved. The problem was not that ajax:success wasn't firing, it's that (for reasons still unknown) I had to read ajax:success at the table body level instead of the table cell in question. Apparently this is a somewhat common issue, where when adding new elements to the DOM after it's originally loaded, you need to read ajax:success higher up the DOM.
Thank you to those who provided input.
are you using turbo links? I think best in place binds to document ready which acts funky if you are using turbo links. If so, remove it from your application.html.erb and application.js file and try again.
Related
I have a page that lets one create records - if the validations aren't satisfied, it redirects to the same page and shows an error message. Here's that snip from the controller:
def create
#signature = Signature.new(signature_params)
if #signature.save
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
end
The trouble is, this is resulting in a full page refresh - so the error message isn't visible because the input form is placed under the fold of the page. I can place it at the top of the page, of course, but is there a way to show the message without reloading the page? Thanks.
OK, so here's what I've settled on:
1) I'm handling validation on the client side with HTML5 "required" attributes - they were created for this explicit purpose and no other gems or plugins are needed. They are supported in all major browsers. Details in this article.
2) I've moved the error messages to the top of the page to handle the case in which a user either is on an old or mobile browser or has JavaScript disabled. Error messages must work with a complete request-response cycle (even if this means re-loading the page) before they work with anything else - this is the unobtrusive JavaScript approach.
3) For the AJAX version, I'm going to be using remote: => true on the form element as explained in the Rails guides. I might be making this open source once I'm done with the callback part of it, and will post a link here.
Obviously, handling errors with flash is the most uniform & DRY way to show the user what's going on, but if you're willing to think outside the box, you'll be able to use Ajax to accomplish a similar job by just handling the errors yourself:
Code Example
#app/controllers/signatures_controller.rb
def create
#signature = Signature.new(signature_params)
if #signature.save
#success = "true"
end
respond_to do |format|
format.js { #errors = error_messages(#signature) }
format.html {
if #success.defined?
redirect_to "/thanks"
else
redirect_to :back, :notice => error_messages(#signature)
end
}
end
end
#app/views/signatures/create.js.erb
<% unless #success.defined? %>
alert(<%=j #errors.inspect() %>)
<% end %>
#app/assets/javascripts/signatures.js
$(document).on("submit", "#signature_form", function() {
$.ajax({
url: "/signatures"
type: "POST"
data: $(this).parent().serialize(); //serialize the form (not the button)
error: function() {
alert("Sorry, there was an error!");
}
});
});
You'd actually be better using JSON for this. If you like the idea, I can refactor it to include JSON for you!
I am using Rails remote true for ajax calls, but I have error in handling the response form controller.
What I am doing is I have placed a form in partial which I render in the view for the first time, and I have also given it a id in html options for form_for. I have also applied some jQuery on that form like on save and cancel button and one script for date picker on one of its field.
As I handle the response from server using format.js for rendering js file, in which I have placed the code as follows
if #vairable.save
format.js { render 'some-file' }
else
format.js { render 'something'}
end
If the variable saved successfully I close the form properly, but if validations occur I render the form again by writing JavaScript code in js.erb file. I have problem in handing this response. The errors are displaying properly but when the form renders again with errors, the jQuery events applied to its fields and button, do not work. The jQuery events don't take place.
This problem only occurs when the model validations occurs and the validation rails are displayed with the new render form.
You should wrap your response in a respond_to block like this:
respond_to do |format|
if #variable.save
format.js { render 'some-file' }
else
format.js { render 'something'}
end
end
Edit: Your jQuery tags should be changed to $(document).on(...). Eg.
#change this:
$("#your-target-id").click(function(){
alert("This doesn't work after your have submitted via ajax")
});
#to this:
$(document).on("click", "#your-target-id", function() {
alert("This should work after ajax submission, and when errors are displayed");
});
I have a model Question which has_many Options.
In an action I modify the Options of the a Question object and then return the Question object as a json. (This is used in an ajax call). The problem is, the returned json is not up to date and doesn't reflect the modifications I made.
def action
question = Question.find(:id)
question.options.each do |op|
op.blah += 1
end
respond_to do |format|
if question.save()
format.json { render json: {question: question.to_json(:include => :options)} }
else
blah blah
end
end
end
Weird because you edit the items in place, then save (and verified that they are saved to db --since refresh works). So the render to json should work.
You could always do explict reload calls, like
format.json { render json: {question: question.reload.to_json(:include => :options)} }
But it would be very weird if that would be the cause, since you actually just saved that data.
But yesterday I had a similar problem, and actually what happened that I did a replaceHtml of a div, but my inserted data did not have the same surrounding div. So the first time it updated correctly, and the second time it just showed the old data (while in fact it did nothing, because it did not find the div to replace).
So in short: if simply refreshing works, imho it is not the database and it is not caching, but will most likely be something in your javascript: the updating of the screen that is going wrong.
Hope this helps.
question.options.each do |op|
op.blah += 1
op.save
end
You forgot to save options after modification.
I'm working through Agile Web Development with Rails, Edition 4 with some tweaks (mostly just naming variations), and I've arrived at Iteration F2. In this iteration, you modify the index button with :remote => true, you add format.js to the respond_to section of the controller, and you generate a js.rjs file to execute the AJAX render. Or at least that's my interpretation of it. The goal of these steps is to have a cart (in this case, a team) in the sidebar update using AJAX when adding new line items (in this case, members)
In my case, I'm trying to add members to a team. Her's some code snippets I've added:
index.html.erb:
<%= button_to 'Add to Team', members_path(:player_id => player),
:remote => true %>
members_controller:
def create
#team = current_team
player = Player.find(params[:player_id])
#member = #team.add_player(player.id)
respond_to do |format|
if #member.save
format.html { redirect_to(nba_url) }
format.js
format.xml { render :xml => #member,
:status => :created, :location => #member }
else
format.html { render :action => "new" }
format.xml { render :xml => #member.errors,
:status => :unprocessable_entity }
end
end
end
create.js.rjs:
page.replace_html('team', render(#team))
The page is able to render, and I'm still able to click the button to add members to the team. However, the AJAX isn't working. When I reload, I can still see that the members have been added in the sidebar. All of the other team functionality remains, as I'm able to empty the team and add whichever members I wish. When I check the server log, I find the following error:
Error:
ActionView::Template::Error (undefined local variable or method `page' for #<
#<Class:0x413e1b8>:0x413cb20>):
1: page.replace_html('team', render(#team))
app/views/members/create.js.rjs:1:in `block in _app_views_members_create_js_rj
s___908569197_34199712_807066544'
app/views/members/create.js.rjs:1:in `_app_views_members_create_js_rjs___90856
9197_34199712_807066544'
app/controllers/members_controller.rb:47:in `create'
Based on this it seems like it has found the create.js.rjs but is having trouble interpreting it. I'm not sure what the weird symbols are in front of page.
Edit: I've also found that if I view the source code before and after clicking the button, the button is indeed refreshing the code and adding the desired items. The problem seems to be exclusively in trying to refresh the partial.
Any help is appreciated. Thanks!
It seems your rjs file has some invalid bits at the start. Maybe try to re-create the file?
What did you expect to do with render(#team) ?
I've taken a look at the action view's method "render" and didn't found how you were expecting it to function. Maybe there is another functionality that you are aware and I don't.
You can also use erb and not rjs, using it just like a view
Along the lines of Bert Goethal's answer, is your editor saving your text file as UTF-8 with BOM?
A BOM will add two unicode encoded characters to the beginning of the file, and that might be where those are coming from...
I was putting together a quick inline editing feature in my first Rails app and just as I was getting it working it occurred to me that I may be violating RESTful principles. The edit updated an image name. To do so, it submits, via PUT to Image#update and passes the new modified name as image[name].
The database gets updated properly, but I need that value back so that my markup can reflect the name change. To do that, I was calling /images/:id.json, but that got me wondering whether a PUT request can "validly" (in that RESTful sort of way) return a value like this.
Thoughts?
Update: For whatever it's worth, I'm using jQuery and the jEditable plugin to do the inline editing. Here's my jEditable code:
$(document).ready( function() {
$('h2').editable(
'/images/' + $('#image-id').val() + '.json',
{
method: 'PUT',
name: 'image[name]',
submitdata: { authenticity_token: $('#auth-token').val() },
submit: 'Save',
cancel: 'Cancel'
}
);
})
And my Image#update method as it exists right now:
def update
#image = Image.find( params[:id] )
if #image.update_attributes( params[:image] )
flash[:notice] = "Successfully updated image."
respond_to do |format|
format.html { redirect_to #image }
format.json { render :json => #image.to_json }
end
else
render :action => 'edit'
end
end
If your concern is just that your update method with JSON provide a response body and not just a 200 OK (Rails's head :ok) then I don't think you need to be worried. The default response is 200 OK so the only difference between what you're doing and what Rails does by default (in its scaffolds) is that you're also including a response body. As far as I can tell proper REST etiquette only requires that you return a 200 OK and doesn't care about the response body, which is in line with what you're doing.
Beyond that all your code looks excellent.