ActiveAdmin get value from form text field - ruby-on-rails

I'm new in Ruby and make some simple admin using ActiveAdmin.
I have a model Question which I want to create, fill and store to database, it has an attribute themes (array of Theme models). When user creates a new record he doesn't enter themes manually but provide some string and system will automatically parse it and find or create themes. So I have code like that:
form do |f|
f.inputs "Questions Details" do
f.input :question, as: :string
f.input :autocomplete_themes, hint: "You should enter here multiple themes,
divide them with `,` or `;`"
end
f.actions
end
It creates a new field autocomplete_themes for entering string and it doesnt' exist in model Question. So what I want - is to get autocomplete_themes value like string and then use split() and my custom logic - but it gives an error.
before_create do |question|
array = []
puts "******"
puts :autocomplete_themes.text
themeTitles = :autocomplete_themes.split(",") #split(/,|;/)
for title in themeTitles do
theme = Theme.find_by(title: title)
theme = Theme.create(title: title) unless theme
array << theme
end
question.themes = array
end
Question: how can I get autocomplete_themes value as string? Thx!
Update: as I understood here - it looks like the similiar case, but there was problem with setting default value to custom field, but I need to get its value from code.

You did not specify the error you were getting, but based on the information you gave, you don't need autocomplete_themes to be a real database-backed attribute of the Question model, but rather you only need the information temporarily so that your before_create filter can use it to execute the appropriate logic.
Therefore, you could make autocomplete_themes a "virtual attribute" that is like a traditional member variable of an instance of Question.
class Question < ActiveRecord::Base
attr_writter :autocomplete_themes
attr_reader :autocomplete_themes
...other code
end
This will allow you to do things like:
#question.autocomplete_themes = "1,2,3"
themes_text = #question.auto_complete_themes
And best of all, ActiveAdmin supports having form inputs assign to virtual attributes. So you can keep your form like this:
form do |f|
f.inputs "Questions Details" do
f.input :question, as: :string
f.input :autocomplete_themes, hint: "You should enter here multiple themes,
divide them with `,` or `;`"
end
f.actions
end
And your before_filter would look like this:
before_create do |question|
array = []
themeTitles = question.autocomplete_themes.split(",") #split(/,|;/)
for title in themeTitles do
theme = Theme.find_by(title: title)
theme = Theme.create(title: title) unless theme
array << theme
end
question.themes = array
end

Related

RailsAdmin enum display existing value of the record

I have defined a User model in which there is a classical role
class User
enum role:[:user, :poweruser, :admin, :superadmin]
I am now using RailsAdmin to manage the fields of the user in rails_admin.rb file.
At the beginning, I manage the role attribute directly like that :
edit do
field :email do
required true
end
field :password
field :password_confirmation
field :role
...
Everything was fine. The current value is displayed (here poweruser), I get a combo automatically from RailsAdmin, with the 4 listed value. Perfect !
Now, I want to manage dynamically the list of roles : I need to restrict the list of roles regarding the current_user logged.
Therefore I use the :enum feature of RailsAdmin like that :
field :role, :enum do
required true
enum do
if bindings[:view]._current_user.superadmin?
{user:0, poweruser:1,admin:2,superadmin:3}
elsif bindings[:view]._current_user.admin?
{user:0, poweruser:1,admin:2}
else
{user:0}
end
end
end
This is working, the list is correctly restricted according to the current user role logged.
However, I would like that the initial value of the role should be set correctly, this value is not displayed at all : instead of the initial value poweruser, the 'Search' word (Chercher in french) is displayed.
How could I solve this issue ?
Thanks for any help.
You need to define the block default_value
rails_admin do
edit do
field :role do
default_value do
:poweruser
end
end
end
end
Thanks Guillermo, but it does not answer to my problem.
The problem is that railsadmin is not able to select the enum field on edit. It does not concern the default value for new record.
Now, I have found that it is an open issue of railsadmin gem, and find this link :
Not selecting enum field on edit #2669 and got a workaround from Richard Venneman
I have to change code in file app/views/rails_admin/main/_form_enumeration.html.haml and override the existing code by :
= form.select field.method_name, field.enum, { include_blank: true }.reverse_merge({ selected: form.object.class.send(field.method_name.to_s.pluralize)[field.form_value] }), field.html_attributes.reverse_merge({ data: { enumeration: true }, placeholder: t('admin.misc.search') })
It is working fine now ! So this problem is closed for me, until an official version of railsadmin !

Associating a temporary (non-DB) model to a simple_form

I have a simple_form, that doesn't really apply to a normal model. If I have the object just set to :thing it seems to work.
However, I want it to have some validation. In other questions, I've found that this means that I NEED to have a model... I'm not sure what needs to be in that model, however. So, I made I model, but I can't figure out how to hook them up.
class ClientEmail
include ActiveModel::Validations
validate :cannot_be_present
attr_accessor :to_domain
def cannot_be_present
newDomClients = Client.where("email like :foo", {:foo => "%#{to_domain}%"})
errors.add(:base, "There cannot be any emails already in the database with the target domain" ) if newDomClients.length > 0
end
end
and the simple_form is:
= simple_form_for(#client_email, url: { action: "update" }, html: {class: "search_form", method: :put }) do |f|
.input-row
= f.input :newDomain, :label => "New domain name", :required => true
(etc)
Initially, it said that #client_email was nil, so I initialized it (which seems unlikely to be necessary given Rails...) with:
- #client_email = ClientEmail.new
but that tells me that ClientEmail doesn't have a to_key method, so I'm clearly missing a bunch of infrastructure somewhere.
If you create a form with an object that belongs to a model you can't create fields that doesn't belongs to that model. You need to create form like this in order to do that:
= simple_form_for(:client_email, url......)
if you create form with symbol like this you can create any field in this form and send to controller that you want. params hash won't change too much and you can call your special fields like this:
params[:client_email][:your_field_name]
since this field doesn't belongs to a model its better to validate it in controller.
Hope it helps.
It appears that I needed:
include Virtus.model
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
I'm not sure exactly what Virtus is. it's probably possible without it, but it's a library I already have...

Enter name in form but save the id as foreign key

I am trying to create a form where one can enter the :name of a foreign model-instance which should be transformed to the :id of that object to be stored as a foreign key.
I have found different ways of doing this, but they all have big disadvantages.
For example: I have two models A and B:
class A < ActiveRecord::Base
belongs_to :b
validates :b_id, presence: true, inclusion: {in: B.all.map(&:id)}
end
class B < ActiveRecord::Base
end
The _form.html.erb uses this field where one can enter the string:
<%= form_for(#a) do |f| %>
...
<div class="field">
<%= f.label :b %><br>
<%= f.text_field :b %>
</div>
...
<% end %>
While the controller for A looks like this:
class AController < ApplicationController
...
before_action :get_a_id, only: [:create, :update]
...
private
def page_params
params.require(:a).permit(:name, :b_id, :content)
end
def get_a_id
b_name = params[:a][:b]
b = B.find_by(name: b_name)
b_id = b.id unless b.nil?
params[:a][:b_id] = b_id
end
end
This works somehow if the entered :name at the text-field has been found in the database. If it is not found, the validation-errors are shown, but the text_field has not been highlighted and it is empty then (instead of containing the previously entered string).
I think this is because the field should contain the Object of B instead of the name and this does not exist in case of a wrong string.
It isn't a good idea at all to manipulate it like this, I think.
Is there a nice way to achieve that? I don't want a drop-down here because there might be a lot of values in there later. Of course the next step will be to implement some sort of auto-completion, but that should not count here.
In theory there should be a way to associate the text_field with a new B-object but just show the :name. The controller could then try to find a database object with that name and replace the placeholder with it. If it does not find the right object, the validation fails, but the value is still shown (of course the validation needs to be changed then).
Any way to achieve such a thing in a nice way is appreciated. Just let me know if anything is unclear.
EDIT:
What I actually want to achieve:
A form-field that allows to enter a string. That string should be passed to the controller where a search is performed that transforms this string into the id of the object (B), so that it can be used as foreign key. When the Object of class A is saved, a validator should check if the ID is set and exists. If that validation fails, the field (with the string) should be highlighted and the error-message should be shown.
The main-problem is, that the string with the name is not in the model. Just the id is. So if the id is validated (what would be the right approach), the error-messages would not be associated to the field containing the name as string.
Update: after getting the full picture:
You want virtual attributes. Then you can do, #a.set_b_name= and it will do the lookup and apply the name, or you can store the name in an instance var and use validations on it.
See these links for more detailed info :)
http://railscasts.com/episodes/167-more-on-virtual-attributes?view=asciicast
Rails: Looking up foreign key id using virtual attribute failing
Old answer was:
if B cannot be found, i.e. b.nil?, then you make a new 'b' object that isn't in the database; b = B.new(name: params[:name]).

How to customize simple_form for json nested hash

I have some nested data:
#preset = Preset.new
#fields is a postgres json data type
#preset.fields = {'primary_category' => {'category_id' => 57882}}
I would like to have the same nested structure preserved in the POST params[:preset][:fields] from a form submit so I have this in my form partial:
<%= text_field_tag("preset[fields][primary_category][category_id]",nil) -%>
Simple form does not know how to deal with postgres new types like hstore or json types. In my case I don't really need it for validation or to detect the data type. Is there a way I can extend SimpleForm to skip detection of the column types and just output the same existing bootstrap boilerplate that it outputs for textfields, but for my arbitrary json nested keys?
Maybe a usage like this:
<%= f.input 'preset[fields][primary_category][category_id]', :as => :json_text_field %>
To output the same thing as helper above, but surrounded with the label, and control-group classed divs, etc.
I have looked into extending the input base class per the documentation.
class JsonTextFieldInput < SimpleForm::Inputs::Base
def input
"#{#builder.text_field(???, input_html_options)}".html_safe
end
end
But here is where I get lost as I'm not sure what to pass to the #builder to bypass checking of attribute name with my own logic to map it hash keys. Also that only changes the form input and not the label, which needs some modification as well. In either case I wasn't able to get very far and I could use some guidance.
I'm using this for jsonb / json input:
class JsonbInput < SimpleForm::Inputs::StringInput
def input()
out = ActiveSupport::SafeBuffer.new
Hash[object.send(attribute_name).sort].each do | k, v|
out << template.content_tag(:div, class: 'group') do
template.concat #builder.label(k, object.send(attribute_name), label_html_options)
template.concat #builder.text_field(k, input_html_options)
end
end
out
end
def input_html_options
{class: 'string form-control'}
end
end
You also need to use store_accessor in your model.

Rails 3 and ActiveAdmin. Filter is displaying Objects not the company name

I have a list of customers but on the filter section on the right column, I get a list like this #<Customer0X0B500> in the select menu. How can I display the company_name attribute of Customer instead?
Figured it out, thanks!
filter :customer, :collection => proc {(Customer.all).map{|c| [c.company_name, c.id]}}
i'm not sure I understand you but probably you should define to_s method inside your Customer class e.g.
class Customer
def to_s
self.company_name
end
end
it would be easier if you shared some code
class Customer
def display_name
self.company_name
end
end
Defining display_name instead of to_s works better...
to_s may be called automatically by other objects.
display_name only affects ActiveAdmin
You can also define:
show :title => :display_name
This will make your company_name appear as the title on the view pages instead of Company #x.
To make ActiveAdmin display select menu properly try in Model.rb:-
alias_attribute :name, :category_name

Resources