the Ransack gem does not find the result - ruby-on-rails

I am using the Ransack gem, I have a patient model, I am following the documentation, and I cannot get it to work.
I have also seen many blogs about it but nothing is working for me. Hope someone can help me
I show the code of the view and the controller
my controller:
def index
#q = Patient.ransack(params[:q])
#Patients = #q.result(distinct: true)
#pagy, #patients = pagy(current_user.patients.order(apellido: :asc), items:20)
end
my view:
<%= search_form_for #q do |f| %>
<%= f.text_field :apellido_cont, class: 'form-control' %>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
<div class="row">
<div class="col-xl-3">
<h3>Mis pacientes</h3>
</div>
<div class="col-xl-6">
<div class="form-group has-search">
<span class="fa fa-search form-control-feedback"></span>
<input type="text" class="form-control" placeholder="Buscar">
</div>
</div>
<div class="col-xl-3">
<%= link_to new_patient_path, class:'nuevo' do %>
<i class="fas fa-plus"></i> NUEVO PACIENTE
<% end %>
</div>
</div>
<div class="alertas">
<% if flash[:notice] %>
<div class="alert alert-success" role="alert"><%= flash[:notice] %></div>
<% end %>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Nombre</th>
<th scope="col">Contacto</th>
<th scope="col">Edad</th>
</tr>
</thead>
<% if #patients.each do |patient| %>
<tbody>
<tr>
<td class="datos"><%= patient.apellido %> <%= patient.nombre %></td>
<td><i class="fas fa-mobile-alt"></i> <span><% if patient.telmovil.blank? %>No hay dato<% else %><%= patient.telmovil %><% end %></span></td>
<td><%= patient.age %></td>
<td><%= link_to patient_path(patient), class:'ver' do %>
<i class="far fa-eye"></i>
<% end %></td>
</tr>
<% end.empty? %>
<h5 class="titulo">No tienes ningun paciente.</h5>
<% end %>
</tbody>
</table>
<div class="paginacion mt-3">
<%== pagy_bootstrap_nav(#pagy) if #pagy.pages > 1 %>
</div>
</div>

so I'm happy I came across this post. I have been using kaminari for years and had no idea about pagy and now I'm excited to try it!
My experience is with kaminari, so I had to google usage with pagy. It looks like to me you need to pass the entire query to the pagy method.
ie:
#pagy, #patients = pagy(Patient.ransack(params[:q]).result(distinct: true))
Hopefully that works!

Related

How to select multiple items to delete (via checkbox) in Rails 6

I created a column of checkboxes where in users can select multiple items to delete on a table. Something like this but only a button "Bulk Delete":
Here's my code:
<%= render "shared/nav_dashboard" %>
<%= render "shared/header_dashboard" %>
<section>
<div class="container-fluid">
<!-- Page Header-->
<header>
<h1 class="h3 display">Pages</h1>
</header>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h4>List of Pages</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th><%= link_to "Bulk Delete", page_path(page), method: :delete, data: { confirm: "Are you sure?" }%></th>
<th>Id</th>
<th>Title</th>
<th>Summary</th>
<th>Date Created</th>
<th colspan="3">Action</th>
</tr>
</thead>
<tbody>
<% #pages.each do |page| %>
<tr>
<td><input type="checkbox" value="false"> </td>
<td scope="row"><%= page.id %></td>
<td><%= page.title %></td>
<td><%= page.body.truncate(60) %></td>
<td> <%= page.created_at.strftime("%B %d, %Y") %> </td>
<td><%= link_to "Show", page_path(page)%></td>
<td><%= link_to "Edit", edit_page_path(page)%></td>
<td><%= link_to "Delete", page_path(page), method: :delete, data: { confirm: "Are you sure?" }%></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
<%= render "shared/footer_dashboard" %>
I am wondering why it did not work?
How can I do this functionality step by step?
You could to use a form. It'll look something like this (I'm using simpleform, but this will work for every your preferred form-tag helper):
In routes.rb:
resources :pages do
collection do
delete :bulk_destroy
end
end
In your view:
<%= simple_form_for :pages, url: "/pages/bulk_destroy", method: :delete do |f| %>
<ul>
<% #pages.each do |page| %>
<li> <%= page.name %> <input type="checkbox" name="collection_ids[]" value="<%= page.id %>" /> </li>
<% end %>
</ul>
<%= f.submit %>
<% end %>
In your controller:
class PagesController < ApplicationController
def bulk_destroy
Page.where(id: params[:collection_ids]).destroy_all
end
end
Heavy-lifting taken care for you: https://github.com/joshmn/active_action or check out the bare-minimum proof of concept at https://github.com/joshmn/bulk_actions_poc
if you don't prefer to write other separate method then you can try like below, you just need to be sure that when multiple ids are selected it should send as params[:ids] rather then params[:id]
def destroy
ids = params[:ids].present? ? params[:ids] : [parmas[:id]]
Pages.where(id: ids).destroy
end
you might need to change syntax based on you jquery version
$('#bulk_delete').on('click', function(e){
var selectedIds = [];
$("input[type='checkbox']:checked").each(function(e){
ids.push($(this).data('id'))
})
$.ajax({url: 'your action url',type: 'DELETE', data: {ids: selectedIds}})
})

Is there a cleaner way to code this piece of code using Ruby on Rails?

So I have a table in my application and I want to implement sort features now. So I have this:
<div class="row dashboard__block">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-12 text-center text-primary mb-3">
<strong>Prices are accurate as
of <%= CardHelper::get_update_accuracy(Time.now - #last_update) %></strong>
</div>
</div>
<% if active_cards.present? %>
<div class="row">
<table class="table table-sm table-striped table-smaller">
<thead>
<tr>
<%# todo: create sort links to append parameters asc and desc %>
<th scope="col">
<%= link_to dashboard_active_path(params.permit(:layout, :page, :items_per_page).merge(active_title_sort: :desc)) do %>
Item title <i class="fa fa-sort"></i>
<% end %>
</th>
<th scope="col">
<%= link_to dashboard_active_path(params.permit(:layout, :page, :items_per_page).merge(active_current_price_sort: :desc)) do %>
Current Price <i class="fa fa-sort"></i>
<% end %>
</th>
<th scope="col">
<%= link_to dashboard_active_path(params.permit(:layout, :page, :items_per_page).merge(active_time_left_sort: :desc)) do %>
Time Left <i class="fa fa-sort"></i>
<% end %>
</th>
<th scope="col">Item Link</th>
</tr>
</thead>
<tbody>
<% if active_cards.present? %>
<% active_cards.each do |card| %>
<tr>
<th><%= card.item_title %></th>
<th>$ <%= card.sale_price.present? ? sprintf('%.2f', card.sale_price) : "Not Available" %></th>
<th><%= card.sale_date %></th>
<% if card.item_id.present? %>
<th>
<%= link_to "https://www.website.com/itm/#{card.item_id}", target: '_blank' do %>
<i class="fa fa-eye"></i> View
<% end %>
</th>
<% else %>
<th>
Not Available
</th>
<% end %>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
<% else %>
<%= render partial: 'shared/empty_block' %>
<% end %>
</div>
</div>
My goal is to refactor the links on the table header which now looks like:
<th scope="col">
<%= link_to dashboard_active_path(params.permit(:layout, :page, :items_per_page).merge(active_title_sort: :desc)) do %>
Item title <i class="fa fa-sort"></i>
<% end %>
</th>
So I was thinking how I could implement a helper method to return these links with the proper icons, links with the params kept and as HTML.
My start point is like this:
def sortable(path, column, title = nil)
title ||= column.titleize
css_class = column == sort_column ? "current #{sort_direction}" : nil
direction = column == sort_column && sort_direction == "asc" ? "desc" : "asc"
link_to title, {sort: column, direction: direction}, {class: css_class}
end
That I took from https://www.youtube.com/watch?v=cWYiAVMSHD4&t=460s
I wanted to write a cleaner code than this.
Thanks.
I would make some helper methods for this view to clean things up a lot.
def link_to_dashboard(text, extra_params)
link_params = params.permit(:layout, :page, :items_per_page).merge(extra_params)
link_to dashboard_active_path(link_params) do
%Q|#{text} <i class="fa fa-sort"></i>|
end
end
Then you can use it like:
<%= link_to_dashboard('Item Title', active_title_sort: :desc) %>
You could also use a loop to create the ths.
<tr>
<%
[
['Item title', {active_title_sort: :desc}],
['Current Price', {active_current_price_sort: :desc}],
['Time Left', {active_time_left_sort: :desc}],
].each do |label, sort_params|
%>
<th scope="col">
<%= link_to_dashboard(label, sort_params) %>
</th>
<% end %>
</tr>
A loop like that makes it much easier to add more sort column headers. You could even specify that array in your controller and use an instance variable. That would be cleanest. Put it into a constant at the top of the controller, and set an instance variable to the constant inside the action. This would also be efficient, as you'd reuse the same hash (as long as you don't mutate it).

Ruby on Rails single form to update multiple records using loop

I have a code that loop through each bidder and tries to create an input box for the owner of the assignment to update the status of the particular bid by each particular bidder.
<div id="titlebar" class="single submit-page">
<div class="container">
<div class="col-lg-10">
<h2><i class="fa fa-bell"></i> Bidders</h2>
</div>
<!-- <div class="col-lg-2">
<br>
<button type="button" class="btn btn-primary btn-lg">
<i class="fa fa-list-alt"></i> Find Assignments
</button>
</div>
-->
</div>
</div>
<div class="container">
<!-- Table -->
<div class="col-lg-12">
<p class="margin-bottom-25"> Bids can be viewed or removed below.</p>
<table class="manage-table resumes responsive-table">
<tr>
<th><i class="fa fa-genderless"></i> Gender</th>
<th><i class="fa fa-clock-o"></i> Experience</th>
<th><i class="fa fa-graduation-cap"></i> Education Level</th>
<th><i class="fa fa-money"></i> Expected Salary</th>
<th><i class="fa fa-file-text"></i> Status</th>
<th></th>
</tr>
<!-- Item #1 -->
<tbody>
<% #bidders.each_with_index do |bidder, i| %>
<tr>
<% if bidder.gender == 1 %>
<td>Male</td>
<% else %>
<td>Female</td>
<% end %>
<td><%= bidder.experience %></td>
<td><%= bidder.education.education %></td>
<td><%= bidder.expected_salary %></td>
<td><%= bidder.bid.status %></td>
<%= form_for(#bids[i]) do |f| %>
<td><%= f.label :status %> and <%= f.text_field :status %></td>
<% end %>
<td class="action">
<i class="fa fa-eye-slash"></i> Hide
<i class="fa fa-remove"></i> Delete
</td>
</tr>
<!-- Item #1 -->
<% end %>
</tbody>
</table>
</div>
</div>
application_controller.rb:
def bidders
bidders_ids = Bid.where(bidders_params).pluck(:user_id)
#bidders = User.where(id: bidders_ids)
#bids = Bid.where(bidders_params)
end
bids_controller.rb:
def bid_params
params.require(:bid).permit(:status, :assignment_id, :user_id)
#params.permit(:status, :assignment_id, :user_id)
end
The problem is that the loop creates multiple forms.
I want only one form but multiple Dropdown Select option 1..x depending on the total number of bidders on this particular assignment for ranking purposes.
I want to ensure that once I click on the submit button (which I haven't include), all the multiple :status fields to be updated in the database in their respective records.
Something like this?
x-editable will create a form with remote-true for each attributes do you want to change

adding labels/categories to looped micropost

I am following Hartl's screencast and we are iterating through microposts and showing them in the view:
<div class="span8">
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
However I want to label each micropost (Micropost 1, Micropost 2, etc...). How can I do that?
<%= render #microposts %> renders the partial app/views/microposts/_micropost.html.erb. So it should just be a case of editing that file.
In the tutorial, I believe the micropost.html.erb file looks like:
<tr>
<td class="micropost">
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</td>
</tr>
To add a label, you could do something like:
<tr>
<td class="micropost">
<span class="label"><%= "Micropost #{micropost.id}" %><span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</td>
</tr>
This will add the text 'Micropost 1', 'Micropost 2' ect above each of the microposts content
Edit
If you don't want to use ID and just want sequential numbering then you can change the code to iterate over each micropost and render them individually, passing in the count:
<% #microposts.all.each_with_index do |micropost, index| %>
<%= render micropost, locals: {index: index} %>
<% end %>
Then in your partial you can use the local variable index:
<tr>
<td class="micropost">
<span class="label"><%= "Micropost #{index}" %><span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</td>
</tr>

Rails - WillPaginate Conflict

I'm trying to paginate the messages a group has.
class GroupsController < ApplicationController
...
def show
...
#message = #group.messages.paginate(:page => params[:page])
end
<% unless #group.messages.empty? %>
<table class="messages" summary="Messages from Group">
<tr>
<td class="messages">
<span class="content"><%= messages.content%></span>
<span class="timestamp">
Posted <%= time_ago_in_words(messages.created_at) %> ago.
</span>
</td>
</tr>
</table>
<%= will_paginate #messagess%>
<%end %>
But when I try to open my group#show page, gives me the error: undefined method 'content' for #<WillPaginate::Collection:0x31a67f0>, but the line the error gives is the line where I create a new Message, that is in the group#show page as well:
<%= f.text_area :content, :rows=>5 , :cols=>49 %>
Any ideas?
You need to loop over the messages and print each one out individually.
Controller:
def show
#messages = #group.messages.paginate(:page => params[:page])
end
View:
<% unless #messages.empty? %>
<table class="messages" summary="Messages from Group">
<% #messages.each do |message| %>
<tr>
<td class="messages">
<span class="content"><%= message.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(message.created_at) %> ago.
</span>
</td>
</tr>
<% end %>
</table>
<%= will_paginate #messagess%>
<% end %>
To prevent the error on your form you will need to pass it a single instance of a message. e.g.
form_for(Message.new) do

Resources