I'm new to ActiveAdmin and I have this problem:
I have two models: Picture and Content. Picture has many contents through polymorphic association.
I succeeded to make the button "Create content" from Picture show page pass contentable_type and picture id as contentable in the new_admin_content request as parameters using the following in app/admin/picture.rb :
action_item :new, only: :show do
link_to "Add content", new_admin_content_path(contentable_type: "Picture", contentable: picture)
end
and to receive the parameters I wrote the following in app/admin/content.rb :
permit_params :list, :of, :attributes, :on, :model, :contentable, :contentable_type
form do |f|
f.object.contentable_type = params[:contentable_type]
f.object.contentable = params[:contentabl]
puts f.object.contentable_type
puts f.object.contentable
f.inputs "Details" do
.
.
.
end
actions
end
But in this case, I get this error:
NoMethodError in Admin::Contents#new
undefined method `primary_key' for String:Class
The error is triggered by line f.object.contentable = params[:contentable]
When I pass only contentable_type to f.object whithout passing contentable, f.object saves contentable_type in its field, but submitting the form doesn't create a new record.
How can I save contentable as in its form field and succeed to make a create action on form submit?
The problem was solved by changing f.object.contentable = params[:contentabl] to f.object.contentable_id = params[:contentabl] (like the column name in the database) and adding all form parameters to permitted params
Related
I have an ActiveAdmin form to create a new ActiveRecord object, and the form is capable of also creating records for one of the "main" model's has_many associations, i.e.:
class Car < ApplicationModel
has_many :tires
end
class Tire < ApplicationModel
belongs_to :car
end
# ... in ActiveAdmin "cars.rb" file...
ActiveAdmin.register Car do
...
form do |f|
f.semantic_errors
f.inputs 'Car' do
f.input :color
f.input :doors, as: :number
f.inputs do
f.has_many :tires, allow_destroy: true, new_record: true do |t|
t.input :type
t.input :size
end
end
end
f.actions
end
end
There is also some controller logic not listed here that properly creates, edits, or destroys these records, including Cars and Tires. When the form renders (let's say I'm creating a new Car), there is a button at the bottom that says "Add New Tire". Clicking it renders a sub-form on the page with two fields: "Type" and "Size". I've set up the controllers to save both the Car and its associated Tires when I submit the form. That all works.
I'm struggling with an automated test to ensure that clicking the "Add New Tire" button causes the sub-form to render. I've added a breakpoint after clicking the button and inspected page.body to see if the sub-form exists, but it doesn't seem to be there - I only see what looks like a template of the sub-form in the button's (really an a element) code. This is the part of my test code that fails:
click_link 'New Car'
fill_in('Color', with: 'Blue')
fill_in('Doors', with: 2)
click_link('Add New Tire') # Succeeds
fill_in('Type', with: 'All-Terrain') # Fails, can't find element
I've tried other ways to match on the first sub-form field, like xpath and css selectors, but it just can't find the field, and like I mentioned above, I don't see an instance of the sub-form being rendered if I inspect the page with page.body. I'm stuck - can anyone help me figure out what's going on here?
I got a tip that adding js: true to the test definition could help, and as it turns out, it did:
RSpec.describe Car, js: true do
...
end
I have a permit and vehicle model. I am trying to update the AA create controller to work how I have it in my rails app. That is taking the vehicle license_number entered and inputting it into the permit table, then also taking the inputted permit_id and inputting it into the permits attribute of the vehicle it is related to in the vehicle table.
admin/permit.rb
permit_params :permit_id, :vehicle, :date_issued, :issued_by, :date_entered, :entered_by
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, :collection => Vehicle.all.map{ |vehicle| [vehicle.license_number]}
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
controller do
def new
#permit = Permit.new
#vehicle = #permit.build_vehicle
#vehicle = Vehicle.all
super
end
def create
vehicle = Vehicle.find_by(license_number: permit_params[:vehicle_attributes][:license_number])
#permit = current_user.permit.build(permit_params.merge(date_entered: Date.today,
entered_by: current_user_admin.email))
super
end
end
My errors that I am getting, is that it is inputting the license_number in for the permit_id and then it is also saying the permit_params is not a defined variable. Any help would be great, thanks!
You have an interesting case here: it is confusing because you have a model called Permit, and usually in Rails you name the params method something like permit_params. Turns out, permit_params is the general method signature for ActiveRecord to implement strong params: https://activeadmin.info/2-resource-customization.html
With that, instead of calling permit_params in your create action, you need to call permitted_params[:vehicle_attributes][:license_number]. That’s why it’s considering permit_params as an undefined variable. Again, those two method signatures will be the same for all your ActiveAdmin forms.
Additionally, I’m not sure if this is a typo but you define #vehicle twice in your new method. I’m not sure you need to build a vehicle for the permit form unless you’re doing nested forms. Either way, I think the last line should read #vehicles = Vehicle.all Then you can use that in your form, which also could use an update in the collection part of your form field:
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, collection: #vehicles.map { |vehicle| [vehicle.license_number, vehicle.id] }
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
The collection_select form tag will take the first item in the array as the value that appears in the form, and the second value will be the value passed through in the params (https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select).
Then in your create action, you can find the Vehicle with the id:
Vehicle.find(permitted_params[:vehicle_attributes][:vehicle_id])
I would avoid Permit as a model name, try using VehiclePermit.
How can I set a textfield in an ActiveAdmin form, who do not correspond to a table attribute ?
I need it to build an autocomplete behavior, to populate a list of checkboxes.
In case you want the value submitted back to your model, you can create a virtual attribute by adding attr_accessible, or explicitly define them in the model, as suggested in this answer:
def my_virtual_attr= (attributes)
#this will be evaluated when you save the form
end
def my_virtual_attr
# the return of this method will be the default value of the field
end
and you will need to add it to permit_params in the ActiveModel resource file.
In case you don't need the value submitted to the backend (needed for front-end processing for example), you can actually add any custom HTML to ActiveAdmin form, and this is an example code it:
ActiveAdmin.register MyModel do
form do |f|
f.semantic_errors # shows errors on :base
f.inputs "My Custom HTML" do
f.li "<label class='label'>Label Name</label><a class='js-anchor' href='#{link}'>Link Text</a><span></span>".html_safe
f.li "<label class='label'>Label 2 Name</label><input id='auto_complete_input'/>".html_safe
end
f.inputs "Default Form Attributes" do
f.inputs # builds an input field for every attribute
end
f.actions # adds the 'Submit' and 'Cancel' buttons
end
end
You can try to remove model prefix from the params name
ActiveAdmin.register MyModel do
form do |f|
f.input :custom, input_html: { name: 'custom' } # instead of 'my_model[custom]'
end
end
I have two related models, Bunny has_many BunnyData (which belongs_to Bunny). From the show page of a particular Bunny (in Active Admin), I want to create a link to make a related BunnyData. I've tried a few different ways, with no success, and am currently trying this:
sidebar :data, :only => :show do
link_to 'New Data', new_admin_bunny_datum(:bunny_id => bunny.id)
end
The link being generated ends up as something like:
.../admin/bunny_data/new?bunny_id=5
But when you go to that page, the dropdown for Bunny is set to the blank default as opposed to showing the name of Bunny with ID 5.
Thanks in advance.
Rails namespaces form fields to the data model, in this case BunnyData. For the form to be pre-filled, any fields provided must also include the namespace. As an example:
ActiveAdmin.register Post do
form do |f|
f.inputs "Post Details" do
f.input :user
f.input :title
f.input :content
end
f.actions
end
end
The fields can be pre-filled by passing a hash to the path helper.
link_to 'New Post', new_admin_post_path(:post => { :user_id => user.id })
Which would generate the following path and set the form field.
/admin/posts/new?post[user_id]=5
In the case of BunnyData, it might be slightly different due to the singular and plural forms of datum. But that can be verified by inspecting the generated HTML to find the name attribute of the inputs.
I am having trouble adding the data from a nested model to my database.
I have a Photo model with the following relationship to a Monster model. I am able to add and remove my nested form for monsters, but can't figure out how to actually add a new Monster object when updating my Photo. This is based on 1 nested level form from the http://railscasts.com/episodes/196-nested-model-form-revised example, basically the Survey - Question relationship demonstrated.
class Photo < ActiveRecord::Base
attr_accessible :title, :monsters_attributes
has_and_belongs_to_many :monsters
accepts_nested_attributes_for :monsters, allow_destroy: true
end
class Monster < ActiveRecord::Base
attr_accessible :first_name, :last_name, :selected
has_and_belongs_to_many :photos
end
I'm sure the trouble is in my photos_controller and my poor understanding of how to use the params submitted when I update my photo form. I get the following params hash when submitting an edit photo form with two monsters. The first one is already associated and the second one I am trying to declare by clicking an "Add Monster" link, entering the first and last name, and submitting the form.
The "Add Monster" link is defined in my _form.html.erb file for a Photo and I am using a partial to render the fields. The helper function is declared in application_helper.rb.
_form.html.erb
…
<!-- this uses the _monster_fields partial -->
<%= f.fields_for :monsters do |builder| %>
<%= render 'monster_fields', f: builder %>
<% end %>
</table>
<%= link_to_add_fields "Add Monster", f, :monsters %>
...
application_helper.rb
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new # make instance of monster association record, Monster.new
id = new_object.object_id # get id of new monster object, id = Monster.new.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder) # render partial _monster_fields.html.erb
end
link_to(name, "#", class: "add_fields", data: {id: id, fields: fields.gsub("\n", '
')}) # returns a link
end
end
params shown from edit (update) of photo to show:
{
"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"8dYkFrzdkulBv8YrZCHU2wfFh4v5LQc9q/JWPmsbDkc=",
"photo"=>{"title"=>"Family Gathering",
"monsters_attributes"=>{
"0"=>{"first_name"=>"Franken", "last_name"=>"Stein", "_destroy"=>"false", "id"=>"1"},
"1358291763102"=>{"first_name"=>"Ware", "last_name"=>"Wolf", "_destroy"=>"false"}
},
"monster_ids"=>"1"
},
"commit"=>"Update Photo",
"action"=>"update",
"controller"=>"photos",
"id"=>"1"
}
In my applcation.js file I define how to handle the adding and removal of fields. In .add_fields, the example uses the id for my newly created object but I don't see how to save my newly created monster in the monsters table.
application.js
$(document).ready(function() {
$('form').on('click', '.remove_fields', function(event) {
$(this).prev('input[type=hidden]').val('1');
$(this).closest('tr').hide();
event.preventDefault();
});
$('form').on('click', '.add_fields', function(event) {
time = new Date().getTime();
regexp = new RegExp($(this).data('id'), 'g');
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault();
});
});
Overall, it's a pretty simple process but I'm not sure how to proceed further when it comes to actually saving a new Monster from my Photo edit view(creating a new active record object).
Could I have issues using a many-to-many relationship?
The example from railscasts is good but it doesn't quite go that far. I am grateful for any assistance someone may have.
http://railscasts.com/episodes/196-nested-model-form-revised
updated 1/16/13: My params hash and the error I get
ActiveModel::MassAssignmentSecurity::Error in PhotosController#update
Can't mass-assign protected attributes: monster_ids
app/controllers/photos_controller.rb:76:in block in update'
app/controllers/photos_controller.rb:75:inupdate'
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"1l62Sged52hnEHQoj0WPf/asRjcIpIKBBzsb7gVDRm0=",
"photo"=>{"title"=>"Family Gathering",
"monsters_attributes"=>{"0"=>{"first_name"=>"Franken",
"last_name"=>"Stein",
"_destroy"=>"false",
"id"=>"1"},
"1358356969634"=>{"first_name"=>"Scary",
"last_name"=>"Frank",
"_destroy"=>"false"}},
"monster_ids"=>"1"},
"commit"=>"Update Photo",
"id"=>"1"}
The beauty of the nested attributes stuff is that the implications for your controller code are non existent. You can keep doing
photo.update_attributes params[:photo]
and monsters will get updated and deleted as appropriate: photo.monsters_attributes= will be called by update_attributes with the data from params[:photo][:monsters_attributes]. Calling accepts_nested_attributes_for will have (among other things) created a suitable monsters_attributes= method