I have an obj #files which sometimes contain one set of data(one file) and sometimes it contains many files.
I have a create.js.erb and this is what I do at the moment.
<% if #files.new_record? %>
alert("Failed to upload: <%= j #files.errors.full_messages.join(', ').html_safe %>");
<% else %>
$(".container").append('<div id="cfile"><%= j render(#files) %></div>');
<% end %>
Then I have a partial called _file.html.erb
<%= file.name %>
<%= file_.id %>
This all works fine but I'm running into problems trying to create a partial for different types of files.
I want to be able to do something like
if file.first.type == image # (Even If the come in groups they would be the same type so i just need one .type field from one of them.)
$(".different_container").append(render different partial here);
else if file.first.type == doc
$(".another_container").append(render another partial here);
How would I go about this?
Please ask if i haven't explained something clearly.
So the problem is that #files can be multiple items or a single item. Before you access it, you can wrap it in Array(), like this:
file_type = Array(#files).first.type
if file_type == :something
elsif file_type == :something_else
end
What Array() does is it tries to convert the argument passed to it in to an array. If you pass it a single object, it will return an array with that one object in it. If you pass it an array, it does nothing.
>> Array(1) # => [1]
>> Array(Object.new) # => [Object]
>> Array([1,2,3]) # => [1,2,3]
Related
In Rails 3.1 it is not possible to access controller instance variables in an asset js.erb or coffee.erb file using syntax such as <%= #foo %>, where #foo is set in the controller. So then the question is what are the best ways for passing controller variables to CoffeeScript or JavaScript assets.
This question has kind of been asked in multiple convoluted forms on the forum, but my point in asking it again is to have a place where all recommendations are gathered together, and the code supplied is simple and readable. Also note that I'm specifically referring to assets and not view response files.
a couple of ways I have done this in the past
put the data in hidden fields, access the data in js/coffee
# single value
<%= hidden_field_tag "foo_name", #foo.name, { :id => "foo-name" } %>
$('#foo-name').val();
# when the 'value' has multiple attributes
<%= hidden_field_tag "foo", #foo.id, { :id => "foo", "data-first-name" => #foo.first_name, "data-last-name" => #foo.last_name } %>
$foo = $('#foo')
console.log $foo.val()
console.log $foo.data("firstName")
console.log $foo.data("lastName")
another option: load data into js data structure in erb, access it from js/coffee
<% content_for(:head) do %>
<script>
window.App = window.App || {};
window.App.Data = window.App.Data || {};
window.App.Data.fooList = [
<% #list.each do |foo| %>
<%= foo.to_json %>,
<% end %>
];
</script>
<% end %>
# coffee
for foo in window.App.Data.fooList
console.log "#{foo.id}, #{foo.first_name} #{foo.last_name}"
I am not a big fan of constructing javascript data from ruby in erb like this, something about it just feels wrong - it can be effective though
and another option: make an ajax call and get the data on-demand from the server
I am also interested in other ideas and approaches
There is a really nice rail cast and quite recent (feb. 2012) about this specific topic:
#324 Passing Data to JavaScript
It shows 3 ways: a script tag, a data attribute, and the Gon gem.
I think house covered all the available techniques. I would only mention that using an AJAX call is best used when you have a large volume of data, dynamic data or combination of both.
Rather than use a hidden field I chose to add a data attribute to the container div which jquery can pick up.
<div class="searchResults" data-query="<%= #q %>"></div>
then the jquery to access it
url: "/search/get_results?search[q]=" + $(".searchResults").data("query") + "&page=" + p
I feel this is the cleanest way to pass data to javascript. After having found no way to pass a variable to a coffee script file with the rails asset pipeline from a controller. This is the method I now use. Can't wait till someone does set up the controller way with rails that will be the best.
In the controller:
#foo_attr = { "data-foo-1" => 1, "data-foo-2" => 2 }
In the view (HAML):
#foo{#foo_attr}
In the CoffeeScript asset:
$("#foo").data("foo-1")
$("#foo").data("foo-2")
In situations where your javascript data gets out of hand, using the gon gem is still the preferred way to go in rails, even in 2015. After setting up gon, you are able to pass data to your javascript files by simply assigning the data to the gon object in rails.
(Gemfile)
gem 'gon'
(controller)
def index
gon.products = Product.all
(layouts)
<%= include_gon %>
(public/javascripts/your_js_can_be_here.js)
alert(gon.products[0]['id');
(html source automatically produced)
<script>
window.gon = {};
gon.products = [{"created_at":"2015", "updated_at":"2015, "id":1, "etc":"etc"}];
You can read more verbose implementation details on Gon or the two other rails-javascript channels from Ryan Bate's screencast.
http://railscasts.com/episodes/324-passing-data-to-javascript
You can edit and add variables to the params array in the controller then access them in the response.js.erb. Here's an example with params[:value]:
def vote
value = params[:type] == "up" ? 1 : -1
params[:value] = value
#public_comment = PublicComment.find(params[:id])
have_voted = #public_comment.evaluators_for(:pub_votes_up) << #public_comment.evaluators_for(:pub_votes_down)
unless have_voted.include?(#current_user) # vote
#public_comment.add_or_update_evaluation(:"pub_votes_#{params[:type]}", value, #current_user)
else # unvote
#public_comment.delete_evaluation(:"pub_votes_#{params[:type]}", #current_user)
params[:value] = 0
end
respond_to do |format|
format.js # vote.js.erb
end
end
And here's an example accompanying response.js.erb
button = $('<%= ".pub#{params[:type]}_#{params[:id]}" %>')
label = button.find('strong')
<% comment = PublicComment.find(params[:id]) %>
label.html('<%= comment.reputation_for(:"pub_votes_#{params[:type]}").to_i %>')
<% if params[:value] == 1 %>
button.addClass('btn-success')
<% elsif params[:value] == -1 %>
button.addClass('btn-danger')
<% else %>
if button.hasClass('btn-success') { button.removeClass('btn-success') }
if button.hasClass('btn-danger') { button.removeClass('btn-danger') }
<% end %>
I am trying to compare the names of the element at the current index and the previous index for each element to determine if they are the same name so I don't print out the name twice.
Unfortunately, trying to access the array element using array[i] doesn't work, but if I hard-code an index or just print out the index, it works fine so I'm not sure where it's messing up.
I need to be able to access the previous element though so I can't use other loops so only suggest something where I can access the previous element in the array.
<% for i in 1..count %>
<% if array[i].count > 1 %>
<% if array[i-1].name == array[i].name %>
<%= array[i].name %>
<%= array[7].name %>
<%= i %>
<% end %>
<% end %>
Does anyone know the correct way to access an element in an array?
This should do it if your list is sorted and all you care about is printing names:
<% array.map(&:name).uniq.each do |name| %>
<%= name %>
<% end %>
More generally, you can do it like this:
array.each_with_index do |el, i|
prev_el = array[i-1] #will be nil for the first element
next_el = array[i+1] #will be nil for the last element
if prev_el && el.name == prev_el.name
#name same as previous
end
if next_el && el.name == next_el.name
#name same as next
end
end
You should avoid index-based array access for loops, not because they don't work but because there are much nicer and more readable ways of looping through arrays in Ruby.
You can keep in mind that you are using Ruby, and the motto of Ruby is "Do More in Less Work".
You can use uniq to filter out all similar elements, then iterate through them to do whatever you want to do. uniq works like this:
a = [ "a", "a", "b", "b", "c" ]
a.uniq # => ["a", "b", "c"]
This should do it all what you tried to do:
<% array.uniq.each do |obj| %>
<%= obj.name %>
<% end %>
Just thought of another way of doing this:
grouped = array.group_by(&:name)
Now you have a hash where each key is a unique name and the corresponding value is all the array elements with that name. So next you can do stuff like
#list of names in alphabetical order
grouped.keys.sort
#get one element for each unique name
grouped.map{|name,els| els.first}
#print out how many you have for each name
grouped.each{|name, els| puts "#{name.inspect} => #{els.size} elements"};false
I have this code in controller:
array = ["asd", "asd", "asd"]
#print = array.each do |i|
puts "Random text #{i}"
end
And now I want to print it in some pages view like show.html.erb:
<%= #print >
And I get this: ["asd", "asd", "asd"] But In controller I sayd to puts each object in array, but it is not doing it?
The puts method is for printing a string to the console. If you wanted to set each of the values of the array to a certain value in order to print it out later, you should use #map.
array = ['asd', 'asd', 'asd']
#print = array.map { |i| "Random text #{i}" }
Now, in your corresponding view, you should add:
<% #print.each do |val| %>
<%= val %>
<% end %>
puts prints to the stdout (standard output) that, in the majority of cases, corresponds to the console where you started the Rails server.
Check the console and you will find, in the middle of the request logs, also the result of the puts statement.
A better way to print out something from the console is to use the Rails logger, especially if you want such output to be logged in the logs in production.
Rails.logger.info "message"
Assuming it's just for debugging purpose, then it's fine to use puts (or p).
You should be doing the looping in your view. This helps maintain the separation between your application logic and your view code.
Controller
#array = ["asd", "asd", "asd"]
View
<% #array.each do |i|
<%= i %> # No need to use the puts method here
<% end %>
it seems that the variable #print is the array. The controller is run once per load of the page and then will output its contents at the end to the view. Plus, "puts" is for printing a string to the console. You should put the loop in question in the view like this:
<% #array.each do |i| %>
<%= i #>
<% end %>
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.
We need to verify that download links work. And because the files are quite large, we need to verify that we don't have outdated, useless files sitting in our download directory. So we have a download management page that validates that every database file_name has a file named the same in the download directory. The page then verifies that every file in our directory has a file_name in our database.
in the controller:
#documents = Document.find(:all, :order => "section asc, sub_section asc, position asc, name asc")
#files = Dir.glob("public/downloads/*").sort
FIRST VALIDATION: in my view to validate that there is a file for every document record in the database:
<% #documents.each do |d| -%>
<% if #files.include?("public/downloads/" + d.file_name)
clr = "Green"
else
clr = "Red"
end %>
... color coded print routine ...
<% end %>
SECOND VALIDATION: in my view to validate that every file has a document record in the datebase:
<% #files.each do |f| -%>
<% str = f.gsub(/^.*\//, '')
if #documents.include?(str)
clr = "Green"
else
clr = "Red"
end %>
... color coded print routine ...
<% end %>
with my small test document list, the printed database file_names match exactly with the file names printed from our download directory. however, the include test for the second validation is not working. i haven't been able to figure out why.
thanks!
This code:
if #documents.include?(str)
should look like this:
if #documents.detect{|doc| doc.file_name == str}
Instead of #documents.include?(str) use #documents.any? { |d| d.file_name == str}
The problem is that #documents is not a collection of file_names, it is a collection of Document instances. You should try something like this instead:
#documents.map(&:file_name).include?(str)
This will collect all file_names and then check if str matches any of them.