Multiple Partial Updates in Rails using Turbo Stream - ruby-on-rails

I have two models, one for customer and one for complaints, one customer also has many complaints. If a complaint is added to the customer, how can you use Turbo Stream to update both the index of complaints and the show view of the customer using two different partials while ensuring only the specific customer's complaints are listed in the customer show view?
Here is some of my code:
Customer model:
class Customer < ApplicationRecord
validates_presence_of :names
belongs_to :company
has_many :complaints
broadcasts_to ->(customer) { [customer.company, "customers"] }, inserts_by: :prepend
end
Complaint model:
class Complaint < ApplicationRecord
belongs_to :company
belongs_to :customer
broadcasts_to ->(complaint) { [transgression.company, "complaints"] }, inserts_by: :prepend
end
Complaints index.html.erb (streaming fine):
<%= turbo_stream_from #company, "complaints" %>
<h1>Complaints</h1>
<%= link_to new_complaint_path, class: "btn", data: { turbo_frame: "remote_modal" } do %>
New
<% end %>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Customer</th>
<th scope="col">Date</th>
<th scope="col">Note</th>
<th colspan="2"></th>
</tr>
</thead>
<tbody id="complaints">
<%= render #complaints %>
</tbody>
</table>
Customer show.html.erb view (not streaming...):
<%= turbo_stream_from #customer %>
<%= turbo_stream_from #customer, "complaints" %>
<%= turbo_frame_tag "customer" do %>
<%= #customer.names %>
<% end %>
<%= link_to edit_employee_path(#customer), :class => "btn", data: { turbo_frame: "remote_modal" } do %>
Edit
<% end %>
<h4>Complaints</h4>
<%= link_to new_complaint_path, class: "btn", data: { turbo_frame: "remote_modal" } do %>
Add
<% end %>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Note</th>
<th colspan="2"></th>
</tr>
</thead>
<tbody id="complaints">
<%= render #complaints %>
</tbody>
</table>
</div>

On customer show page you're subscribing to #customer, "complaints" but you're not broadcasting anything to it, so there are no updates.
# app/models/complaint.rb
class Complaint < ApplicationRecord
belongs_to :company
belongs_to :customer
# NOTE: this will send turbo streams to `[company, "complaints"]`
# you have to be subscribed to that channel to receive updates.
# Defaults:
# partial: "complaints/complaint"
# target: "complaints"
broadcasts_to ->(complaint) { [complaint.company, "complaints"] }, inserts_by: :prepend
# NOTE: to receive updates on the customer show page you have send
# updates to [customer, "complaints"]
broadcasts_to ->(complaint) { [complaint.customer, "complaints"] },
inserts_by: :prepend,
partial: "customers/complaint" # <= if you want to use a different partial
end
# app/views/customers/show.html.erb
# make sure to subscribe to a channel that you're broadcasting to
<%= turbo_stream_from #customer, "complaints" %>
# you have to have an `id` target for the incoming `turbo_stream`
<div id="complaints">
# render a different partial
<%= render partial: "customers/complaint", collection: #complaints %>
</div>
I'm not sure what issues you have in the controllers, you don't need to do anything for broadcasts to work:
# app/controllers/customers_controller.rb
# GET /customers/1
def show
# NOTE: only show specific customer complaints on initial request
# broadcasts are already limited to `#customer`
#complaints = #customer.complaints
end

Related

how can i get project_id by remarks in ruby on rails

I have manager remark model that takes input as a remark and decision value and saves it with the project site ID. I have a project site model that takes input as name, date, and file and stores it. Many remarks have a many to one relation with project site ID, and the project site belongs to the manager remark. I want to access the decision attribute boolean value in project site index form, but I am unable to access that boolean value in the index page of the project site. Here is my code of project site and manager remarks model, view and controller-
project site index.html.erb
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Attendance</th>
<th>Status</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #project_sites.each do |project_site| %>
<tr>
<td><%= project_site.name.titleize %></td>
<td><%= project_site.date %></td>
<td><%= link_to ' View attendance', project_site.file, :class => "fi-page-export-csv" %></td>
<td><%= "here i want to access manager remark decision value" %></td>
<td><%= link_to 'Remark ', project_site %><span>(<%= project_site.manager_remarks.size %>)</span></td>
<td><%= link_to 'Edit', edit_project_site_path(project_site) %></td>
<td><%= link_to 'Destroy', project_site, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
project site controller
def index
#project_sites = ProjectSite.all.order("created_at DESC")
#manager_remark = ManagerRemark.joins(:project_site).where(:project_sites => { :user_id => #user.id })
end
# GET /project_sites/1
# GET /project_sites/1.json
def show
#manager_remark = ManagerRemark.new
#manager_remark.project_site_id = #project_site.id
end
# GET /project_sites/new
def new
#project_site = ProjectSite.new
end
def project_site_params
params.require(:project_site).permit(:name, :date, :file)
end
manager_remark controller
class ManagerRemarksController < ApplicationController
def create
#manager_remark = ManagerRemark.new(remark_params)
#manager_remark.project_site_id = params[:project_site_id]
#manager_remark.save
redirect_to project_site_path(#manager_remark.project_site)
end
def remark_params
params.require(:manager_remark).permit(:remark, :decision)
end
end
manager_remark view form
<%= form_for [ #project_site, #manager_remark ] do |f| %>
<div class="row">
<div class="medium-6 columns">
<%= f.radio_button :decision, true %>
<%= f.label :approve %>
<%= f.radio_button :decision, false %>
<%= f.label :reject %>
</div>
<br>
<br>
<div class="medium-6 cloumns">
<%= f.label :remark %><br/>
<%= f.text_area :remark %>
</div>
</div>
<div>
<%= f.submit 'Submit', :class => 'button primary' %>
</div>
<% end %>
routes.rb
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :project_sites do
resources :manager_remarks
end
get '/project_manager_level_two' => 'project_manager_level_two#index'
get '/project_managers' => 'project_managers#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
If I understand correctly, you have a ProjectSite that contains a ManagerRemark with a decision, right? If that's the case, the simple answer is:
<%= project_site.ManagerRemark.decision %>
If you are saying that each ProjectSite has many ManagerRemarks, you'll want to place the above inside a loop, like so:
<% project_site.manager_remarks.each do |manager_remark| %>
<%= manager_remark.decision %><br/>
<% end %>
This assumes that your models are correctly configured to recognize these relationships. The above may also be optimized by adding an include clause to your fetch inside the controller and there's no need to fetch the ManagerRemark objects separately. Therefore, you'd probably want something like:
def index
#project_sites = ProjectSite.all.includes( :manager_remark ).order("created_at DESC")
end

Rails: how to render nested form in table

For example, I have two models:
class Task < ApplicationRecord
has_many :task_details
end
class TaskDetail < ApplicationRecord
belong_to :task
end
I want to display a table, each row in table is one TaskDetail and allow user input. After that user submits, all data will put to server. Here is my code:
(Note that: I #data[:task] is a task object because I want to return a hash with some information for view)
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table> ... </table>
<% end %>
My question is: How can I do as my requirement.
thanks
Ensure that your Task model has accepts_nested_attributes_for :task_details and then you can do something like...
<%= form_for #data[:task], :url => tasks_path do |f| %>
<table>
<tr>
<th>Task Name</th>
<th>Task Description</th>
<tr>
<%= f.fields_for :task_details do |task_detail| %>
<tr>
<%= task_detail.hidden_field :id %>
<td><%= task_detail.text_field :name %></td>
<td><%= task_detail.text_field :description %> </td>
<tr>
<% end %>
</table>
<% end %>
Note the use of the hidden field for :id ... you need that so that rails can distinguish data from existing tasks versus a new task you're entering.
In your new method you should ensure there's at least one new task detail to provide an empty line on the form to input the detail
def new
...
#data[:task].task_details.build
...
end

How do I use lookup_context to make this view as DRY as possible?

For starters, this is the view I am trying to replicate:
This is the HTML from that layout (from the SAT portion anyway, you can extrapolate the rest):
<table class="table table-hover table-bordered">
<thead>
<td colspan="2" class="text-center">
<strong>SAT</strong>
</td>
<tr>
<th>Subject</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr>
<td>Reading</td>
<td>900</td>
</tr>
<tr>
<td>Math</td>
<td>700</td>
</tr>
<tr>
<td>Writing</td>
<td>800</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td><strong>2,400</strong></td>
</tr>
</tbody>
This is what my Grade.rb model looks like:
# == Schema Information
#
# Table name: grades
#
# id :integer not null, primary key
# subject :string
# result :string
# grade_type :integer
# profile_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Grade < ActiveRecord::Base
belongs_to :profile
enum grade_type: { csec: 0, cape: 1, sat: 2, g7: 3, g8: 4, g9: 5, g10: 6, g11: 7, g12: 8, g13: 9 }
end
This is what that table looks like currently, i.e. before using the lookup_context method in Rails:
<table class="table table-hover table-bordered">
<thead>
<td colspan="2" class="text-center">
<strong>SAT</strong>
</td>
<tr>
<th>Subject</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<% #sat_grades.each do |grade| %>
<tr>
<% if grade.subject.eql? "Total" %>
<td><strong><%= grade.subject %></strong></td>
<td><strong><%= grade.result %></strong></td>
<% else %>
<td><%= grade.subject %></td>
<td><%= grade.result %></td>
<% end %>
</tr>
<% end %>
</tbody>
Where #sat_grades is this: #sat_grades = #profile.grades.where(grade_type: :sat).
I want to use this lookup_context method, I was thinking like this:
<% #grades.each do |grade| %>
<% if lookup_context.template_exists?(grade.grade_type, "grades/grade_types", true) %>
<%= render partial: "grade/grade_types/#{grade.grade_type}", locals: {event: event, index: index} %>
<% end %>
<% end %>
The issue I am running into is that each grade_type has a different table. So grade_type: :sat belongs in the "SAT" table, the same for "CSEC", "g11", etc.
I can't think of a way to have each of those grade_types rendered specifically within their HTML table, without having lots of lookup_context.template_exists? calls within that view.
It almost defeats the purpose of doing it like that, if I have to have a lookup_context call for each grade_type.
What's the best way to approach this so I just have 1 lookup_context call (if possible), but it correctly renders and handles all the different grades correctly.
With the given fragment I would try the following:
# Render each grade
<%= render(partial: "grade/grade", collection: #grades, locals: {event: event, index: index}) || "There's grade to be displayed" %>
# Render Concated content
<%= content_for :all_grades %>
Within grade/_grade.html.erb:
# If a special grade template exists prepare the content to be shown
# but don't display it right now
<% if lookup_context.template_exists?(grade.grade_type, "grades/grade_types", true) %>
<%= render partial: "grade/grade_types/#{grade.grade_type}", locals: {event: event, index: index} %>
<% end %>
# Render the common stuff
...
# Display the special stuff stored for the grade
<%= content_for :grade_table %>
# Repeat previous steps
...
Within the grade template (for instance grade/grade_types/_g7.html.erb):
# remove content from previous grades
<% content_for :grade_table, flush: true do %>
...
<% end %>
<% content_for :xxx_xxx, flush: true do %>
...
<% end %>
...
# Concat content for all grades together (flush: false)
<% content_for :all_grades do %>
...
<% end %>
Another approach can be a presenter or maybe even Single Table Inheritance.

Rails 4 - Displaying associated attribute (without all attributes in associated table)

I'm trying to make an app in Rails 4.
I have a profile model.
Im trying to display a user's roles in that profile show page.
I have three models:
User
rolify (which has a user_role join table)
Role
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
Profile
belongs_to :user
In my profile show page, I have:
<%= #profile.user.roles.each do |role| %>
<%= role.name.titlecase %> <span style= "margin-right: 30px"></span>
<% end %>
In the show view, I get:
Manager [#<Role id: 9, name: "faculty_manager", resource_id: nil, resource_type: nil, created_at: "2016-01-16 08:06:55", updated_at: "2016-01-16 08:06:55">]
The 'Manager' part is the only correct part. How do I get the show page not to set out all the other attributes in the role table?
You have <%= #profile instead of just <% #profile which puts result of enumerator in the view
<% #profile.user.roles.each do |role| %>
<%= role.name.titlecase %> <span style= "margin-right: 30px"></span>
<% end %>
If you want to fetch name only for each role then do this
<% #profile.user.roles.pluck(:name).each do |role_name| %>
To set your show page in a nice table view format do something like the following in you show page code:
The error was using <%= #profile.user.roles.each do |role| %>
Note the = you used
<table class="table ld-margin-top-20">
<thead>
<tr>
<th>Manager ID</th>
<th>Manger Name</th>
<th>Manger Resource ID</th>
<th>Manger Resource Type</th>
</tr>
</thead>
<tbody>
<% #profile.user.roles.each do |role| %>
<tr>
<td> <%= role.id %></td>
<td> <%= role.name.titlecase %></td>
<td><%= role.resource_id %></td>
<td><%= role.resource_type %></td>
</tr>
<% end %>
</tbody>
</table>

Should I save a Model calculation as an attribute?

I have an app that enables a user to create a new room using a form to enter name, description, length, and width. Each room created becomes a new record and the app calculates the 'size' of the room as 'length' * 'width'. This is a simple app that I'm playing with to learn Rails but I may take it further to have a collection of rooms form a house, with some total 'size' of each house.
My question relates to the 'size' value and how that should be integrated into the app. I initially thought that the user should see the value of 'size' right away on the form, but shelved that once it appeared that Ajax may be required. I moved the 'size' method calculation from the view to the model to conform to "fat model, skinny controller" concept and I now show the 'size' in the 'index' view, leaving the 'new' view purely for entering data.
I initially set up the model to include length, width and size. See migration for the Room model:
20150118183743_create_rooms.rb
class CreateRooms < ActiveRecord::Migration
def change
create_table :rooms do |t|
t.string :name
t.text :description
t.integer :length
t.integer :width
t.integer :size
t.timestamps null: false
end
end
end
Should I save 'size' for each record to the database? I've read that it shouldn't be necessary to save calculations as an attribute to the model. Presumably, the app should handle that? What's the correct way to think about this?
My 'index' view calculates & returns max 'length' and 'width', but I run into an error when I try to calculate the max 'size'. I have a calculation (i.e., method) for this in model but it appears to be wrong. Any suggestions?
Below are relevant code:
room.rb
class Room < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 30 },
uniqueness: { case_sensitive: false }
validates :length, :width, presence: true,
numericality: { only_integer: true,
less_than_or_equal_to: 1000,
greater_than_or_equal_to: 1 }
def size
size = length * width
end
def max_room
size.max
end
end
rooms_controller.rb
class RoomsController < ApplicationController
def show
#room = Room.find(params[:id])
end
def new
#room = Room.new
end
def index
#rooms = Room.all
end
def create
#room = Room.new(user_params)
if #room.save #a boolean, if able to save the instance
flash[:success] = "You created a new Room!!"
redirect_to #room #we send the user to the room
else
render 'new' #so we want to render the new template
end
end
private
def user_params
params.require(:room).permit(:name, :description, :length,
:width, :size)
end
end
index.html.erb
<% provide(:title, 'All Rooms') %>
<h1>All rooms</h1>
<div class="container">
<div class="row column-md-7">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th class="text-right">Length (ft.) </th>
<th class="text-right">Width (ft.) </th>
<th class="text-right">Size (sq.ft.) </th>
<th class="text-center">Delete? </th>
</tr>
</thead>
<tbody>
<% #rooms.each do |room| %>
<tr>
<td> <%= link_to room.name, room %> </td>
<td> <%= room.description %> </td>
<td class="text-right"> <%= room.length %> </td>
<td class="text-right"> <%= room.width %> </td>
<td class="text-right"> <%= room.size %> </td>
<td class="text-center"> <%= link_to "delete", room, method: :delete,
data: { confirm: "You sure?" } %> </td>
</tr>
<% end %>
</tbody>
</table>
<div class="alert alert-info">
The model contains <%= pluralize(Room.count, "room") %> in total.
The max length is <%= Room.maximum('length') %>.
The max width is <%= Room.maximum('width') %>.
</div>
</div>
</div>
I tried showing 'size' by adding
The max size is <%= Room.max_room %>
but that returned an error.
new.html.erb
<% provide(:title, "New Room") %>
<h1>The Rooms page </h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#room) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.label :length, "Length (ft.)" %>
<%= f.number_field :length %>
<%= f.label :width, "Width (ft.)" %>
<%= f.number_field :width %>
<%= f.submit "Create my room", class: "btn btn-primary" %>
<% end %>
</div>
</div>
show.html.erb
<% provide(:title, #room.name) %>
<h1>The "<%= #room.name %>" page </h1>
<h2>This page contains the show action associated with the
Rooms page </h2>
<br>
<br>
<div class="container">
<div class="col-md-6 col-md-offset-3">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th class="text-right">Length (ft.) </th>
<th class="text-right">Width (ft.) </th>
<th class="text-right">Size (sq.ft.) </th>
</tr>
</thead>
<tbody>
<tr>
<td> <%= #room.name %> </td>
<td> <%= #room.description %> </td>
<td class="text-right"> <%= #room.length %> </td>
<td class="text-right"> <%= #room.width %> </td>
<td class="text-right"> <%= #room.size %> </td>
</tr>
</tbody>
</table>
</div>
</div>
<hr>
<%= link_to "Create a new room", new_room_path, class: "btn btn btn-primary" %>
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get 'home' => 'static_pages#home'
get 'calculations' => 'static_pages#calculations'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'new_room' => 'rooms#new'
get 'rooms' => 'rooms#index'
resources :rooms
end
I plan to use apps that will be heavy with numerical calculations so I want to get these fundamentals right. I don't want the app's database to blow up if I'm saving too many calculations down when they should be done (perhaps) in a virtual environment.
So, to recap ....
Should a app calculation be saved to the database as an attribute to a new record?
What might be the correct calculation/method for 'size'?
If I want to perform a calculation on a calculated value, does that value first have to be saved as an attribute?
Your implementation of max_room is wrong, since the size value is just a number, the max method is not defined on a number, but instead it should be called on an Enumerable of values.
So Room should be implemented this way:
class Room < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 30 },
uniqueness: { case_sensitive: false }
validates :length, :width, presence: true,
numericality: { only_integer: true,
less_than_or_equal_to: 1000,
greater_than_or_equal_to: 1 }
def size
size = length * width
end
class << self
# This is a class method, since it depends on all the rooms
# not on a specific room
def max_size
# This will delegate the calculation to the database
select('MAX(length * width) AS max')[0]['max'];
end
# But, this will instantiate the records on memory before it makes the calculation
# def max_room
# all.max{ |room| room.length * room.width }
# end
# This is a class method as well
def max_room
order('size DESC').first
end
end
end
Should a app calculation be saved to the database as an attribute to a new record?
If the attributes on which the calculated value depends will change frequently, in this case you should not save the calculated value, but rather calculate it every time you need it. But as I can see the length and the width of a room will not change, so the calculated value will need to be calculated once, and saved to be used when needed (e.g to calculate the max_size), so in this case you need to create an attribute size and calulate it when you create the record using a hook.
before_save :calculate_size
private
def calculate_size
size = length * width
end

Resources