toggle between "active" and "inactive" records in an index view - ruby-on-rails

I am extremely "green" when t comes to Ruby & Rails, I could do this easily in VB, but I want to learn rails....
I have a simple index view which shows all the clients in my database, and I figured out how to replace the index action in the controller to display only the clients who are marked as "active" (a field in the clients table), but my head is swimming trying to figure out routing and/or control actions for switching between the two or three recordsets (in VB terms).
My ultimate goal would be to have radio buttons on the index view where the user chooses between "active", "inactive" or "all".
Currently I have this in the clients controller...
#clients = Client.find(:all, :conditions => { :active => true })
##clients = Client.find(:all, :conditions => { :active => false })
##clients = Client.all.order(sort_column + ' ' + sort_direction)
If I comment out two of the lines, the remaining one does exactly as I want.
4 specific questions: (1) How do I write a conditional statement to make this switching occur, (2) WHERE should this be implemented, controller? Routing? Elsewhere?, (3) can this be implemented with user selectable radio buttons in the index view?, and (4) how do I add my "order" condition back in. (I tried just daisy-chaining it onto the end, but that doesn't work.)
Thanks in advance,
MDS

You've got a couple of strategies.
1) Load all the records onto the page, give them a class based on their active status, and hide some of them. The radio buttons have javascript attached, which shows or hides records with a particular class.
2) Load one set of records, and make the radio buttons trigger a request to the server to reload the contents of the list, passing through different params, eg "active=true" or "active=false" (when params[:active] isn't present you could load all of them). It's nicer if this is done via ajax but you could have it reload the whole page.
I wouldn't do this via routing since it's just a params thing.

You can have a dropdown in your index view that has all 3 statuses (radio buttons probably aren't that different but I don't have an example right now):
<%= form_tag("/clients", method: "get") do %>
<%= select_tag(:active, options_for_select([['Active', true], ['Inactive', false], ['All', '']], params[:active]), :prompt => 'Status') %>
<%= submit_tag("Search") %>
<% end %>
(Assuming your controller index page is at /clients, if not change the value in the form_tag)
You then add a scope to your Client model that takes an argument, this way you're not cluttering up your controller:
scope :active, lambda { |active| where(:active => active)}
In your controller, you then call the scope with the param value if it's set:
#clients = Client.all.order(sort_column + ' ' + sort_direction)
#clients = #clients.active(params[:active]) unless params[:active].blank?
No need to mess with routing since you're just passing a params value.

Related

How to add a search parameter to an existing Ransack query

I have a search form on a page, that allows a user to filter a list on multiple conditions. Now I'd like to add quick links on top of the list to apply scopes. I tried it like this:
= link_to "Visited", q: {"activated" => true}
While this filters the list to show only activated items, it also resets the search query. In other words, it doesn't remember what was already filtered in the form.
Is there a way to adapt #q so that I can add this "activated" => true to the hash of required filters?
Assuming you're only using the :q param to filter, you could aggregate that.
= link_to "Visited", q: (params[:q] || {}).merge(activated: true)
I don't think you can because if you follow a link you are not submitting the form therefore the parameters are not going to be submitted.
Passing the params in your link to will send the params if any exist:
= link_to "Visited", q: {"activated" => true, "your_params" => params}
This will only work if the form has been submitted once though, otherwise the params would be empty.
EDIT
I assume that the fields on your forms are populating if there is a value.
For example,
<%= text_field_tag(:email, if !params["email"].nil? then params["ip_email"] end) %>

How get the value of a collection_select within the same html.erb form itself

I have a form with this collection_select
<%= collection_select :bmp, :bmpsublist_id,
Bmpsublist.where(:bmplist_id => #bmp.bmp_id), :id,
:name,{ :required => false,
:selected => #bmp.bmpsublist_id, } %>
I would like to be able to get the value of this collection_select so that lower down in the same form, I can check to see which list I should use when displaying another collection_select
Something like this partial pseudocode here:
if earlier result == 2 then
use this list: Irrigation.where(:id != 8)
else
use this other list: Irrigation.all
and they would be updating the collection_select:
<%= collection_select :bmp, :irrigation_id, the_chosen_list_from_above, :id, :name,
{:prompt => 'Select Irrigation Type'}, {:required => true} %>
How can I do that?
Based on what you've asked, there are two ways to query and apply the value of the collection: static and dynamic.
Static occurs at the time that the ERB view is rendered, and this will apply the logic at the time that the page is initially rendered and loaded. Dynamic occurs after the page is loaded, and as the user interacts with the elements on the page. Which approach you choose to go with depends entirely on your application's design and intended level of interaction with the user.
Static Detection
You're already specifying the selected item in the initial collection_select, so you can reuse that in your later code. Try this, based on your pseudocode example:
<% if #bmp.bmpsublist_id == 2 %>
<% irrigation_list = ["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking"] %>
<% else %>
<% irrigation_list = ["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking", "Pads and Pipes - Tailwater Irrigation"] %>
<% end %>
<%= select :bmp, :irrigation_id, options_for_select(irrigation_list),
{ :prompt => 'Select Irrigation Type'}, { :required => true } %>
Why will this work? The :selected option for the initial collection_select is where you provide which option will be initially chosen. Since this value is typically taken from the model value, it's supplied in a separate param from the actual collection values. So, it's queued up and ready for you, simply by virtue of sticking to the Rails conventions.
The subsequent select builds the HTML <select> element and uses the options_for_select to turn the array of options into HTML <option> elements. This way, you can use the variable list of options to select from, based on which element from the original collection_select was chosen.
Best thing of all: with the static approach, you don't have to drop into Javascript (or jQuery) to do this; it gets rendered directly by the ERB template (or the HAML template, if that's your bag).
Dynamic Detection
If you actually wanted dynamic behavior, you can drop into Javascript / jQuery and get it done. You can create your "Irrigation Types" select just like with the static approach (above), except that you initialize it with all of the options, like this:
<%= select :bmp, :irrigation_id,
options_for_select(["Sprinkle", "Furrow/Flood", "Drip", "Furrow Diking", "Pads and Pipes - Tailwater Irrigation"]),
{ :prompt => 'Select Irrigation Type'}, { :required => true } %>
Then, edit the Javascript source associated with your view (let's call it Product). Open the app/assets/javascripts/product.js (if you use CoffeeScript, it's the product.coffee file in the same directory).
Edit that Javascript file to include this code:
function OnProductEditForm() {
// Edit the selectors to match the actual generated "id" for the collections
var bmp_collection = $("#product_bmp");
var drip_collection = $("#product_irrigation_type");
var drip_option = drip_collection.find("option")[2];
function select_available_drip_options() {
var value = bmp_collection.val();
if (value == 2) {
drip_option.attr("disabled", "disabled");
} else {
drip_option.removeAttr("disabled");
}
}
bmp_collection.change(function() {
select_available_drip_options();
});
select_available_drip_options();
}
This identifies the HTML element of the collection and installs a change event handler. You'll need to verify the collection element's id, as per the code comment, and the rest happens from there. When the collection is changed (a new value is chosen), the event handler will hide or show the third select <option> (specified as find("option")[2]), as appropriate for the #product_bmp selection.
Next, in the app/views/products/_form.html.erb, include this at the end of the file:
<script>
jQuery(document).ready(OnProductEditForm);
// Uncomment the next 2 lines for TurboLinks page refreshing
//jQuery(document).on('page:load', OnProductEditForm);
//jQuery(document).on('page:restore', OnProductEditForm);
</script>
This will automatically load the OnProductEditForm method when the page loads, and will result in the afore-mentioned event handler getting installed. Note that the last 2 lines are necessary if you have TurboLinks enabled, as TurboLinks initiates events for page loading independently of the standard $(document).ready.
And that's all there is to it. Adding dynamic behavior is just that easy!
You're gonna have to use some javascript (jquery and ajax, I would suggest). When the value of the first select change (jquery), it requests (ajax) the collection (passing the current value selected) to a controller action that returns the collection that should be used. With the collection returned, you populate the options (jquery) for the second select. It's not quite simple, but if you ever did something like that, you shouldn't have problems. If never did, do some research about it... it's quite useful and improve user experience a lot!

Ruby on Rails Joining Two Strings in Dropdown Text

I have a ruby on rails site with a page called "meetings". These meetings currently work fine, but I want to change the way that one of the drop downs displays its data. Currently, I have this in my controller/view:
Controller
#meeting_times = MeetingTime.select("id, meeting_type, meeting_at").where("
(meeting_at > ?)", Time.now + 1.week).order("meeting_at")
View
<%= f.select :meeting_time_id, options_from_collection_for_select
(#meeting_times, "id", "meeting_at") %>
Currently, as you can see, the dropdown just shows the meeting time. What I want to do make the dropdown show the meeting_type + meeting_time in one string. This sounds easy, however meeting_type is an int value. So, I would need to say "If meeting_type == 0 then display 'Staff Meeting' + meeting_time", and so on. How can I accomplish this without changing the database values for meeting_type?
in MeetingTime model create method
def meeting_time_display
"#{meeting_type_names[meeting_type]} #{meeting_at}"
end
private
def meeting_type_names
##m_type_names ||= ['Staff Meeting', 'Type 2', ..., 'Type n']
end
In view call
<%= f.select :meeting_time_id, options_from_collection_for_select (#meeting_times, "id", "meeting_time_display") %>

simple_form select collection populated by AJAX

I thought this would be fairly easy, but I'm not finding any help by Googling.
I have a form (simple_form) with numerous inputs with select lists (collections) that are populated from the database, so many it is slowing down the initial page load. I thought I could speed it up by only populating those drop down lists as the user selects them using Ajax. Is there something built in like remote => true for the form itself? Can someone point me in the right direction?
EDIT:
I found this SO question but I cannot figure out how to implement the answer.
Currently, my form looks like this;
= simple_form_for(#account)
= f.input :account_number
= f.input :area, collection: #areas
= f.submit nil, :class => 'btn btn-primary'
Based on the answer in the linked question, I should add something like this, but of course it is not working
= simple_form_for(#account)
= f.input :account_number
= f.input :area, collection: #areas, :input_html => {"data-remote" => true, "data-url" => "/my_areas", "data-type" => :json}
= f.submit nil, :class => 'btn btn-primary'
I can think of two ways to go about this if you don't want to load the contents initially when the page loads. One way is to run a script after the DOM has loaded to change the options for the select tag and the other is to collect the options when you click on the drop-down on the select element. I might go for the first way because there wouldn't be latency when a user clicks on the select element--they wouldn't have to wait for the options to populate.
So you'd run a jQuery script on document ready that makes an AJAX call to a method in your controller, which then returns the collections you want, then you iterate through the select elements you want to change with JQuery scripts. It might look something like this.
# in view with the select options to be changed
$(document).ready(function() {
$.get(change_selects_path, function(response) {
$.each(response, function(args) {
// code for each select element to be changed
$('.class_of_select_element').html(<%= j options_from_collection_for_select(args) %>);
});
});
)};
# in controller
def change_selects
# make db calls and store in variables to feed to $.get request
end
Note that this not tested but should give you a good start towards a solution. For further info on the each loop, you can check out this documentation.
Not sure if this fits your exact use case (please clarify if not), but I also have a few collection selects that have a large amount of database rows behind them. I use the select2-rails gem to take care of this. Users can begin to type in the name and the relevant results will show up (it will also show a few initially if they don't type something).
Check it out here: https://github.com/argerim/select2-rails
Edit: For a cascading dropdown, I recommend this gem: https://github.com/ryanb/nested_form

Rails: how to get value from another field when executing an onchange remote function in textfield

I want to create a form for making a reservation for borrowed items. A reservation consists of pick up time, return time and items the reserver wants to borrow and their amounts. For example, I might want to reserve three plates, three knives and three forks for the rest of the week.
In the form, I want to do an AJAX validation that checks whether there is enough items available. Next to each item I have a text box with which the reserver inputs the item amount. After the input, I want to do an onchange call that checks if the amount of items is available for the given dates. Thus, I need to pass the remote function called in the onchange following parameters: item id, item amount (value of the current textfield) and pick up time and return time which are both given in datetime_select fields above. This is my code:
<% with = "'amount='+value+'&item=#{item.id.to_s}&pick_up_time=#{#reservation.pick_up_time.to_s}&return_time=#{#reservation.return_time.to_s}'" %>
<%= text_field_tag "reservation[#{prefix}reserved_items][#{item.id}]", get_amount_value(item, #reservation), :size => 3, :onchange => "#{remote_function(:url => { :controller => :items, :action => :availability }, :with => with, :update => availability_url) }" %>
Obviously, this does not work since #reservation.return_time and #reservation.pick_up_time are not yet set because the form is not yet sent. My question is: how do I get those values? I believe that it should be done via a javascript call, but I didn't manage to insert a javascript call in the "with" variable or at least didn't get it to work. Does anybody have any idea on what I should do?
use prototype selectors $(#reservations_pick_up_time).value
the :with attribute is just a place for you to write JS code that it will display inline

Resources