activeadmin, has_many, and ckeditor - ruby-on-rails

I'm putting up the same question I asked here in activeadmin's issues board on github:
https://github.com/gregbell/active_admin/issues/645
Hi,
I have two different issues.
1: i love the way active admin handles has_many relationships with a simple DSL like so:
ActiveAdmin.register Artist do
form do |f|
f.inputs do
f.input :name
f.input :description
end
f.inputs "ArtistLinks" do
f.has_many :artist_links do |j|
j.inputs :title, :url
end
end
f.buttons
end
end
The ability to add more links at the bottom of the form is great.
However,I have been using a wyiswyg which i can't seem to get working in this format. I've been using/adding it with a partial like so:
ActiveAdmin.register NewsItem do
form :partial => "/news_items/form"
end
/app/views/news_item/_form.html.erb
<%= javascript_include_tag "/javascripts/ckeditor/ckeditor.js" %>
<%= semantic_form_for [:admin, #news_item], :multipart => true do |f| %>
<%= f.inputs :title, :photo, :excerpt %>
<%= cktext_area_tag("news_item[content]", #news_item.content) %>
<%= f.submit %>
<% end %>
However,
in my partial, i can't seem to be able to make the has_many relationship nicely like so:
f.inputs "ArtistLinks" do
f.has_many :artist_links do |j|
j.inputs :title, :url
end
end
Could you either explain to me how to get my wysiwyg which uses a form helper cktext_area_tag into my admin resource or explain to me how to get that nice has_many into my view partial?
Thanks a bunch!

The reason why has_many does not work in partials is because Active Admin tells you to use semantic_form_for when writing your partial. Active Admin extends Formtastic which it uses to generate forms. It does so by creating its own form builder that extends the Formtastic builder and adds, among others, the has_many method. So if you want to use that inside partials you have to use the Active Admin form builder. To do that use active_admin_form_for instead of semantic_form_for.
If you have problems using active_admin_form_for, take a look at my branch which should fix most of the issues (it's still beta - but I'm working on getting it into Active Admin core)

Related

How to display only unchecked items in active admin for has many relationship?

I am using Ruby on Rails 5 with active admin as a backend for resources management. I need to show the only unchecked items for a check_boxes field all the time in new and edit action. Instead of running a complex query for the collection I think this is the best way to manage. All of my associated models stuff related to this are working fine.
It should show only 2nd item if it is not checked already.
Right now my code snippet is
f.input :subscribers, :as => :check_boxes, :collection => Subscriber.all.collect {|subscriber| [subscriber.email, subscriber.id]}
Is there any way in active admin to display only unchecked values ?
Have you considered using collection_check_boxes for this case?
It would look something like this:
<%= f.collection_check_boxes(:subscribers_ids, Subscriber.all, :id, :email) do |b| %>
<% if !b.check_box.include?(checked="checked") %>
<%= b.label %>
<%= b.check_box %>
<% end %>
<% end %>
I think that should solve your problem. If you want to learn more about collection_check_boxes

rails simple_form fields not related to the model

I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.
But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.
If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.
In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:
<%= simple_form_for #user do |f| %>
<%= f.input :agreement, as: :fake %>
....
Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.
UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
end
end
You can use attr_accessor
class Order < ActiveRecord::Base
attr_accessor :card_number
end
Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.
See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor
and here for a useful stackoverflow question What is attr_accessor in Ruby?
Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible
The best way to handle this is to use simple_fields_for like so:
<%= simple_form_for #user do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= simple_fields_for :other do |o| %>
<%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
<% end %>
<% end %>
In this example, I have added a new field called change_password which is not part of the underlying user model.
The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by #baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.
Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.
I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...
Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Example:
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
<%= devise_error_messages! %>
<% resource.class.invite_key_fields.each do |field| -%>
<%= f.input field %>
<%= hidden_field_tag :object_name, #object.class.name %>
<%= hidden_field_tag :object_id, #object.id %>
<% end -%>
I found a very simple (and somewhat strange) workaround.
Just add the input_html option with any value key inside. E.g:
= simple_form_for #user do |f|
= f.input :whatever, input_html: {value: ''}
Tested simple_from versions: 3.2.1, 3.5.1

Add star ratings to my model rails3

I have a model called teacher that I'd like to add ratings to (5 star). Currently, I implement this by adding a ratings nested route (resource rating) inside of my teacher resource. Then I created a model: rating with (id, user_id, teacher_id, ratings, ...). Then I created a form with hidden fields, one of which is called stars. When a user clicks on a star, I use jQuery to send an AJAX request to create/update the rating for that user and teacher.
My confusion is this: I'm having two separate forms on the page. I have a form for writing the reviewers comments. This form has two fields: title, comments (and submit). Then I have the ratings form with hidden fields. Is this the right way to go about something like this? It seems to me that I should really have the ratings model fields somehow embedded in the main review form.
Any help highly appreciated. Thank you.
[EDIT]
I've updated my application so that instead of rating a teacher object, users now rate a comment on a teacher
my setup is something like this:
routes
resources :comments as :teacher_comments do
resource :rating
end
models
comment
has_one :rating
attr_accessible :body, :rating_attributes
accepts_nested_attributes_for :rating
rating
belongs_to :comment
attr_accessible :stars, :user_id, :teacher_id, :comment_id
view
<%= form_for( #comment, :remote => true, :url => teacher_comments_path ) do |tc| %>
<%= tc.text_area :body, :maxlength => 450 %>
<%= tc.fields_for :rating do |builder| %>
<%= builder.text_field :stars %>
<% end %>
<% end %>
I don't see the text_field for the stars. It's just not showing up. Is there something I missed?
Indeed, it's generally better to have all these fields in a single form (good for user experience).
Edit:
You might use the method accepts_nested_attributes_for (as you suggested in the comments below). Put the following in your parent Model (teacher); then you should be able to create a single form to handle inputs for both Models:
in the model:
class Comment < ActiveRecord::Base
has_one :rating
accepts_nested_attributes_for :rating
end
in the controller:
def new
#comment = Comment.new
#comment.rating = Rating.new
end
Ryan Bates gives a detailed screencast on the use of these concepts here: Nested Model Form. I recommend it for users who want to know more ins and outs.
Original:
This means that you'll need to point the form to an action that can handle both types of input. You can still use form_for if you like, but specify an action other than your default (or change the code within the default action in your teacher_controller.rb file):
<%= form_for #teacher, :url => {:action => 'create_and_rate'} do |f| %>
Since rating is a Model distinct from teacher (whose form we just created), you'll want to use the generic _tag form helpers for the rating fields.
<%= text_field_tag :rating, :name %> # rating's fields should use the generic form helper
<%= f.text_field :name %> # teacher's fields can use the specific form helper
Since you are pointing to a non-RESTful action, add it to your routes file.
resources :teacher do
:collection do
post 'create_and_rate' # this will match /teachers/create_and_rate to TeachersController#create_and_rate
end
end

Submitting multiple instances of a class using Rails forms

I have a bit of an interesting predicament. I have an :ingredients field which I'm using to get user-input for an ingredient. Each ingredient belongs to a recipe, which is the form_for (the :ingredients is the field_for). However, the user should be able to enter multiple ingredients, an unlimited amount. Right now I've implemented functionality for adding 1 ingredient, as shown here:
<%= form_for(:recipe, :url => {:action => 'create'}) do |f| %>
<%= fields_for :ingredient do |i| %>
<%= i.text_field(:quantity, :size => '6', :maxlength => '6') %>
<%= i.text_field(:units, :size => '20', :maxlength => '20') %>
<% end %>
<% end %>
To simplify things, I'm going to scratch the unlimited requirement, which I will do later with AJAX, and assume the user only puts in a max of 20. I'm confused as to how to design my form_for to do that, and to access each of those 20 elements in my controller. Would it be something like params[:recipe][1...20]?
I read this question:
How can I create multiple instances of an assosiated model from a rails 3 form_for when I don't know how many I'm going to add?
but I'm still stuck and I would like to avoid using Gems if possible (silly constraint I know).
Have a look at nested_form gem inorder to add multiple ingredients. Also look at
class Recipe < ActiveRecord::Base
accepts_nested_attributes_for :ingredients
end
inside recipe controller new action..
def new
#recipe = Recipe.new
#recipe = #recipe.ingredients.build
end
inside view
<%= nested_form_for(#recipe, :url => {:action => 'create'}) do |f| %>

virtual model and form_for (or formtastic)

Sometimes we need form without model creation - for example search field or email, where should be send some instructions. What is the best way to create this forms? Can i create virtual model or something like this? I'd like to use formtastic, but not form_tag.
Firstly, Formtastic doesn't need a model in all cases, although it certainly works best and requires less code with a model.
Just like Rails' own built-in form_for, you can pass in a symbol instead of an object as the first argument, and Formtastic will build the form and post the params based on the symbol. Eg:
<% semantic_form_for(:session) do |f| %>
...
<% end %>
This will make the form values available to your controller as params[:session].
Secondly, a model doesn't mean an ActiveRecord model. What I mean is, Formtastic will work with any instance of a class that quacks like an ActiveRecord model.
A classic example of this that many people are using Authlogic for authentication with Formtastic. Part of Authlogic is the idea of a UserSession model, which works fine:
Controller:
def index
#user_session = UserSession.new
end
Form:
<% semantic_form_for(#user_session) do |f| %>
<%= f.input :login %>
<%= f.input :password %>
<% end %>
This will make your form data available in your controller as params[:user_session].
It's really not that hard to create a model instance to wrap up the concerns of your model. Just keep implementing the methods Formtastic is expecting until you get it working!
default_language.rb
class DefaultLanguage
attr_accessor :language_id
end
foo_controller.rb
def index
#default_language = params[:default_language] || Language.find_by_name("English")
end
index.erb
<% semantic_form_for #default_language do |form| %>
<% form.inputs :id => 'default_language' do %>
<%= form.input :id,
:as => :select,
:collection => #languages,
:required => false,
:label => "Primary Language:",
:include_blank => false %>
<% end %>
<% end %>
I used AJAX to post the form when the value changed.
Or you simply create a form with form_for and leave the model reference blank.
for example
<% form_for "", :url=>some_url do |f| %>
<%= f.text_field "some_attribute" %>
<%= submit_tag "submit" %>
You can fetch the values by simply saying params[:some_attribute] in your controller.

Resources