Ruby on Rails 7 Hotwire Turbo in table html - ruby-on-rails

I have a simple ruby on rails 7 app that uses Hotwire and turbo frames. I have a HTML table on my index page, and would like to make it with turbo frame tag, so that all the actions stay on index page.
Here is the index file:
<%= turbo_stream_from "companies" %>
<main class="container">
<div class="header">
<h1>Companies</h1>
<%= link_to "Add company",
new_company_path,
class: "btn btn--primary",
data: { turbo_frame: dom_id(Company.new) }
%>
</div>
<%= turbo_frame_tag Company.new %>
<div class="table-responsive">
<table class="table mb-0">
<thead>
<tr>
<th><%= sort_link(#q, :company_name) %></th>
<th><%= sort_link(#q, :street_address) %></th>
<th><%= sort_link(#q, :postal_code) %></th>
<th><%= sort_link(#q, :city) %></th>
<th><%= sort_link(#q, :country) %></th>
<th colspan= 2> Action</th>
</tr>
</thead>
<tbody id="companies_table">
<%= render #companies %>
</tbody>
</table>
</div>
<%= raw pagy_nav(#pagy) %>
</main>
Here is _company.html.erb partal:
<%= content_tag :tr, id: dom_id(company) do %>
<td><%= company.company_name %></td>
<td><%= company.street_address %></td>
<td><%= company.postal_code %></td>
<td><%= company.city %></td>
<td><%= company.country %></td>
<td>
<%= link_to edit_company_path(company), class: "btn btn--light" do %>
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-pencil-square svg-icon" viewBox="0 0 16 16">
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
</svg>
<% end %>
</td>
<td>
<%= button_to company_path(company), method: :delete, class: "btn btn--light" do %>
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-trash svg-icon" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
</svg>
<% end %>
</td>
<% end %>
And here is create.turbo_stream.erb file:
<%= turbo_stream.prepend "companies_table", #company %>
<%= turbo_stream.update Company.new, "" %>
<%= render_turbo_stream_flash_messages %>
Is it possible to make this table somehow dynamic, after creating a new record, to append the row to table.
thank you
PS. If I missed an important piece of puzzle, please let me know. thanks

I'm going to mention that this is a longstanding issue with Turbo and Tables that is still not really satisfied, as can be seen from the following issue thread on the Turbo repo. Sadly, it is still an open issue. The thread I linked to does offer some solutions. Alternatively, you can also ignore the semantically correct html and build your table using <div> elements and style them as you might style a table. Sorry that this issue continues to be troublesome, though you are not alone.

Related

Rails edit form update query error (turbo frames)

When I try to update a service and submit everything updates except the name that goes to the SQL query with the old value.
The edit form: https://i.stack.imgur.com/YSZuD.png
If I submit this I get this:
↳ app/controllers/services_controller.rb:133:in `block in update'
Service Exists? (5.4ms) SELECT 1 AS one FROM `services` WHERE `services`.`name` = 'Tratamento 2' AND `services`.`id` != 2 LIMIT 1
↳ app/controllers/services_controller.rb:133:in `block in update'
Service Exists? (3.9ms) SELECT 1 AS one FROM `services` WHERE `services`.`ref` = 'test' AND `services`.`id` != 2 LIMIT 1
↳ app/controllers/services_controller.rb:133:in `block in update'
Service Update (3.9ms) UPDATE `services` SET `services`.`ref` = 'test', `services`.`description` = 'test', `services`.`updated_at` = '2021-12-29 11:05:20.531023' WHERE `services`.`id` = 2
ref gets the new value but name search for "Tratamento 2" (Old value).
/services/_edit.html.erb
<%= turbo_frame_tag :edit_service_details do %>
<div data-controller="unblind-modal">
<div data-unblind-modal-target="background" data-popup-confirm-delete-target="background" class="modal-background" style="z-index: 9998;"></div>
<div data-unblind-modal-target="content" data-target-modal="edit_details" class="fixed inset-0 overflow-y-auto flex items-center justify-center m-6" style="z-index: 9999;">
<%= form_with model: #service, url: polymorphic_path([#service], { business_id: #service.business.id }), class: " relative w-full flex flex-col items-center justify-center overflow-auto bg-white rounded-lg max-w-screen-sm" do |form| %>
<div class="w-full sticky top-0 flex items-center">
<div class="mx-6 py-4 w-full flex justify-between items-center">
<h1 class="txt_heading text-neutral-100 text-neutral-80">Edit Details</h1>
<div data-action="click->unblind-modal#close" data-target-modal="edit_details" class="btn-secondary btn-md flex items-center w-9 px-0 justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18L18 6M6 6l12 12"/>
</svg>
</div>
</div>
</div>
<div class="w-full h-auto flex flex-wrap md:flex-nowrap overflow-auto">
<div class="w-full overflow-auto">
<div class="px-6 pb-4 overflow-auto space-y-4">
<div class="space-y-1 w-full">
<%= form.label :ref, :Reference, class: "input-label" %>
<%= form.text_field :ref, placeholder: :reference, autofocus: true, class: "text-field input-md w-1/2" %>
<% unless #service.errors[:ref].empty? %>
<div class="flex space-x-1 ">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-error" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<span class="txt_caption text-error ">The reference field <%= #service.errors[:ref].first %></span>
</div>
<% end %>
</div>
<div class="space-y-1 w-full">
<%= form.label :name, class: "input-label" %>
<%= form.text_field :name, placeholder: :name, autofocus: true, class: "text-field input-md w-full" %>
<% unless #service.errors[:name].empty? %>
<div class="flex space-x-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-error" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<span class="txt_caption text-error ">The first name field <%= #service.errors[:name].first %></span>
</div>
<% end %>
</div>
<div class="space-y-1 w-full">
<%= form.label :description, class: "input-label" %>
<%= form.text_area :description, rows: 3, placeholder: :description, class: "text-field w-full" %>
<% unless #service.errors[:description].empty? %>
<div class="flex space-x-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-error" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<span class="txt_caption text-error ">The last name
field <%= #service.errors[:description].first %></span>
</div>
<% end %>
</div>
</div>
</div>
</div>
<div class="w-full sticky bottom-0 flex items-center">
<div class="mx-6 py-4 w-full h-full flex justify-end items-center">
<%= form.submit "Save", class: "btn-primary btn-md", disabled: false %>
</div>
</div>
<% end %>
</div>
</div>
<% end %>
services_controller.rb
def update
respond_to do |format|
if #service.update(service_params)
flash.now[:success] = ["The Service " + #service.name + " was updated"]
format.turbo_stream {
render turbo_stream: [
turbo_stream.update("flash", partial: "flash"), #Show Notification
turbo_stream.update("show_service_" + #service.id.to_s, partial: "show", locals: { service: #service }),
turbo_stream.update("edit_service_details"),
turbo_stream.update("edit_service_categories"),
turbo_stream.update("edit_service_variants"),
]
}
else
flash.now[:error] = ["The Service " + #service.name + " had an error."]
format.turbo_stream {
render turbo_stream: [
turbo_stream.update("flash", partial: "flash")
]
}
end
end
end
service_params:
def service_params
params.require(:service).permit(:name, :description, :ref, :state,
service_per_categories_attributes: [:_destroy, :id, :service_category_id],
service_variants_attributes: [
:_destroy,
:id,
:name,
:state,
:price],
)
end

Nested Loop method, RoR

<div class="row">
<% #group.each do |j| %>
<% #group.each_with_index do |i, index| %>
<% if j.category_id == i.category_id %>
<%= j.category_id%>
<%= j.title %>
<% break %>
<% end %>
<% end %>
<% end %>
</div>
This is the result:
1 Ultimo de ultimo 1 Benza Comedia 1 Lazo Comedia 1 Juans comedia 1 Primer Titulo Comedia
2 acc last 2 Acc Benza 2 Juans Acc 2 Primer titulo Accion
3 terr last 3 Juans terr 3 Terror tittle
4 Juans rom 4 Romance tittle
I only want the result to be the first find.
I want it to only be: 1 Ultimo de ultimo, 2 acc last, 3 terr last and 4 Juans rom.
You can try grouping it first
<div class="row">
<% #group.group_by { |g| g.category_id }.each_with_index do |(key, val), index| %>
<%= key %>
<%= val.first.title %>
<% end %>
</div>

Rails summary with sub-totals by date

I'm writing my first Rails view that summarizes data by date. I want one row for each date with the columns summarized for that date.
I have been able to make it work. But, it's awkward coding. This is what I have:
<h3>Carwings Daily Summary</h3>
<table class="display dataTable table table-striped table-bordered" id="dataTable2">
<thead>
<tr>
<th>Date</th>
<th># Trips</th>
<th>E Consumption (kWh)</th>
<th>E Regeneration (kWh)</th>
<th>E Total (kWh)</th>
<th>Distance (Miles)</th>
<th>Energy Economy (Miles/kWh)</th>
<th>CO2 Emission Reduction (lbs)</th>
</tr>
</thead>
<tbody>
<% trips = 0 %>
<% consumption = 0 %>
<% regen = 0 %>
<% total = 0 %>
<% distance = 0 %>
<% economy = 0 %>
<% emissions = 0 %>
<% sumdate = nil %>
<% #carwings.each.with_index do |carwing, index| %>
<% sumdate = carwing.date if index == 0 %>
<% if carwing.date == sumdate %>
<% trips = trips + 1 %>
<% consumption = consumption + carwing.e_consumption %>
<% regen = regen + carwing.e_regen %>
<% total = total + carwing.e_total %>
<% distance = distance + carwing.distance %>
<% economy = economy + carwing.economy %>
<% emissions = emissions + carwing.emission_reduction %>
<% else %>
<tr>
<td class="nowrap"><%= sumdate %></td>
<td><%= trips %></td>
<td><%= consumption %></td>
<td><%= regen %></td>
<td><%= total %></td>
<td><%= distance %></td>
<td><%= economy %></td>
<td><%= emissions %></td>
</tr>
<% trips = 1 %>
<% consumption = carwing.e_consumption %>
<% regen = carwing.e_regen %>
<% total = carwing.e_total %>
<% distance = carwing.distance %>
<% economy = carwing.economy %>
<% emissions = carwing.emission_reduction %>
<% sumdate = carwing.date %>
<% end %>
<% end %>
<tr>
<td class="nowrap"><%= sumdate %></td>
<td><%= trips %></td>
<td><%= consumption %></td>
<td><%= regen %></td>
<td><%= total %></td>
<td><%= distance %></td>
<td><%= economy %></td>
<td><%= emissions %></td>
</tr>
</tbody>
</table>
There's got to be a better way.
Suggestions?
Thanks for the Help!!
Some minor stuff:
trips = trips + 1
# is better written as:
trips += 1
erb tags can be multiline eg:
<% if blah
do_something
something else
end %>
If you are setting multiple variables to the same value, you don't need to repeat them each line eg:
trips = consumption = regen = 0
yes - this is minor stuff - but clean up the minor stuff and it'll give you a better shape of what you're trying to do.
perhaps if you gave us your logic in descriptive pseudocode (so we aren't just guessing what you're trying to do) then we can give you better structure to your code too. :)
Personally: I'd recommend setting up all this data in your controller (or even your carwing model) before it ever hits the view. I'd use a hash - whee the carwnig.date is the key, and all the rest is another hash eg:
data = Hash.new({:trips => 0, :consumption => 0}) # google initialising hashes
#carwings.each do |carwing|
data[carwing.date][:trips] += 1
data[carwing.date][:consumption] += carwing.e_consumption
# etc
end

rails: organizing data into a table using for

I have the following view:
<table class="fixed">
<tr>
<th>Student Name</th>
<!-- create as many <th> as there are evaluations -->
<% #eval_count.times do |i| %>
<th>Evaluation <%= i+1 %></th>
<% end %>
<th>Student Average <br />(for this goal)</th>
</tr>
<% for eval in #evals %>
<tr class="<%= cycle("odd", "even", name: "evals")%>">
<!-- eval returns { s_id [eval],[eval]} -->
<td><%= eval[1].first.student.name%></td>
<!-- in each student's row, print the score for each consecutive evaluation -->
<% #eval_count.times do |i| %>
<td><%= eval[1][i].score %><% #ss_scores << eval[1][i].score %></td>
<% end %>
<td><%= #ss_scores %></td>
</tr>
<% reset_cycle("evals") %>
<% end %>
</table>
<% #ss_scores.in_groups(#student_count, false) do |group|%>
<%= (group.sum.to_f/group.size).round(2) %>
<% end %>
which renders the following:
I want to put the average for each student in the last column, but #ss_scores is a variable and so calling anything on it doesn't work. But when the for loop has finished, #ss_scores can be worked with nicely as in the bottom of the screenshot. Any idea how to do this better?
Try emptying the array everytime, using [] and calculate the average inline, like below
<td><%= #ss_scores.inject(0.0) { |sum, el| sum + el } / #ss_scores.size %></td>
<% #ss_scores = [] %>
-
<% for eval in #evals %>
<tr class="<%= cycle("odd", "even", name: "evals")%>">
<!-- eval returns { s_id [eval],[eval]} -->
<td><%= eval[1].first.student.name%></td>
<!-- in each student's row, print the score for each consecutive evaluation -->
<% #eval_count.times do |i| %>
<td><%= eval[1][i].score %>
<% #ss_scores << eval[1][i].score %>
</td>
<% end %>
<td><%= #ss_scores.inject(0.0) { |sum, el| sum + el } / #ss_scores.size %></td>
<% #ss_scores = [] %>
</tr>
<% reset_cycle("evals") %>
<% end %>

Rails each loop logic

I have a problem with my each loop. The line that prints programdetail.name and programdetail.bodypart doesn't print the value. Also do you know how can I make this loop a little bit more efficient? I want to print first 2 items with class "odd" and the other 2 with non-class. So and so forth.
<% #counter = 0 %>
<% #program.programdetails.each do |programdetail| %>
<% #counter = #counter + 1 %>
<% #counter = #counter % 3 %>
<% if (#counter == 0)
#counter -= 1
end %>
<%= '<h3 class="odd"><span class="moduleLabel"> #{programdetail.name}</span><span class="moduleDescription">#{programdetail.bodypart}</span></h3>' if #counter != 0 %>
<%= '<h3><span class="moduleLabel">#{programdetail.name}</span><span class="moduleDescription">#{programdetail.bodypart}</span></h3>' if #counter != 0 %>
<% end %>
cycle helper could have worked, if you wanted odd/even combo, or over a collection:
<% #program.programdetails.each do |programdetail| %>
<h3 class="<%= cycle("odd", "odd", "", "") %>
<span class="moduleLabel"><%= programdetail.name %></span>
<span class="moduleDescription"><%= programdetail.bodypart %></span>
</h3>
<% end %>
To fix your code:
<% #counter = 0 %>
<% #program.programdetails.each do |programdetail| %>
<% #counter = (#counter % 4) + 1 %>
<h3 class="<%= ((1..2).cover?(#counter))? 'odd': '' %>">
<span class="moduleLabel"><%= programdetail.name %></span>
<span class="moduleDescription"><%= programdetail.bodypart %></span>
</h3>
<% end %>

Resources