Essentially I'm dealing with 3 things: (*the first 2 work)
An Action called Index(), which accepts a user_id parameter.
A CSV export feature
A needed link to the CSV export file, from the Index view; which ideally utilizes the link_to method.
The first two work perfectly and I'm stumbling on generating a link using the link_to method because the CSV file needs to be scoped to the proper user using a user_id parameter.
Here is my Controller Code:
def index
#pro = Pro.find_by_user_id(params[:user_id])
csv_code = CSV.generate do |csv|
#pro.accounts.each do |account|
csv << [account['name'],account['number']]
end
end
respond_to do |format|
format.html
format.csv { render :csv => csv_code}
end
end
index.html.haml:
* notice I'm missing a parameter 1234, that this needs to generate a csv
= link_to image_tag "export.png", {:action => :index.csv}
The Url that works to generate a CSV:
http://localhost:3000/.csv?user_id=1234
I'm sure this is a super easy problem to solve, and I've spent an hour overlooking the obvious solution. Thank you in advance!
Just add the "(" to the image tag.
= link_to image_tag ("export.png"), {:action => :index.csv}
Related
On my index page for one of my classes item, I have a form that filters entries in the database. You can filter by all fields that item has and all elements are represented so the code is as follows:
<%= form_tag(url_for({ :controller => :item, :action => :filter }), :id => 'filter_form') do %>
<li>
<% Category.active.each do |category| %>
<% check_box_tag "categories[]", category.id, checked?(:categories, category.id) %>
<%= category.name %>
</li>
...
<% end %>
And the controller in the filter method filters through all entries in Item using the query generated here:
#items = Item.includes(:category, ...)
.joins(:item)
.where(:category => params[:categories]
...
And so on for all fields in the filter.
I also have an export method in the same controller which exports all entries as a CSV file. The view renders what's in a helper method and passes the variable #items which is passed from the export method here as Item.active.
What I'd like is to have another method export_filter that, instead of exporting all entries in the Item table, exports just the filtered entries. In other words I'd like to get these form parameters for my filter form available in my export_filter method and use them to pass to the view and helper. How do I do this?
This is often handled by a single index method using formats (html or csv in your case). The filtering is simply another scope on the list - the rendering is handled by a block within the method, something like:
respond_to do |format|
format.html
format.csv { render text: #products.to_csv }
end
As usual - Railscasts has covered this and is a great resource:
http://railscasts.com/episodes/362-exporting-csv-and-excel?view=asciicast
To set the format - you could either do another button (and look at the value of the button) or another input that helps to select the format.
I'm sure this has been answered before but I've looked everywhere, so I apologize in advance. When I run:
<%= params %>
I get back: {"controller"=>"spree/taxons", "action"=>"show", "id"=>"women/long-sleeve"}
I'm trying to access the :id in the show action of the taxons controller. I have:
def show
#taxon_id = params[:id]
end
This assigns 'women/long-sleeve' to #taxon_id.
Is there a way to retrieve only 'women' from 'women/long-sleeve'.
I would like to render a partial based on this, something like:
<% if #taxon_id == params[:id] %>
<%= render 'shared/#{#taxon_id}' %>
<% end %>
But instead of rendering 'shared/women' it's trying to render 'shared/women/long-sleeve', which isn't a partial.
Thank you.
If you just want "women" from your params hash then you can do
#taxon_id = params[:id].split("/").first
I would just split on / and grab the first element in the resulting array:
params[:id].split('/').first
If it's always going to be in that format you can change your route:
match '/spree/taxons/:id/:slug' => 'taxons#show'
And your :id field will match correctly.
Using rails 2. I want a link to the current page (whatever it is) that keeps all of the params the same but changes the format to 'csv'. (setting the format can be done by having format=csv in the params or by putting .csv at the end of the path). Eg
posts/1
=> posts/1.csv OR posts/1?format=csv
posts?name=jim
=> posts.csv?name=jim OR posts?name=jim&format=csv
I tried this as a hacky attempt
request.url+"&format=csv"
and that works fine if there are params in the current url (case 2 above) but breaks if there aren't (case 1). I could come up with more hacky stuff along these lines, eg testing if the request has params, but i'm thinking there must be a nicer way.
cheers, max
EDIT - btw, it's not guaranteed that the current page could have a named route associated with it, in case that's relevant: we could have got there via the generic "/:controller/:action/:id" route.
<%= link_to "This page in CSV", {:format => :csv } %>
<%= link_to "This page in PDF", {:format => :pdf } %>
<%= link_to "This page in JPEG", {:format => :jpeg } %>
EDIT
Add helper
def current_url(new_params)
url_for :params => params.merge(new_params)
end
then use this
<%= link_to "This page in CSV", current_url(:format => :csv ) %>
EDIT 2
Or improve your hack:
def current_url(new_params)
params.merge!(new_params)
string = params.map{ |k,v| "#{k}=#{v}" }.join("&")
request.uri.split("?")[0] + "?" + string
end
EDIT
IMPORTANT! #floor - your approach above has a serious problem - it directly modifies params, so if you've got anything after a call to this method which uses params (such as will_paginate links for example) then that will get the modified version which you used to build your link. I changed it to call .dup on params and then modify the duplicated object rather than modifying params directly. – #Max Williams
You can use:
link_to "text of link", your_controller_path(format:'csv',params: request.query_parameters)
#floor's answer was great, I found it very useful.
Although the method can be improved by using the to_params method rather than contructing your own, like so:
def current_url(new_params)
params.merge!(new_params)
"#{request.uri}#{params.to_params}"
end
I have a shop application and another site thats for a special promotion. I've used Active Resource to import products from the shop in to the promo site and added a shopping cart to add the products. However, to actually order the products I need to send the items to the shop application, creating a new cart there to finish the order.
I've made a demo 'RESTful' application to practice using xml to send data back and forth, so I'm trying to use the principles of REST for the real app. However, I need to send the products to a non-RESTful controller. Just to give you an idea of the Cart controller in the shop, here are its actions:
def index…
def add…
def checkout…
def update…
def remove…
def empty…
def apply_discount…
def remove_discount…
def apply_credits…
def remove_credits…
def stock_check…
# My action to accept items from carts in other apps
def cart_import…
And in routes.rb, the only route relating to the cart is currently
map.cart 'cart/:action/:id', :controller => 'shop/cart'
I've inherited the shop application from a previous developer, so I'd probably try to make it more RESTful if I was to make it from scratch.
Anyway, I'm pretty confident that I can get the cart to respond to XML, even without being defined with map.resources. My problem is how to send a hash of the cart items and quantities from the promo app.
To group the cart items and quantities I've collected the item's product id and quantity in to a hash:
<% #items = Hash.new %>
<% #cart.items.collect {|i| #items[i.product_id] = i.quantity} %>
Which when inspected gives the following output:
<%= Rails.logger.info #items.inspect %>
{1144=>2, 1143=>1}
So I figured to send them to the shop I could pass them in a posted link_to:
<%= link_to 'Export Cart', "http://shop.example.com/cart/cart_import", :items => #items, :method => :post %>
That doesn't seem to do anything, whereas omitting the first field appends the items to the URL in a format that looks sensible, but appears as a relative link on the promo application:
<%= link_to "http://shop.example.com/cart/cart_import", :items => #items, :method => :post %>
http://promo.example.com/cart?items[1143]=1&items[1144]=2&method=post
I'm sure the clue is in that the #items object needs to be passed in with the url, but since I can't use a named route I don't really know how to get it in there so that it is posted in the correct format.
Thanks for the help,
Gareth
the way you are passing in the parameters for the link_to method is assuming that :items is one of the link_to options, not one of the url options. this is an order of precedence issue and if you wrap your url inside parens then you can use the options available for the url_for method on your url, to build the path: http://apidock.com/rails/ActionView/Helpers/UrlHelper/url_for
You can't use a link to generate a POST request. It is turning to a GET request. Better use javascript to generate a post request on click of a button or some other event.
In the end I made a helper:
def hash_to_params(items)
result = ""
i = 0
items.each do |item|
i > 0 ? result += "&" : result += ""
result += "items[#{item[0]}]=#{item[1]}"
i += 1
end
return result
end
Then for the link I called the helper:
<%= link_to "export", "http://shop.example.com/cart/cart_import?#{hash_to_params(#items)}", :method => :post %>
Pretty ugly way of doing it, but I really can't think of anything better?
Cheers,
Gareth
I'm trying to generate a JSON response that includes some HTML. Thus, I have /app/views/foo/bar.json.erb:
{
someKey: 'some value',
someHTML: "<%= h render(:partial => '/foo/baz') -%>"
}
I want it to render /app/views/foo/_baz.html.erb, but it will only render /app/views/foo/_baz.json.erb. Passing :format => 'html' doesn't help.
Beginning with Rails 3.2.3, when calling render :partial (only works outside of the respond_to block).
render formats: [ :html ]
instead of
render format: 'html'
What's wrong with
render :partial => '/foo/baz.html.erb'
? I just tried this to render an HTML ERB partial from inside an Atom builder template and it worked fine. No messing around with global variables required (yeah, I know they have "#" in front of them, but that's what they are).
Your with_format &block approach is cool though, and has the advantage that you only specify the format, whereas the simple approach specifies the template engine (ERB/builder/etc) as well.
Rails 4 will allow you to pass a formats parameter. So you can do
render(:partial => 'form', :formats => [:html])}
Note you can do something similar in Rails 3 but it wouldn't pass that format to any sub partials (if form calls other partials).
You can have the Rails 4 ability in Rails 3 by creating config/initializers/renderer.rb:
class ActionView::PartialRenderer
private
def setup_with_formats(context, options, block)
formats = Array(options[:formats])
#lookup_context.formats = formats | #lookup_context.formats
setup_without_formats(context, options, block)
end
alias_method_chain :setup, :formats
end
See http://railsguides.net/2012/08/29/rails3-does-not-render-partial-for-specific-format/
For Rails 3, the with_format block works, but it's a little different:
def with_format(format, &block)
old_formats = formats
self.formats = [format]
block.call
self.formats = old_formats
nil
end
Building on roninek's response, I've found the best solution to be the following:
in /app/helpers/application.rb:
def with_format(format, &block)
old_format = #template_format
#template_format = format
result = block.call
#template_format = old_format
return result
end
In /app/views/foo/bar.json:
<% with_format('html') do %>
<%= h render(:partial => '/foo/baz') %>
<% end %>
An alternate solution would be to redefine render to accept a :format parameter.
I couldn't get render :file to work with locals and without some path wonkiness.
In Rails 3, the View has a formats array, which means you can set it to look for [:mobile, :html]. Setting that will default to looking for :mobile templates, but fall back to :html templates. The effects of setting this will cascade down into inner partials.
The best, but still flawed way, that I could find to set this was to put this line at the top of each full mobile template (but not partials).
<% self.formats = [:mobile, :html] %>
The flaw is that you have to add that line to multiple templates. If anyone knows a way to set this once, from application_controller.rb, I'd love to know it. Unfortunately, it doesn't work to add that line to your mobile layout, because the templates are rendered before the layout.
Just elaborating on what zgchurch wrote:
taking exceptions into account
returning the result of the called block
Thought it might be useful.
def with_format(format, &block)
old_formats = formats
begin
self.formats = [format]
return block.call
ensure
self.formats = old_formats
end
end
You have two options:
1) use render :file
render :file => "foo/_baz.json.erb"
2) change template format to html by setting #template_format variable
<% #template_format = "html" %>
<%= h render(:partial => '/foo/baz') %>
I had a file named 'api/item.rabl' and I wanted to render it from an HTML view so I had to use:
render file: 'api/item', formats: [:json]
(file because the file have no underscore in the name, formats and not format (and passes and array))
It seems that passing a formats option will render it properly in newer Rails version, at least 3.2:
{
someKey: 'some value',
someHTML: "<%= h render('baz', formats: :html) -%>"
}
I came across this thread when I was trying to render an XML partial in another xml.builder view file.
Following is a nice way to do it
xml.items :type => "array" do
#items.each do |item|
xml << render(:partial => 'shared/partial.xml.builder', :locals => { :item => item })
end
end
And yeah... Full file name works here as well...