Rails:how to show name instead of ID or Address in has_many relation in active_admin? - ruby-on-rails

These days I'm using the active_admin to manage my data. I have a Audio model and Problem model. Audio has many problems and Problem belongs to audio.
I use the active_admin to create the problems. But in the problem's new page, there is a drop-down list shows the content like:
#<Audio:0xb4116084>
With the address I can hardly recognize which file I want. What I want to show in the Audio's drop-down list is the Audio's title which is a column of Audio model. I just want to change this column in the new page, and others remain the same as default. What should I do? Thanks!

Audio class must implement display_name method
Ex
class Audio
def display_name
title
end
end
this is from active admin sources
# Active Admin makes educated guesses when displaying objects, this is
# the list of methods it tries calling in order
setting :display_name_methods, [ :display_name,
:full_name,
:name,
:username,
:login,
:title,
:email,
:to_s ]
Looks like you haven't such methods so to_s is called for Audio objects

You can use a :member_label.
Here is a example
form :html => { :enctype => "multipart/form-data" } do |f|
f.input :problems,
:input_html => { :multiple => false, :style => "width: 700px;"},
:collection => Audio.all,
:member_label => :audio_name
end

Related

ActiveAdmin: display parameter of object selected in form

I am not very familiar with ActiveAdmin or rails in general, so sorry if I use some incorrect phrasing.
I have a model for an Athlete, which has an attribute "notes". This is described in the permit_params of the athlete.rb.
On an additional page, I have the following:
f.input :athlete, :collection => Athlete.all.sort_by(&:last_name), :required => true
I would like to find a way to, if the :notes is not empty, display it.
If I could display it as a "row" on the form, that would be great.
Thanks!

Limiting down the amount of calls to the database during a simple_form with multiple HABTM

I am currently trying to speed up an old (3.2) Rails app and hitting an issue with way too many calls being made to the database during an edit view using simple_form
Here is the simplified setup;
class Event
HABTM Speakers
HABTM Sponsors
class Speaker
belongs_to Sponsor
HABTM Events
class Sponsor
have_many Speakers
HABTM Events
For an event we want to show both each potential speaker (of which there are thousands) and all the sponsors (hundreds).
For each speaker if they have a sponsor they need to show the sponsor name.
This all adds up to a lot of calls to the sponsors table, way too many.
Current Code
Right now the simple_form is set up as;
<%= simple_form_for [:admin, #event] do |f| %>
...
<%= f.association :speakers, :input_html => { :class => 'select2able' }, :label_method => :summary %>
...
<%= f.association :sponsors, :input_html => { :class => 'select2able' } %>
...
That label method is on the speaker model;
delegate :name, to: :sponsor, prefix: true, allow_nil: true
def summary
[name, job_title, sponsor_name].compact.join ' - '
end
The #event variable is assigned in the controller with a simple
#event = Event.find params['id']
This is where I have been spending most of my time trying to improve.
What I have tried
Calling Event.includes(:speakers => :sponsor).find() I thought would help. but it hasn't at all.
I have also tried just including speaker and sponsor separately.
I have searched around for people having issues with simple_form specifically but haven't found anything. I know this is a sign of the mental structure that was made, but anything I can do to improve it without a complete re-write would be excellent.
I was almost there with my original attempts.
This version of Rails didn't like the external includes (it executed but didn't apply anything)
The final code was a slight change where I brought the include inside the find_by.
Event.find_by_slug(params[:id], :include => [:speakers, :sponsors, :speakers => :sponsor])

Adding a value-dependent data attribute to a simple_form checkbox collection

I'm generating a list of checkboxes for a single collection like so:
= f.input :parts, as:check_boxes, collection: #parts_list
I want some checkboxes in the collection to disappear/reappear depending on the value of a select widget higher up in the form. (e.g. choosing "Tracker Robot" from the Robot select means that the "Legs" part checkbox disappears and the "Wheels" checkbox appears, etc.)
What I'd like to do is attach a computed data attribute to each individual Part checkbox, with the attribute value listing the Robots that can use that Part; then some JS will do the work of hiding/showing the checkboxes. However, I don't know how I can generate those data attributes using simple_form.
I would normally create a custom "parts" input, but there seems to be a problem with making custom collection inputs; it looks for a named method (collection_parts) inside form_builder.rb, which won't exist, and if I try and extend the FormBuilder it sends me down a major rabbit hole.
I could write some JS to load the data attrs into the generated HTML, but then I have to generate custom JS based on my Rails data, and that feels like the wrong way to do it.
Let's assume that the form is for Order model and you are changing the parts collection based on the value of a field called region.
Update the form view. Specify the id for form, region field and parts field.
= simple_form_for(#order, :html => { :id => "order-form"}) do |f|
= f.input :region, :wrapper_html => { :id => "order-form-region", |
"data-parts-url" => parts_orders_path(:id => #order.id, :region => #order.region)} |
= f.input :parts, as: check_boxes, collection: #parts_list, |
:wrapper_html => { id' => 'parts-check-box-list'} |
Add a new action called parts in the route.rb file.
resources :orders do
collection do
get :parts
end
end
Add the new action to your controller
class OrdersController < ApplicationController
# expects id and region as parameters
def parts
#order = params[:id].present? ? Order.find(params[:id]) : Order.new
#parts_list = Part.where(:region => params[:region])
end
end
Add a helper
def parts_collection(order, parts_list)
"".tap do |pc|
# to generate the markup for collection we need a dummy form
simple_form_for(order) do |f|
pc << f.input(:parts, as: check_boxes, collection: parts_list,
:wrapper_html => {:id => 'parts-check-box-list'})
end
end
end
Add a js view for the action (orders/parts.js.erb)
$('#parts-check-box-list').replaceWith('<%= j(parts_collection(#order, #parts_list)) %>');
Register data change event handlers for region field in your application.js
$(document).ready(function() {
$('#order-form').on("change", "#order-form-region", function () {
// Access the data-parts-url set in the region field to submit JS request
$.getScript($(this).attr('data-parts-url'));
});
});
I think you can do it like this:
= f.input :parts do
= f.collection_check_boxes :parts, #parts_list, :id, :to_s, item_wrapper_tag: :label, item_wrapper_class: :checkbox do |b|
- b.check_box(data: { YOUR DATA ATTRIBUTES HERE }) + b.text
this may be simpler.
Assumptions
#robots - an array containing the list of robots
#parts - a hash containing a list of parts for each robot
Sample Code
# controller
#robots = %w[tracker nontracker]
#parts = { tracker: %w[wheels lcd resistor], nontracker: %w[lcd resistor] }
# view
= f.input :robots, as: :select, collection: #robots, input_html: { id: 'robot-select' }
#parts-list
:javascript
var parts = #{#parts.to_json};
$(document).ready(function() {
$('#robot-select').change(function() {
$('#parts-list').html('');
$(parts[$(this).val()]).each(function(index, text) {
$('#parts-list').append('<input type="checkbox" value=' + text + '>' + text + '</input>')
})
})
})
you can see this working if you clone https://github.com/jvnill/simple_form_search_app and go to /robots
Some input options in SimpleForm accept a lambda that gets called for every item in a collection:
f.input :role_ids, :collection => (1..10).to_a,
:label_method => :to_i, :value_method => :to_i,
:as => :check_boxes, :required=> true,
:disabled => ->(item){ item.even? }
but input_html doesn't seem to be one of them.
The solution is probably to create a custom SimpleForm collection input that applies the data attributes itself. Not as flexible perhaps, but I think this is the only way to go for now.
There's a tutorial page on GitHub that should get you started.

Manipulating tags with acts_as_taggable_on and ActiveAdmin

I have a Post model which I'm accessing through ActiveAdmin. It's also taggable using the acts_as_taggable_on gem. So the admin can add, edit or delete tags from a specific Post.
The normal way to add the tagging functionality for the resource in your admin panel is by doing this in admin/posts.rb:
ActiveAdmin.register Post do
form do |f|
f.inputs "Details", :multipart => true do
f.input :tag_list
# and the other irrelevant fields goes here
end
f.buttons
end
end
However, I want to have the tags selected from a multiple select form field and not being entered manually in a text field (like it is with the code above). So I've tried doing this:
f.input :tag_list, :as => :select,
:multiple => :true,
:collection => ActsAsTaggableOn::Tag.all
but it doesn't work as expected. This actually creates new tags with some integer values for names and assigns them to that Post. Someone told me that extra code is needed for this to work.
Any clues on how this is done? Here's my model just in case: http://pastie.org/3911123
Thanks in advance.
Instead of
:collection => ActsAsTaggableOn::Tag.all
try
:collection => ActsAsTaggableOn::Tag.pluck(:name)
Setting the collection to Tag.all is going to tag your posts with the tag's ID, since that's how tags are identified by default (that's where the integer values for names are coming from). map(&:name) tells the form builder to use the tag's name instead.

an example of a nested form in simple form?

I am still struggling both writing the controller and the actual form to be able to nest one form in another with an optional model?
I have Message which has many contacts
When submitting a message, I want to add a contact optionally.
I have this as an example:
= simple_form_for Message.new, :remote => true do |f|
#message_form
= f.error_messages
%p
= f.input :account_name, :url => autocomplete_account_name_messages_path, :size => 40, :as => :autocomplete
%p
= f.input :topic, :required => true,
:input_html => {:size => 30}
#add_contact_btn
= link_to "Add Contact"
#contact_form
= f.simple_fields_for :contactd do |fc|
= fc.input :email
= fc.input :first_name
= fc.input :last_name
= f.submit 'Give'
= f.submit 'Request'
For Message.rb model, I have the following:
has_many :contacts
accepts_nested_attributes_for :contacts, :reject_if =>:all_blank
Note: When I used :contacts in the simple_fields_for it didn't work, so it is singular. But the reverse for accepts_nested_attributess_for.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Here is what I see passed as form data from google chrome:
message%5Baccount_name%5D:McKesson
message%5Btopic%5D:testing a contact
message%5Bbody%5D:testing this
sender_id:
receiver_id:23
message%5Bcontacts%5D%5Bemail%5D:888#gmail.com
message%5Bcontacts%5D%5Bfirst_name%5D:Ang
message%5Bcontacts%5D%5Blast_name%5D:Name
The correct method name is simple_fields_for (notice the plural)
Also, you need to keep the f. to call it on the simple_form object
I have a small project where I demonstrate how to use nested forms in simple-form, combined with cocoon (a gem I created to add/remove nested elements dynamically).
The project is on github.
Hope this helps.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Make sure you put in your Message.rb model the ability for it to accept the attributes too.
class Message < ActiveRecord::Base
attr_accessible :contacts_attributes
has_many :contacts
accepts_nested_attributes_for :contacts
I know it doesn't answer your question fully but it may have just been this. When it comes to my project, it would return nil if i didn't include the :contacts_attributes, in my case it deals with products. Hope this helps even if I'm not using simple form as of now!
I faced similar issues working with nested forms. As suggested by JustinRoR you need to define
attr_accessible: contacts_attributes.
You should be able to test the hash in the ruby console ( I am not sure if you have tried this). I suggest you to print the params[:message] and use this to create message from console like Message.new(params[:message]). (Note params[:message] is what you get by printing the params[:message] hash).
Once it works in console it should work like charm

Resources