Is there any way we can list multiple models in one index page?
Like I have 4 models:users, agencies, authorizedpersons and mentors that I would want to list them in one index page.
Is there a specific process that I could follow?
You can just to query for all of these in that controller's index action:
class MyController < ApplicationController
def index
#users = User.all
#agencies = Agency.all
#authorized_people = AuthorizedPerson.all
#mentors = Mentor.all
respond_to do |format|
format.html
end
end
# ...
end
And reference them in your views as normal:
<% #agencies.each do |agency| %>
<!-- do stuff -->
<% end %>
<% #users.each do |user| %>
<!-- do more stuff -->
<% end %>
<!-- etc. -->
Rubyuser,
Yes, this is totally normal behavior. In your controller whenever you specify a variable using #myvariable, it is an instance variable available in the scope of the controller and view. A variable without the # is a local variable only available within that method.
So in your controller when you do:
class Foos < ApplicationController
def index
#foos = Foo.all
#bars = Bar.all
end
end
You can then reference #foos and #bars from within your view.
<h1>My foos and bars</h1>
<table>
<thead>
<th>foo</th>
</thead>
<tbody>
<% #foos.each do |f| %>
<tr>
<td>f.name</td>
</tr>
<% end %>
</tbody>
</table>
<table>
<thead>
<th>bar</th>
</thead>
<tbody>
<% #bars.each do |b| %>
<tr>
<td>b.name</td>
</tr>
<% end %>
</tbody>
</table>
Now, to keep things more clean, you may want to consider using a partial. Create a file called _bars_index.html.erb and copy the table with the bars code in it.
replace it with
<%= render "bars_index" %>
and now your code is nice and tidy and easy to follow.
Related
In my Rails 7 app I've got data table which I want to decorate. The data comes from the API response so in fact it's an array of hashes. Like below:
# transactions_controller.rb
class TransactionsController < ApplicationController
def index
response = client.transactions.list(platform_id: current_user.platform_id, page: 1, per_page: 100)
#transactions = response.body['data']
end
private
def client
#client ||= TestAPI::Client.new
end
end
Now inside the transactions/index.html.erb I've got a table with #transactions data which I want to decorate:
#views/transactions/index.html.erb
<table class="table table-striped">
<thead>
<tr>
<b>
<tr>
<th>Date</th>
<th>Amount</th>
</tr>
</b>
</tr>
</thead>
<tbody>
<% #transactions.map do |transaction| %>
<tr>
<td>
<%= transaction['created_at'] %>
</td>
<td>
<%= transaction['amount_cents'] %>
</td>
</tr>
<% end %>
</tbody>
</table>
I know I could inject that logic inside of view file to be like:
(...)
<td>
<%= Date.parse(transaction['created_at']).strftime("%d.%m.%Y") %>
</td>
<td>
<%= "#{ transactions_data.last['amount_cents']/100}" "#{ transactions_data.last['currency']}" %>
</td>
(...)
But I want to get rid of that logic from the view since I'll have more and more logic in the future here.
Kudos for wanting to remove logic from the view.
You need a new object, it could be called TransactionPresenter or whatever you choose. It will implement the view logic. So in your TransactionsController:
def index
response = client.
transactions.
list(platform_id: current_user.platform_id, page: 1, per_page: 100).
map{|t| TransactionPresenter.new(t)}
#transactions = response.body['data']
end
and the TransactionPresenter model could be something like this:
class TransactionPresenter
def initialize(transaction)
# capture the fields of interest as variables
end
def amount
"$#{amount_cents.to_f/100}" # for example, whatever makes sense in your context
end
end
so all logic is removed from the view:
<table>
<% #transactions.each do |transaction| %>
<tr><%= transaction.amount %></tr>
<% end %>
</table>
I wan to do a generic grid, that could be used in my app to show data from different Models. As I am a newbie in RoR - please let me know if my "vision" is ok, or i should change the way i think about this problem:
Controller:
class Admin::UsersController < ApplicationController
before_action :authenticate_user!
authorize_resource
def index
#current_scope = params[:role]
#users = User.select(:id, :email, :role, :last_sign_in_at).role(#current_scope)
end
end
User Model:
scope :role, -> (role) {where('role = ?', role) if role.present?}
Index view:
<%= render '/layouts/shared/grid', object: #users%>
Grid partial:
<% object.column_names.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
My problem is that i can show always ALL columns (that is what ActiveRecord::Relation returns), not only columns for parameters selected in my query. I see few potential solutions - which one would be the best?
usage of ActiveRecord::Base.connection.exec_query - if i do so, i could use then .columns method that returns what i need. But i am not sure if i like it...
usage of search_params instead of query result to iterate
Any other suggestion?
You can use values method. It's not very documented but it returns parsed sql query.
For this query:
User.select(:id, :email, :role,:last_sign_in_at).role(#current_scope).values
It returns something like this:
{:select=>[:id, :email, :role, :last_sign_in_at], :where=>#<ActiveRecord::Relation::WhereClause:0x007fe0d3b29bf0 #predicates=["role = admin"], #binds=[]>}
If a query will not use select when values will not return select result too. In this case, you need to use column_names.
Too I can recommend you wrap table code to decorator. This is raw example:
class TableDecorator
def initialize(scope)
#scope = scope
end
def header_columns
scope.values[:select] || #scope.column_names
end
def columns
#scope
end
end
# controller
#table = TableDecorator.new(#users)
# index view
<%= render '/layouts/shared/grid', locals: {table: #table} %>
# grid partial
<table>
<thead>
<tr>
<% table.header_columns.each do |column_name| %>
<th><%= column_name %></th>
<% end %>
</tr>
</thead>
<tbody>
<% table.columns.each do |item| %>
<tr>
<% table.header_columns.each do |column_name| %>
<td><%= item[column_name] %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
Here is a index.html.erb. How do I hide the table header, such as Remark, with a method when no records exists in remark attribute column? Preferably without using JavaScript.
index.html.erb
<table id = "kola" class="table listing text-center">
<% has_remark = collection_has_remark?(#aslani361s) %>
<thead>
<tr class="tr-head">
<td>Date</td>
<td>Description</td>
<td>Amount</td>
<td>Discount</td>
<td>Paid</td>
<td>Balance</td>
<td>DelnDel</td>
<% if has_remark %>
<td>Remark</td>
<% end %>
<td>Hide</td>
</tr>
</thead>
</table>
However I am able to hide the remark attribute values like as below ;
_aslani361.html.erb
<% if aslani361.remark.present? -%>
<td class="col-1"><%= aslani361.remark %></td>
<% end %>
aslani361s_helper.rb
module Aslani361sHelper
def collection_has_remark?(collection)
collection.each do |aslani361|
if aslani361.remark.present?
return true
end
end
end
end
aslani361.rb
class Aslani361 < ActiveRecord::Base
end
Any suggestions are most welcome.
Thank you in advance.
If you wish to hide the column because no records in your array has the remark value, you can do something like this:
Define a method in your helper module file for the controller:
def collection_has_remark?(collection)
collection.each do |record|
if record.remark.preset?
return true
end
end
end
Then use it in the view
<% has_remark = collection_has_remark?(#records) %>
<thead>
<tr class="tr-head">
<td>Date</td>
<td>Description</td>
<td>Amount</td>
<td>Discount</td>
<td>Paid</td>
<td>Balance</td>
<% if has_remark %>
<td>Remark</td>
<% end %>
</tr>
</thead>
Then use the same if statement inside your loop. Personally I think it's important to leave an empty column, so users know for sure it doesn't have one.
I want to print in tabular form the entries given by user. The table will contain 3 columns for name,registration number and classes attended, however the output is not as expected. Its first printing all the names and then all the registration numbers and so on. I want to print a name and the registration number according to name and classes attended.
</head><table width="100%">
<tr>
<th>NAME</th>
<th>REGISTRATION NUMBER</th>
<th>CLASSES ATTENDED</th>
</tr>
<tr>
<% #name_students.each do |t| %>
<th><%= t.name_student %></th><br>
<% end %>
<% #reg_nos.each do |t| %>
<th><%= t.reg_no %></th><br>
<% end %>
<% #classes_ats.each do |t| %>
<th><%= t.classes_at %></th><br>
<% end %>
</tr>
</table>
Here is my controller action.
class PagesController < ApplicationController
def home
#name_students = Page.all
#reg_nos = Page.all
#classes_ats = Page.all
end
def list
#name_students = Page.all
#reg_nos = Page.all
#classes_ats = Page.all
end
def add
Page.create(:name_student => params[:nam])
Page.create(:reg_no => params[:reg])
Page.create(:classes_at => params[:cls])
redirect_to :action => 'home'
end
end
If I understand what you're doing, you should probably have PagesController#home return something like #pages = Page.all object and display the data kind of like this:
<thead>
<tr>
<th>Name</th>
<th>Registration Number</th>
<th>Classes Attended</th>
</tr>
</thead>
<tbody>
<% #pages.each do |p| %>
<tr>
<td><%= p.name %></td>
<td><%= p.registration_number %></td>
<td><%= classes_attended(p.classes_attended) %></td>
</tr>
<% end %>
</tbody>
Above, classes_attended(p) is a call to a helper method that you would use to map the class names of the classes that student attended into an display-able array. Really, that kind of display logic might be better in a decorator, but a helper method should be fine for now.
Let me know if I've totally misunderstood what you're doing, and I'll delete my answer.
Edit to add:
Example index method:
def home
#pages = Page.all
end
Also, looking at your question again, is there a reason you're creating three Page objects with one attribute each instead of one Page object with all three attributes? It should probably look something like Page.create(:name_student => params[:nam], :reg_no => params[:reg], :classes_at => params[:cls]). That's pretty much the only way the solution I posted will work. Again, though, I might be totally misunderstanding what you're going for.
Lets say that I have a model with name of User. How can I add a virtual attributes to the final result of generated query ?
class User < ActiveRecord::Base
ATTRIBUTES = %w[name email balance]
scope :main_selection, -> { select('name,email,total_bought, total_deposit') }
def balance
(total_deposit - total_bought).round
end
end
and inside my controller I have
#user = User.main_selection
#attributes = User::ATTRIBUTES
Inside the View I would like to show it in a table
<table>
<thead>
<tr>
<% #attributes.each do |a| %>
<th><%= a %><th>
<% end %>
</tr>
</thead>
<tbody>
<% #user.each do |u| %>
<tr>
<% #attributes.each do |a| %>
<td><%= u[a] %><td>
<% end %>
</tr>
<% end %>
</tbody>
<table>
The only problem is that I need to add the balance attribute into the generated result, so that the loop with u[a] could work.
I need to mention that the balance can be called by #user.first.balance, but inside the loop does not work and it shows a nil value instead.
Try u.send(a) instead of u[a]
u[a] will only work on attributes. In your example, balance is not an attribute, it's a method.