I have various values in a table that need to be updated after a DOM element is updated using best_in_place. How can you trigger a javascript action like "create.js.erb" called "update.js.erb" after a best in place update?
For example, I have a table of item prices, and I need the table's "total" field to update after a user updates an individual item quantity.
I've seen many people asking this question, but haven't found a satisfactory solution. I found the best approach is to create a javascript function that watches for a particular DOM element to update after an ajax:success, and then use jQuery to update a specified DOM element.
In the controller of the item being updated, I package a JSON response that includes all of the information my javascript will need in order to update DOM elements.
You'll see I also render the new table row's partial as an HTML string, and pass this string along with the JSON response.
___Item Controller_____
respond_to do |format|
if #item.update_attributes(params[:item])
#json response variables to refresh table row after update
id = [#item]
new_row = render_to_string('items/_item.html', :layout => false, :locals => { :item => #item })
#----------json-variables-----------#
format.json { render :json => { new_row: new_row, id: id, :status => 200 }}
format.js
else
format.json { render :json => #item.errors.full_messages, :status => :unprocessable_entity }
format.js
end
end
Then in a .js file that gets loaded with the page (like application.js), I include a function that watches for a table row with class "row_class" to be updated.
$(document).ready(function(row_class, row_id_prefix){
$("." + row_class).on("ajax:success",function(event, data, status, xhr){
var parsed_data = jQuery.parseJSON(data); //parses string returned by controller into json object
var id = parsed_data["id"];
var new_row = parsed_data["new_row"]; //this is an html string that replaces the existing table row
$('tr#' + row_id_prefix + id).replaceWith(new_row);
$('tr#' + row_id_prefix + id).effect('highlight');
$('.best_in_place').best_in_place(); //activates in-place-editing for newly added content.
}));
You can modify the jQuery to update any DOM element you need. Also, if you need the results of ruby methods called on objects (such as a humanized total, tax total, etc) you can define these as variables in the JSON object in the controller.
Ultimately it'd be best if best_in_place would trigger (in this case) the items/update.js.erb script, but it doesn't look like that's on the roadmap.
Related
I'm going through my application and making sure that it responds properly to validation issues. In this particular situation, when creating a new delivery order you must use a unique number when creating one. I've found that when I put in a duplicate number the system displays an error.
The error is:
undefined method `purchase_request_fundings' for nil:NilClass
I understand why this error is occurring but I don't understand how to fix it. The error occurs because after the application finds a validation error, it tries to kick the user back to the delivery order new function with an friendly error message to display to the user however, the #purchase_request object is not being instantiate with any data when it's get back to the new function. I'm thinking it doesn't see the parameters it needs to find and set the object properly and thus the object - #purchase_request - is left as nil.
My Code:
Controller
def new
#delivery_order = DeliveryOrder.new(:purchase_request_id => params[:purchase_request])
#purchase_request = PurchaseRequest.find(#delivery_order.purchase_request_id)
end
def create
#delivery_order = DeliveryOrder.new(params[:delivery_order])
respond_to do |format|
if #delivery_order.save
params[:discrete_tasks].each do |key, value|
DeliveryOrderFunding.create(
:delivery_order_id => #delivery_order.id,
:discrete_task_id => key,
:amount => value
)
end
format.html do
flash[:success] = "Delivery Order was succesfully created"
redirect_to #delivery_order
end
else
format.html { render action: "new" }
end
end
end
View includes
= hidden_field_tag 'delivery_order[purchase_request_id]', params[:purchase_request]
Also tried
= hidden_field_tag :purchase_request, params[:purchase_request]
If I understand the situation correctly, your call to render action: "new" doesn't call the new method in your controller, it only renders the new view, using the instance variables that your create method has set. So you need to do something like:
def create
#delivery_order = DeliveryOrder.new(params[:delivery_order])
respond_to do |format|
if #delivery_order.save
...
else
format.html do
#purchase_request = PurchaseRequest.find(#delivery_order.purchase_request_id)
render action: "new"
end
end
end
end
I'm trying to wrap my around a task and haven't found anyone who has address this issue.
I'm created an awards nomination system.
Anon user submits nomination
Admin user generates a merged letter from template and edits letter
Letter text is saved to database to later generate PDF
I read that you can render an ERB text to a variable. This is working great but then I'm stuck with the text because of the Double Render Error.
def generate_letter
#submission = Submission.find(params[:id])
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
#submission.save
redirect_to #submission
end
Is there a better way to generate this text for the database or a workaround to redirecting? I was trying to avoid keeping my merge template in the code and ERB seems a nice way to handle it.
You need to use render_to_string. This line
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
should be
#submission.letter_text = render_to_string( template: 'submissions/generate_letter.text.erb', layout: false )
I have a prescription model in my Rails 3 application. I am trying to work out the best method of allowing records to be duplicated, but allowing the user to "review" the duplicate before it's saved.
I have read a number of questions/answers on SO (such as this one) which explain how to duplicate/clone the record and then save it - but none which explain how to show the form before save.
Reading the Rails API is appears the clone method is available.
Reading other questions and answers shows that is can be done but there is no example code apart from:
new_record = old_record.dup
The controller code I am currently working with is as follows (the model doesn't have any relationships):
# POST /prescriptions
# POST /prescriptions.json
def create
#prescription = Prescription.new(params[:prescription])
#prescription.localip = request.env['REMOTE_ADDR']
#prescription.employee = #prescription.employee.upcase
respond_to do |format|
if #prescription.save
format.html { redirect_to #prescription, notice: 'Prescription was successfully created.' }
format.json { render json: #prescription, status: :created, location: #prescription }
else
format.html { render action: "new" }
format.json { render json: #prescription.errors, status: :unprocessable_entity }
end
end
end
I am going to be linking to this clone action from the view with:
<%= link_to "Create another like this?", clone_prescription_url(#prescription), :method => :put %>
Is it as simple as adding an action to my controller like this?
def clone
#prescription = Prescription.find(params[:id])
#prescription.dup
#prescription.save
end
Apologies if the above code is completely wrong, I'm trying to get my head around it! I've seen someone do exactly what I'm trying to achieve with the cloning - but not with the editing before save.
The user that's duplicating won't have permission to edit a record once saved. It's purely for the intial data entry.
To do this, you're going to have to create a new instance of your Prescription class. "dup" works, but you're assuming it overwrites the existing record. Only methods that end with a bang(!) tend to do that.
Your code should be:
def clone
#prescription = Prescription.find(params[:id])
#new_prescription = #prescription.dup
#new_prescription.save
end
or
def clone
#prescription = Prescription.find(params[:id]).dup
#prescription.save
end
This isn't testing for times when the :id isn't found.
If you want the clone action to allow the user to review the duplicate before it is saved (AKA created), then it is almost like the "new" action, except with filled in fields already.
So your clone method could be a modification of your new method:
def new
#prescription = Prescription.new()
end
def clone
#prescription = Prescription.find(params[:id]) # find original object
#prescription = Prescription.new(#prescription.attributes) # initialize duplicate (not saved)
render :new # render same view as "new", but with #prescription attributes already filled in
end
In the view, they can then create the object.
I was looking for logic to clone an existing record. I had to modify the logic posted by ronalchn slightly because when it tried to execute the second statement of clone I got an mass assignment error because it tried to copy id, created_at, updated_at which are not included in my attr_accessible list. This is how I modified the logic to get it to work in my application using my model:
#old_event = Event.find(params[:id]) # find original object
#event = Event.new
#event.field_1 = #old_event.field_1 (statement for each field in attar_accessible)
render :new # render same view as "new", but with #prescription attributes already filled in
I am trying to render a view + layout based on a condition. The following code seems to work but loses access to #objects set upfront. I call this code in a ProfilesController method Show.
#profiles = Profile.where(:user_id => current_user.id).first
if #profile.nil? == true
render :view => "show",
:layout => "application"
else
render :template => "profiles/my_profile",
:layout => "profiles"
end
Gives output:
undefined method `profiles' for nil:NilClass
How could one render based on a condition and still preserve the previous set #objects (in this case the access to #profiles)
Setting layout in view is not the way to go, its not the rails way and was not advised so even if possible. I changed setting layout in controller action and render the right code in my show partial wich complete solved the issue.
render :json => #bs.to_a.to_json, :except => ["completo"]
I want to render everything to json except the field "completo". That should be working but given that I need to do ".to_a" and ".to_json", that stopped working. Is there a way to revert that?
Thanks
Assuming that #bs is a MongoDB Cursor, do the following:
#bs = #bs.to_a.map { |obj| obj.delete("completo"); obj }
render :json => #bs.to_json
In summary:
Make it an array.
Remove the completo key from every item in the array, making sure we return the item itself at the end of the map
Render as before.