Generate PDF file using AJAX call - ruby-on-rails

I'm trying to generate a PDF file using AJAX call in Rails3. The following code generates a PDF file which I have created using PRAWN gem.
<%= link_to "Generate pdf", books_path(#book, :format => 'pdf') %>
I do not want user to view the PDF until they order it. So, the goal is to create a PDF file in the server.
Any ideas or thoughts much appreciated.

Use this, make sure your remote action does not return the PDF, but simple generates and stores it on the server.
link_to "Generate PDF", prepare_books_path(#book), :remote => true, :method => :put
This will work in Rails 3. If you're using jQuery, make sure to read this article on how to set things up correctly.
Your controller action may look like this:
def prepare
# Do your thing to generate the PDF
render :text => "PDF Generated", :status => 200
end
I used the PUT-method because you are altering the state of your data (e.g. you are generating something new, you don't want a bot or crawler to automatically call that).

Firstly, it beats me why you would do something on a request like generating a PDF, when the user is not expecting that action. Isn't better to only generate the pdf when the user requests for it?

Thanks Ariejan.
I modified your code as following and it did just what I wanted.
<%= link_to "Generate Story Book", pdfbook_stories_path(:format => 'pdf'), :remote => true %>
And for the controller,
def pdfbook
#stories = current_account.stories
respond_to do |format|
format.pdf {}
end
end

Related

Use Rails logic with JSON data

This may seem a pretty stupid question but I'm out of ideas at the moment and appreciate suggestions.
I have created a quiz interface that should change the question when NEXT button is clicked. The next question is loaded as JSON via AJAX call. Now the question contains HTML and I need to use Rails sanitize helper to filter tags.
I am not sure how I would use sanitize method on JSON data data acquired via AJAX call.
Please guide me.
EDIT:
The stress was not on JSON data. It was an honest mistake. The issue was the inability to use Rails logic on the data returned by AJAX call.
After some research and with the help of this answer, I was able to achieve what I wanted to.
All I had to do was to create a partial template (say, _question.html.erb) for the quiz question container with a form like this:
_question.html.erb
<%= form_tag('/question', :remote => true, :id => 'quiz') do -%>
... some code here ...
<%= submit_tag 'Submit' %>
<% end -%>
Create a route /question in routes.rb:
match 'question', :to => 'quiz#question', via: [:post]
and in QuizController.rb handle the response:
class QuizController < ApplicationController
def question
... some logic here ...
#ques = ...
#options = ...
respond_to do |format|
format.html
format.js
end
end
end
Now, create a question.js.erb to render the template with data:
$('#quiz-container').html("<%= escape_javascript(render partial: 'question',
:locals => { ques: #ques, options: #options }) %>");
The :remote => true option in the form does the trick here. It submits the form via ajax. Now, I can use Rails logic inside the partial template that gets the data via AJAX call.

rails 3 - link_to to destroy not working

I am trying to create a destroy link to my users controller, I am also using devise.
Here is my code -
View
<%= link_to 'Delete User?', child, :confirm => "Are you sure you want to delete #{child.full_name}?", :method => :delete, :class => "user-additional", :style => "font-size:12px;font-weight:normal;" %>
Controller
def destroy
if #user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to account_index_path }
format.xml { head :ok }
end
end
end
Routes
devise_for :users
resources :users, :except => [:new]
The link translates to localhost:3000/users/10
When clicked this opens the users show instead of deleting them
Any ideas ?
Destructive actions should be performed as a form submission - http://www.w3.org/2001/tag/doc/whenToUseGet.html#checklist
use button_to (passing a :method => :delete) instead and style the button appropriately.
Actually I just had the exactly same problem yesterday
Try this:
<%= button_to "delete", your_object, :method=>:delete, :class=>:destroy %>
It works (for me at least)
In case that you are using jQuery instead of Prototype, you are probably missing a javascript file.
You can find details on how to add it to your project from the jquery-ujs GitHub page or from episode 205 of the Railscasts.
At a guess I think it is because in Rails 3, unobtrusive javascript is now used for functionality such as this (Rails 2 would output a bunch of nasty inline javascript for your code, Rails 3 puts the javascript in an external file, and uses HTML5 data- attributes to interact with that.)
To solve this you need to include <%= csrf_meta_tags %> in your page header to reference the external javascript. It also deals with XSS issues.
Some details here: Delete link sends "Get" instead of "Delete" in Rails 3 view
If you are using jQuery, make sure you have something like this:
<script type="text/javascript">
// this allows jquery to be called along with scriptaculous and YUI without any conflicts
// the only difference is all jquery functions should be called with $j instead of $
// e.g. $jQ('#div_id').stuff instead of $('#div_id').stuff
var $jQ = jQuery.noConflict();
</script>
follow the steps in the installation part rails/jquery-ujs
add <%= javascript_include_tag "application" %> in your layout file.
If you haven't included jquery and jquery-ujs in your app , the default link_to default coming with scaffold wont work!
I had the same issue.It got solved after including both these js!
Also if you get this problem in production mode, it may be because you have not compiled the assets. See http://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
Worked for me with confirmation message.
<%= button_to 'Destroy', {action: :destroy, id: version.id}, onclick: 'return confirm("Are you sure?")', method: :delete %>

Rails: send_file not causing download, links to 'show' controller method instead

First time using rails, I'm trying to create a download link to get something off the file system.
In my view, I've got
<%= link_to 'Show', upload, :method => :download %>
and my 'download' method in my controller looks like:
def download
#upload = Upload.find(params[:id])
send_file '/data_store/50692.pdf'
This is needed because I have restrictions on who can view this pdf, but on this page, I get linked to
http://localhost:3000/uploads/10
(10 is the id of this 'upload')
and on that page, I get the error
Unknown action
No action responded to 10. Actions: clearance, create, destroy, download, edit, index, is_admin, new, show, su_required, and update
I want it instead to stay on the same page, and offer the user a download prompt, but I'm not sure what I'm doing wrong.
Ideally you should be looking into a Ajax call if you'd want to stay on the same page and offer the user a download prompt, but the code given below would also work. Upon clicking the "download" link it'll open a new tab/window,offer the user a download prompt and close the tab/window.
Also the :method parameter to link_to tells it which HTTP method (POST,GET,DELETE,PUT) to use. To reference the "download" method in your controller use the :action parameter.
view
----
<%= link_to 'Show', upload, :action => :download, {:target => "_blank"} %>
controller
----------
return send_file '/data_store/50692.pdf', :type => "application/pdf", :filename => "50692.pdf"
I think it is a problem in the calling download method.
Try this one
<%= link_to "Show",:controller=>'controllername',:action=>'download',:id=>upload.id %>
You must pass your controller name in the place "controllername".

Rails controller not rendering correct view when form is force-submitted by Javascript

I'm using Rails with jQuery, and I'm working on a page for a simple site that prints each record to a table. The only editable field for each record is a checkbox. My goal is that every time a checkbox is changed, an ajax request updates that boolean attribute for the record (i.e., no submit button).
My view code:
<td>
<% form_remote_tag :url => admin_update_path, :html => { :id => "form#{lead.id}" } do %>
<%= hidden_field :lead, :id, :value => lead.id %>
<%= check_box :lead, :contacted, :id => "checkbox"+lead.id.to_s, :checked => lead.contacted, :onchange => "$('#form#{lead.id}').submit();" %>
<% end %>
</td>
In my routes.rb, admin_update_path is defined by
map.admin_update 'update', :controller => "admin", :action => "update", :method => :post
I also have an RJS template to render back an update. The contents of this file is currently just for testing (I just wanted to see if it worked, this will not be the ultimate functionality on a successful save)...
page << "$('#checkbox#{#lead.id}').hide();"
When clicked, the ajax request is successfully sent, with the correct params, and the action on the controller can retrieve the record and update it just fine. The problem is that it doesn't send back the JS; it changes the page in the browser and renders the generated Javascript as plain text rather than executing it in-place.
Rails does some behind-the-scenes stuff to figure out if the incoming request is an ajax call, and I can't figure out why it's interpreting the incoming request as a regular web request as opposed to an ajax request.
I may be missing something extremely simple here, but I've kind-of burned myself out looking so I thought I'd ask for another pair of eyes. Thanks in advance for any info!
In your controller you need to specify the proper response. Since you didn't post the controller I'll just try to fill in the blanks.
def update
# Update something
respond_to do |format|
format.js # this renders your rjs file
end
end
Specifying the format tells the rails app to interpret the javascript instead of just sending it back as text.
The other option instead of using rjs is to do an inline rjs block like this:
render :update do |page|
page.replace_html 'user_list', :partial => 'user', :collection => #users
page.visual_effect :highlight, 'user_list'
end
Only use the inline rjs if you will be doing minimal changes to the interface that can be put into one or two lines. Anything more should be in it's own rjs file.
This question is related to this one, but the answer varies slightly. I had to create a new way to submit the form, since the default jQuery submit() method does not submit as a 'script' and certainly does not fire the code that Rails generates in the onsubmit="..." handler via the form_remote_tag helper.
The solution was to create a new function as the linked answer suggests, but the contents are slightly different:
jQuery.fn.submitWithAjax = function() {
jQuery.ajax({data:jQuery.param(jQuery(this).serializeArray()) + '&authenticity_token=' + encodeURIComponent('<%= form_authenticity_token %>'), dataType:'script', type:'post', url:'/update'});
return false;
};
This is brittle right now-- notice that I insert rails' form_authenticity_token into the Javascript, but really the method (post) and the url (/update) should also be generated rather than hardcoded.
Things are working A-OK now.

Rails ignoring render and redirect_to

I've got a really simple rails question here but I can't seem to find the answer anywhere. I guess some of the problems stem from me following a tutorial for Rails 1.2 with Rails 2.1. Anyway..
I'm writing a blog system and I'm implementing the comments bit. I have comments displaying fine once I've created them using script/console, but getting the comment form itself working is the hard bit.
In posts_controller.rb I have
def comment
Post.find(params[:id]).comments.create(params[:comment])
flash[:notice] = "Added comment"
#render :action => show
redirect_to :action => show
end
and in show.html.erb (the view) I have
<%= form_tag :action => "comment", :id => #post %>
<%= text_area "comment", "body" %><br>
<%= submit_tag "Post Comment" %>
When I submit the form it tries to go to the urb /posts/comment/1 which is obviously incorrect, and it complains that it can't find a template. Obviously I don't want a template there because I've told it to redirect to the show action because I want it to just re-display the post's show page, with the new comment there.
I've tried both the commented out line (render :action => show) and the redirect_to line, and neither seem to do anything at all.
I'm sure I'm missing something simple, but what is it?
Does redirect_to :action => 'show', :id => params[:id] with quotes around show work?
Rails 2.1 embraces "RESTful resources". show just happens to be the name of one of the predefined REST actions that all rails controllers use.
Rails does some magic behind the scenes, and :show is equivalent to "display this one specific element with a specific given ID". Sounds like it's getting mixed up with that. The ID is probably defaulting to "1". Hence the generated URL you're seeing from the render call
The Rails 2.1 way of doing it would use the following actions and templates:
index - displays the full list of comments
create - add a new comment
show - display a specific comment only (not the full list). Doesn't sound like this is what you want, but the "magic" inside rails will default to this.
There are also actions for new (show view to enter a new comment) edit (show view to do an edit of an existing comment) update (handle update submission) and destroy (duh), but it doesn't look like you'd use them in this example.
Do you have a link to the tutorial? Wouldn't be too hard to port it to Rails 2.1 style.
yes, you use old rails style.
Something new:
form_for :comment, :url => { :post_id => #post } do |f|
f.text_area :body
submit_tag "Post"
end
you can use resources for posts and comments, search google for better tutorial or install rails 1.2.6:
gem install -v 1.2.6 rails

Resources