Generating a table in Ruby on Rails - ruby-on-rails

Why doesn't this produce a table when called from my view? With fields_table(#user, ["id", "username"]) I am not getting the tbody's trs or tds, but I am getting everything else.
def fields_table(obj, fields)
return false if obj.nil?
content_tag(:table) do
thead = content_tag(:thead) do
content_tag(:tr) do
content_tag(:td, "Property") + content_tag(:td, "Value")
end
end
tbody = content_tag(:tbody) do
fields.each do |name|
content_tag(:tr) do
content_tag(:td, name) + content_tag(:td, obj.read_attribute(name))
end
end
end
thead + tbody
end
end

This code just iterates through the fields. It doesn't return anything, so the enclosing tbody isn't going to have anything to content.
tbody = content_tag(:tbody) do
fields.each do |name|
content_tag(:tr) do
content_tag(:td, name) + content_tag(:td, obj.read_attribute(name))
end
end
end
You need to return something like you do in the other parts of the code or change it to something like this:
tbody = content_tag(:tbody) do
fields.map do |name|
content_tag(:tr) do
content_tag(:td, name) + content_tag(:td, obj.read_attribute(name))
end
end.join
end

I would recommend rendering a partial using the collection argument, and built in rails goodness to do this type of operation. Im guessing you want the table headings to line up with the fields? You can still do that with something along the lines of the following (havent tested, but should work),
In your model define a class method or array as constant containing the attributes you want to display on the front end, e.g.
models/user.rb
VisibleFields = [:id, :username]
#workaround for toplevel class constant warning you may get
def self.visible_fields
User::VisibleFields
end
views/users/index.html.erb
<table>
<thead>
<tr>
<% User.visible_fields.each do |field| %>
<th><%= field.to_s.titleize %></th>
<% end %>
</tr>
</thead>
<tbody>
<%= render :partial => 'user', :collection => #users %>
</tbody>
</table>
**views/users/_user.html.erb**
<tr>
<% user.visible_fields.each do |field| %>
<td class="label"><%= field.to_s.titleize %></td><td class="value"><%= user.send(:field) %></td>
<% end %>
</tr>

Related

Problems in Rails undefined method `map' for #<Contact:0x000000013aa76b48>

I'm putting together a combo box or called the same as a select in rails, I put everything together but it gives me an error that tells me that I have a problem with the map inside the select, I'm using simple_form_for and I'm doing a map inside the collection inside the selector or called in simple_for associatio.
I copy the view and the controller
This view
<h1>HistContact#index</h1>
<p>Find me in app/views/hist_contact/index.html.erb</p>
<%= simple_form_for #histcontact, url:hist_contact_index_path do |f| %>
<% f.association :contact, collection: #contacts.map{|cont| [cont.name , cont.id]}%>
<%f.submit "buscar"%>
<% end %>
<table id = "client_table" class="table table-striped table-sm">
<thead>
<tr>
<th>Id</th>
<th>fecha</th
</tr>
</thead>
<tbody>
<% #histcontacts.each do |c|%>
<tr>
<td> <%= c.id %> </td>
<td> <%= c.created_at %></td>
</tr>
<% end %>
</tbody>
</table>
the controller
class HistContactController < ApplicationController
before_action :authenticate_user!
def index
#histcontacts = HistContact.all
#contacts = Contact.all
end
def new
#histcontact = HistContact.new
#contacts = Contact.new
end
def create
#histcontact = HistContact.find(contact_id: params[:contact])
end
private
def contactID(current_user)
client_id = Client.where(user_id: current_user.id)
contact_id = Contact.where(client_id: client_id.ids[0])
return contact_id
end
end
Thank you
According to error, you are trying to map a single object instead of an array of objects. Based on your controller code, the view file you shared is probably new.html.erb. To solve this problem you need do it like this:
def new
#histcontact = HistContact.new
#contacts = Contact.all
end

Filterrific gem for two tables

I am using filterrific gem to add filters in my app. I have parents table and children table. On the children_list page which displays list of all the children with their firstname and their parent's firstname. The issue I am facing is in the search query I want to add the parent.firstname search as well for filterrific. I tried adding a join as below:-
num_or_conds = 2
joins(child: :parent).where(
terms.map { |term|
"(LOWER(children.firstname) LIKE ?) OR (LOWER(parents.firstname) LIKE ?) "
But this didnt do the job. Any idea how this can be achieved.
parent.rb
has_many :children
child.rb
belongs_to :parent
filterrific(
available_filters: [
:search_query,
:with_clinic_date
]
)
scope :search_query, lambda { |query|
return nil if query.blank?
terms = query.downcase.split(/\s+/)
terms = terms.map { |e|
(e.gsub('*', '%') + '%').gsub(/%+/, '%')
}
num_or_conds = 2
where(
terms.map { |term|
"(LOWER(children.firstname) LIKE ?) OR (LOWER(parents.firstname) LIKE ?)"
}.join(' AND '),
*terms.map { |e| [e] * num_or_conds }.flatten
)
}
scope :with_clinic_date, lambda { |ref_date|
where('children.clinic_date = ?', ref_date)
}
end
_children.html.erb
<h1>Children</h1>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>Parent First Name</th>
<th>Child firstname</th>
</tr>
</thead>
<tbody>
<% #children.each do |child| %>
<tr>
<td><%=child.parent.firstname %></td>
<td><%=child.firstname %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
children-list.html.erb
<%= form_for_filterrific #filterrific do |f| %>
<div class="row">
<div class="col-sm-3">
Search
<%= f.text_field(
:search_query,
class: 'filterrific-periodically-observed form-control'
) %>
</div>
<div class="col-sm-3">
Request Date
<%= f.date_field(:with_clinic_date, class: 'js-datepicker form-control') %>
</div>
<div class="col-sm-3">
<br>
<%= link_to(
'Reset filters',
reset_filterrific_url,
) %>
</div>
</div>
<%= render_filterrific_spinner %>
<% end %>
<%= render( partial: 'children/children_list') %>
children.js.erb
<% js = escape_javascript(
render(partial: 'children/children_list')
) %>
$("#filterrific_results").html("<%= js %>");
AFAIK, you can't filter two separate classes on the same page. It will use the last defined filterrific instance. When I ran into this problem, I used remote forms with custom action/routes
# routes.rb
resources :parent do
get :filter_parents
resources: children do
get :filter_children
end
end
And then the controllers..
# parents_controller.rb
def index
parents_filter # this would be a helper method running your filter queries
end
def filter_parents
parents_filter # this would be a helper method running your filter queries
end
The children's controller would look similar, just different named helper method/custom action.
And then use a partial for the table. Target the table's container, and use a filter_parents.js.erb and filter_childrens.js.erb file
$('#parents-table').html('<%= escape_javascript render 'path/to/partial'%>')
// same in childrens.js.erb, just target the appropriate table

Using multiple content_tag in one method in Rails

I am trying to update a Rails 2.3 application to a newer Rails version(4/5).
I have there a method that prints a html table using a list as input, and the caller can customize the display of the rows. I also searched some existing gems that do something similar, but they don't have all the requirements I need. So I have to make this work. The code is
def model_table_2(collection, headers, options = {}, &proc)
options.reverse_merge!({
:id => nil,
:class => nil,
:style => nil,
:placeholder => 'Empty',
:caption => nil,
:summary => nil,
:footer => nil
})
placeholder_unless !collection.empty?, options[:placeholder] do
html_opt = options.slice(:id, :class, :style, :summary)
content_tag(:table, html_opt) do
table_sections = []
table_sections << content_tag(:caption, options[:caption]).to_s if options[:caption]
table_sections << content_tag(:thead,
content_tag(:tr,
headers.collect { |h|
concat(content_tag(:th, h))
}
)
)
if options[:footer]
table_sections << content_tag(:tfoot,
content_tag(:tr, content_tag(:th, concat(options[:footer]), :colspan => headers.size))
)
end
table_sections << content_tag(:tbody,
collection.each_with_index.collect do |row, row_index|
concat(
proc.call(row, cycle('odd', 'even'), row_index)
)
end.join
)
table_sections.join
end
end
end
def placeholder(message = t('general.empty'), options = {}, &proc)
# set default options
o = { :class => 'placeholder', :tag => 'p' }.merge(options)
# wrap the results of the supplied block, or just print out the message
if proc
t = o.delete(:tag)
concat tag(t, o, true), proc.binding
yield
concat "</#{t}>", proc.binding
else
content_tag o.delete(:tag), message, o
end
end
def placeholder_unless(condition, *args, &proc)
condition ? proc.call : concat(placeholder(args), proc.binding)
end
In the view file I call it like this:
<% table_cols = ["No.", "Name"] %>
<% obj_list = [{active: true, name: 'First'}, {active: true, name: 'Second'}, {active: false, name: 'Last'}, nil] %>
<%= model_table_2(obj_list, table_cols, {:class=>'table table-bordered', :caption=>'Model Table Test', :footer=>'The Footer'}) do |record, klass, row_index| -%>
<% if !record.nil? then %>
<% content_tag :tr, :class => klass + (record[:active] ? '' : ' text-muted') do -%>
<td><%= row_index+1 -%></td>
<td><%= record[:name] %></td>
<% end %>
<% else %>
<% content_tag :tr, :class => klass do -%>
<td style="text-align:center;">*</td>
<td>render form</td>
<% end %>
<% end %>
<% end %>
But the output is not how I would expect:
<table class="table table-bordered">
<th>No.</th>
<th>Name</th>
The Footer
<tr class="even">
<td>1</td>
<td>First</td>
</tr>
<tr class="odd">
<td>2</td>
<td>Second</td>
</tr>
<tr class="even text-muted">
<td>3</td>
<td>Last</td>
</tr>
<tr class="odd">
<td>*</td>
<td>render form</td>
</tr>
</table>
As you can see, some of the tags are missing, like caption, thead, tbody, tfoot. I guess it's because the content_tag calls are nested. I tried before without the table_sections array, but it didn't work either.
Also, I have an error when the list is empty, and the code goes to the placeholder... methods.
It's a weird quirk of content_tag but if you nest them you need to use a concat on each return in the inner tags. Otherwise, you just get the last returned string and the inner tags just disappear into the ether. Sadly, in my experience, I've found complex nesting isn't worth the effort of moving into a helper method.
Perhaps, a better approach would be to DRY up the html with a decorator pattern, rails partials, or using something like the cells gem.

What is the recommended pattern for extend html class in view?

I have following <tr> tag in my table
<% if user.company.nil? %>
<tr class="error">
<% else %>
<tr>
<% end %>
<td><%= user.name %></td>
</tr>
I would like to add another if statement
<% if user.disabled? %>
<tr class="disabled">
<% end %>
So when two of this statements are true I would like to receive:
<tr class="error disabled">
I know I should move that to helper but how to write good case statment for extending class depends of this statements?
def tr_classes(user)
classes = []
classes << "error" if user.company.nil?
classes << "disabled" if user.disabled?
if classes.any?
" class=\"#{classes.join(" ")}\""
end
end
<tr<%= tr_classes(user) %>>
<td><%= user.name %></td>
</tr>
But the good style is:
def tr_classes(user)
classes = []
classes << "error" if user.company.nil?
classes << "disabled" if user.disabled?
if classes.any? # method return nil unless
classes.join(" ")
end
end
<%= content_tag :tr, :class => tr_classes(user) do -%> # if tr_classes.nil? blank <tr>
<td><%= user.name %></td>
<% end -%>
you could try a helper method, something like
def user_table_row(user)
css = ""
css = "#{css} error" if user.company.nil?
css = "#{css} disabled" if user.disabled?
content_tag :tr, class: css
end
not sure how well this will work in the case of a table row, as you will want to nest td inside it
UPDATE: here is updated version yielding the block of td code
def user_table_row(user)
css = # derive css, using string or array join style
options = {}
options[:class] = css if css.length > 0
content_tag :tr, options do
yield
end
end
then in the view
<%= user_table_row(user) do %>
<td><%= user.name %></td>
<% end %>

update_attributes is not working

Here's my controller
class ActivitiesController < ApplicationController
def exercises
if current_user.userprofile.present? #chef whether there is a userprofile object
#weeknum = current_user.userprofile.week
#dayly_activity = Activity.where(:week => 1, :day => 'Monday').first
end #end check userprofile
end
def updatexercises
respond_to do | format |
#dayly_activity = Activity.where(:week => 1, :day => 'Monday').first
#dayly_activity.update_attributes(params[:#dayly_activity])
#dayly_activity.save
format.html { render action: "exercises" }
end
end
end
And my template
<h1>WEEKLY EXERCICES</h1>
Day : <%= #dayly_activity.day %>
<%= form_for(#dayly_activity, :url => { :action => "updatexercises" }) do | f | %>
<table>
<tr>
<td>Jogging:</td>
<td>
<% list = (0..20).to_a %>
<%= f.select :jog, list %>
x 0.1 km
</td>
</tr>
<tr>
<td>Bicycling:</td>
<td>
<% list = (0..10).to_a %>
<%= f.select :bicycl, list %>
km
</td>
</tr>
<tr>
<td>Push ups:</td>
<td>
<% list = (0..20).to_a %>
<%= f.select :pushups, list %>
x 10 times
</td>
</tr>
<tr>
<td colspan = "2"><%= f.submit %></td>
</tr>
</table>
<% end %>
When I click the button, the Daily+activity object is not being saved. Am I missing some thing
EDIT
I've tried to hard code this way and it saving to the database.
#dayly_activity.jog = 17
#dayly_activity.pushups = 13
#dayly_activity.save
Obviously, the problem must be with the update_attributes
You need to use params[:dayly_activity] (drop the # sign).
Also, I would put these two lines :
#dayly_activity = Activity.where(:week => 1, :day => 'Monday').first
#dayly_activity.update_attributes(params[:dayly_activity])
Outside of your respond_to block (put them on top of it).
You can also drop the #dayly_activity.save, update_attributes do it automatically and will returns true/false if it works/fails.
You have error in [:#dayly_activity]
And in that code
#dayly_activity.update_attributes(params[:#dayly_activity])
#dayly_activity.save
save is useless. update_attributes saving the record.
It better to check result of update_attributes. So you can catch validation errors.
For example
if #dayly_activity.update_attributes(params[:dayly_activity])
redirect_to dayli_activity_path, :notice => "Updated"
else
render :edit
end

Resources