I've been trying to figure out how to post a string into an array from a user input text field that is part of a form. The push and << methods work fine over the console, but I couldn't manage to save any string data into arrays from my forms. It always reports a type mismatch like this:
Attribute was supposed to be a Array, but was a String.
What is the appropriate way to deal with this ?
EDIT code example:
the array:
serialize(:name, Array)
Let's say that the form contains just a simple text field, nothing else. How would I store the input data into the array above ? Basically what's the correct format of this piece of code:
<%= form_tag(grids_path , method: "post") do %>
<table id="grid">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= text_field(:grid, :name) %></td>
</tr>
</tbody>
</table>
<%= submit_tag("Save") %>
<% end %>
If you simply want to store a param into and array, you should do something like this:
<%= text_field_tag 'grid[name][]' %>
then the params['grid']['name'] will contain an array of strings
In your view:
<%= text_field_tag :my_string_a, 'String A' %>
<%= text_field_tag :my_string_b, 'String B' %>
In your controller:
my_array_of_strings = []
my_array_of_strings << params[:my_string_a]
my_array_of_strings << params[:my_string_b]
puts my_array_of_strings
Hope this works.
Related
For a current project, I have duplicate code between views, and I'm not sure of the best route to refactor it.
I appear to be in a position where I can have duplicate code across various .html.erb files, or I could put identical code into a partial and use conditionals. I've always heard logic should stay out of views. Neither option seems ideal, and I don't currently know of alternatives.
To illustrate my question, I created a simple rails app called animals. I scaffolded for two models: one for cat and one for dog. Images display their corresponding attributes:
Displaying #cats and #dogs is pretty much the same. Cats just have a column for meows while Dogs have a column for barks, and a dog has the additional attribute column of plays_catch.
Lets say we choose to reduce the duplicate code for displaying cats and dogs by making a shared view partial:
#views/shared/_animal.html.erb
<tr>
<td><%= animal.name %></td>
<td><%= animal.age %> </td>
<% if animal.class == Cat %>
<td><%= animal.meows %> </td>
<% end %>
<% if animal.class == Dog %>
<td><%= animal.barks %> </td>
<td><%= animal.plays_catch %> </td>
<% end %>
</tr>
Then to render #cats = Cat.all:
<%= render partial: "shared/animal", collection: #cats %>
Then to render #dogs = Dog.all:
<%= render partial: "shared/animal", collection: #dogs %>
Obviously it would be overkill to do something like this for this specific example, but the real world project I'm applying it to would not be overkill.
The overall question is: how do you remove nearly identical code that iterates over collections, where the only difference is adding/removing a column of information? It just doesn't feel right to put that logic in the view itself, and leaving the duplication feels wrong.
You could use decorators and add methods that return the extra column(s):
class DogDecorator < Draper::Decorator
def extra_columns
[:barks, plays_catch]
end
end
class CatDecorator < Draper::Decorator
def extra_columns
[:meows]
end
end
...
<% animal.extra_columns.each do |column| %>
<td><%= animal.attributes[column.to_s] %>
<% end %>
...
<% #cats = CatDecorator.decorate_collection(Cat.all)
<%= render partial: "shared/animal", collection: #cats %>
You can use respond_to? to solve the problem more generically. The view logic doesn't feel so wrong when it's more generic.
<% [:meows, :barks, :plays_catch].each do |method| %>
<% if animal.respond_to?(method) %>
<td><%= animal.send(method) %> </td>
<% end %>
<% end %>
You can add a method of the same name to both Cat and Dog classes which would return the specific instance attributes names and values. I'd recommend returning two arrays (one with the names of the fields, other with the fields' values, or vice-versa) since hashes are not exactly ordered. This way you can control the order in which they'll appear in the view.
For example:
#models/cat.rb
def fields_and_attributes
fields = ["Name","Age","Meows"]
attributes = [self.name, self.age]
if self.meows
attributes.push("Yes")
else
attributes.push("No")
end
[fields,attributes] # make sure each attribute is positioned in the same index of its corresponding field
end
#models/dog.rb
def fields_and_attributes
fields = ["Name","Age","Plays catch"]
attributes = [self.name, self.age]
if self.plays_catch
attributes.push("Yes")
else
attributes.push("No")
end
[fields,attributes] # make sure each attribute is positioned in the same index of its corresponding field
end
#controllers/animals_controller.rb
def display_animals
#animals = Cat.all + Dog.all # an array containing the different animals
end
#views/display_animals.html.erb
for i in (0...#animals.size)
fields_and_attributes = #animals[i].fields_and_attributes
for f in (0...fields_and_attributes[0].size)
<p><%= fields_and_attributes[0][f] %> : <%= fields_and_attributes[1][f] %></p>
end
end
Here, we first iterate over all of the animals and call the .fields_and_attributes method of that specific record; we then iterate over the results of calling that method, displaying fields and attributes in the same order as the one defined within the method and also guaranteeing that the code will display every field and every attribute regardless of the difference in the total number of fields for each different animal.
I don't know of any canonical way to accomplish this, but I would use one partial for this in the following way:
<tr>
<% animal.attributes.each do |_, value| %>
<td><%= value %></td>
<% end %>
</tr>
You can get rid of repeated attributes calls by providing in the partial a local variable with pre-obtained model attributes.
EDIT: if you only want to display some attributes.
# Declare whitelist of attributes
# (you can also declare a blacklist and just calculate the difference between two array: all_attributes - blacklist_attributes):
<% whitelist = [:name, :age, :barks] %>
<%= render partial: 'shared/animal',
collection: #dogs,
locals: {attrs: (#dogs.first.attributes.keys.map(&:to_sym) & whitelist)} %>
views/shared/_animal.html.erb:
<tr>
<% attrs.each do |attr| %>
<td><%= animal[attr] %></td>
<% end %>
</tr>
Below is my answer after reviewing posted answers. Basically:
I left the differences within each scaffold model's index page
I made shared partials for common table headers and table data
code below:
#app/views/cats/index.html.erb
<h1>Listing Cats</h1>
<table>
<thead>
<tr>
<%= render partial: "shared/cat_dog_table_headers" %>
<th>Meows</th>
</tr>
</thead>
<tbody>
<% #cats.each do |cat| %>
<tr>
<%= render partial: "shared/cat_dog_table_data", locals: {animal: cat} %>
<td><%= cat.meows %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Cat', new_cat_path %>
And for the dogs:
#app/views/dogs/index.html.erb
<h1>Listing Dogs</h1>
<table>
<thead>
<tr>
<%= render partial: "shared/cat_dog_table_headers" %>
<th>Barks</th>
<th>Plays catch</th>
</tr>
</thead>
<tbody>
<% #dogs.each do |dog| %>
<tr>
<%= render partial: "shared/cat_dog_table_data", locals: {animal: dog} %>
<td><%= dog.barks %></td>
<td><%= dog.plays_catch %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Dog', new_dog_path %>
The shared table headers for cats and dogs:
#app/views/shared/_cat_dog_table_headers
<td><%= Name %></td>
<td><%= Age %></td>
The shared table data for cats and dogs:
#app/views/shared/_cat_dog_table_data_headers
<td><%= animal.name %></td>
<td><%= animal.age %></td>
There are two sets of ids (id1 and id2) within one row on a form (form_tag), representing a pair of possible combination of id1 and id2 to choose. With check_box_tag, one id could be saved in an array like this:
<%= check_box_tag 'id_array[]', id1 %>
The id_array is returned as an array in params[:id_array]. Is there a way 2 ids can be saved with one check_box_tag (only need to check once)? We tried:
<%= check_box_tag 'id_array[][]', id1, id2 %>
and it did not work.
Update
Here is a single id form implemented:
<%= form_tag mass_onboard_user_accesses_path, :method => :put do %>
<tr>
<th></th>
<th><%= t('Engine Name') %></th>
<th><%= t('Engine Desp') %></th>
</tr>
<% #engines.each do |r| %>
<tr>
<% engine = Engine.find_by_id(r.resource_id)%>
<td><%= check_box_tag 'id_array[]', r.resource_id %></td>
<td><%= engine.name %></td>
<td><%= engine.module_desp %></td>
</tr>
<% end %>
<tr>
<th>
<%= submit_tag t('Save') ,:name => "save[#{#project_id}]" %>
</th>
</tr>
<% end %>
The only way I can think to do it is to cheat. I was trying to think how the collection of ids in a single checkbox would map to parameters in the URL and I failed to. So, the cheat is to assume that all params[:id_array] values are potentially an array of strings.
In your view:
<%= check_box_tag 'id_arrays[]', [id1, id2].join(",") %>
In your controller:
ids = []
params[:id_arrays].each do |sub_array|
ids += sub_array.split(",")
end
Or in a less verbose, and arguably less clear, form:
ids = params[:id_arrays].to_a.inject([]) {|c, id_array| c + sub_array.split(",") }
Except you'd probably encapsulate that in a method:
def multiple_id_param(param_name)
params[param_name].to_a.inject([]) do |c, id_array|
c + id_array.split(",")
end
end
ids = multiple_id_param(:id_array)
But then I fully appreciate that that's the kind of work you were trying to avoid by just calling the check_box_tag with some clever parameters. I guess this answer boils down to: "I don't think you can".
Although your use case is not absolutely clear to me, this might help:
<% Outerloop.each do |o| %>
<% Innerloop.each do |i| %>
<%= checkbox_tag "id_array[#{o.id}][]", i.id %>
<% end %>
<% end %>
I have 2 models:
1) upload
2) date_range
there is an intermediate join table as these models are associated by a many to many relationship thus, each is habtm to the other.
In my view for uploads(index.html.erb) Im trying to show all the date_ranges for a particular upload as follows:
<tr>
<th>File name</th>
<th>Upload Date, Time, Filename</th>
<th>Type</th>
<th>Dates in Upload</th>
<th>Total Rows</th>
<th>Rows Entered in DB</th>
<th>Percentage Completed</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% #uploads.each do |u| %>
<tr>
<td> <%= u.sourcedata_file_name%></td>
<% path_arr = u.f_path.split("\/")%>
<td><%= path_arr[-3..-1]%></td>
<td> <%= u.sourcedata_content_type%></td>
=>> <td> <%= u.date_ranges.inspect%>
<td> <%= u.total_rows%></td>
<td> <%= u.rows_completed%></td>
like so.
This shows up as follows on the browser:
In my "Dates in Upload" column I want to only show some string with dates like this:
"2013-12-25, 2013-12-26" how do I only get these extracted out of the ActiveRecord::Associations::CollectionProxy object as it shows in the image?
Thanks
Use u.date_ranges.pluck(:date_range) to get just the date ranges.
you can then pretty it up with
u.date_ranges.pluck(:date_range).each {|range| puts range}
if you want them in a column.
I see you want them side by side, so it looks like there will only be two because it's a "range" so:
<%= u.date_ranges.pluck(:date_range).first %>, <%= u.date_ranges.pluck(:date_range).last %>
The simplest thing would probably be to add a to_s method in your DateRange model:
def to_s
date_range.to_s
end
And in your view, something like:
<%= u.date_ranges.map {|dr| dr.to_s }.join(', ') %>
However, that's really a bit too much code to put right in the view. Better would be to move that to a helper, or even use a presenter pattern. The 'draper' gem can make this kind of thing very easy, so you can do the same transformation in multiple places in your app, and keep your view template much cleaner.
This is hard to explain.
I have a form builder (Question model) that creates form fields that belong to a specific event, these questions appear on the registration page handled by the Registration model.
There are default form fields that always stays the same and then X additional ones created by the form builder. The Question model has the field "db_field", which gets populated with the corresponding db field in the Registration model.
Note that the questions also have position_ids.
What I'm trying to achieve is to get answers display under the corresponding headings in a table in the index page, my view looks like this
<% #questions = Question.where(:event_id => #event.id).order(:position) %>
<table class="table table-striped table-bordered table-condensed">
<tr>
<% #questions.each do |q| %>
<th><%= q.question %></th>
<% end %>
</tr>
<% #event.registrations.each do |r| %>
<tr>
<% #questions.each do |q| %>
<td><%= r.(q.db_field) %></td>
<% end %>
</td>
</table>
So basically I need 'q.db_field', which might be 'title', for instance, to call r.title - if that makes sense.
I've tried a few things, but nothing seems to work.
Thanks in advance,
Charl
You can use Object#send to invoke methods on a given object.
See http://ruby-doc.org/core-2.0/Object.html for more info.
so replacing <%= r.(q.db_field) %> with <%= r.send(q.db_field) %> will allow you to use the field name from the DB. If you need to specify arguments for the method being called, you can pass them in after the method name. Per the docs, method name can either be a symbol or a string, if it is a string, it will be converted to a symbol for you.
Warning I'm brand new to rails!
While reading through a tutorial it has asked me to place a hash of string keys with decimal values into the products action method (My assumption they are talking about the "def products" in the controller.
In reguards to using the products method in the controller did I place my hash correctly?
In reguards to the placing the information from the hash into a table do I even need the helper method or is there a better way?
My helper needs help and doesn't format the data correctly using .html_safe I
This is what I have so far in my controler:
def products
#hard coded as products in controller
#stuff = {"a"=>200.00, "b"=>150.00, "c"=>100.00, "d"=>9.00, "e"=>15.00, "f"=>20.00 }
end
This is what I have in my product.html.erb file
<%= form_tag(products_path) do %>
<table id="aboutus_table">
<%= products_tabler() %>
</table>
<% end %>
and then the helper...it needs help
def products_tabler
snowholder = #snow_stuff.each {|key,value|puts "<tr><td>#{key}</td><td>#{value}</td><tr>"}
return snowholder
end
puts is probably a mistake, you don't really want to print to standard out in a web service. See if this works?
def products_tabler
snowholder = ""
#snow_stuff.each {|key,value| snowholder += "<tr><td>#{key}</td><td>#{value}</td><tr>"}
return snowholder
end
I realize this is a tutorial, but using a helper that emits hardcoded html is not an improvement over having the html in the view itself.
In this case, it's really simple to do it in the view:
<table id="aboutus_table">
<% #snow_stuff.each do |key, value| %>
<tr>
<td><%= key %></td><td><%= value %></td>
</tr>
<% end %>
</table>
If you really wanted to separate the creation of the rows, a collection partial would be better. Then Rails does the iteration for you. Use this technique when you've got real data (i.e. ActiveRecords instead of hashes).
<table id="aboutus_table">
<%= render :partial => "row", :collection => #stuff %>
</table>
Then the _row partial would contain:
<tr>
<td><%= row.name %></td><td><%= row.value %></td>
</tr>