how to embed raw html in active_admin formtastic - ruby-on-rails

I'm trying to build a form, with formtastic, inside an active_admin model.
The problem is I need a certain script tag and other raw HTML stuff directly inside or around the form.
I'm doing it with the normal form block:
form do |f|
f.inputs :name => "User Details", :for => :user do |user_form|
user_form.input :first_name, :required => true
...
How do I embed a simple div tag right in between?
Or even a script tag?
I thought about using a render :partial, but I want to know if the above method is possible first. Thanks!

You can insert a div or javascript like this:
f.form_buffers.last << content_tag(:div, "Div content here")
f.form_buffers.last << javascript_tag("alert('hi');")

In the current ActiveAdmin the form_buffers is deprecated. Instead it's possible to do the following:
insert_tag(Arbre::HTML::Div) { content_tag(:span, "foo") }

Active admin created a DSL on top of Formtastic according to their docs
https://github.com/activeadmin/activeadmin/blob/master/docs/5-forms.md
So you can now do:
form do |f|
f.semantic_errors(*f.object.errors.keys)
import_errors = self.controller.instance_variable_get("#errors")
if import_errors.present?
ul class: 'errors' do
import_errors.each do |e|
li e
end
end
end
# ...
end

Related

Rails 6 create custom formtastic input based on existing one

Inside of my ActiveAdmin Rails6 app I've got below partial which replaces the standard input by editor.js (there is some JS magic behind, not relevant to this question). The partial and render look like below:
# _editor.html.erb
<%= f.input field, as: :hidden, input_html: { id: :editor_field } %>
<div style='width: 100%; background: white;' id="editorjs"></div>
# example of a parital call for field :body
<%= render 'admin/editor_js', f: f, field: :body %>
Because ActiveAdmin is based on formatic gem instead of this partial I want to create and use custom input based on :text field. I was trying to do something like below.
module CustomInputs
class EditorJsInput < Formtastic::Inputs::TextInput
def input_html_options
super.merge(input_html: { id: 'editor_field' }).merge(as: :hidden)
end
end
end
(as: :hidden) is not working and no idea how to add this empty div at the end <div style='width: 100%; background: white;' id="editorjs"></div> which is quite crucial.
as: :hidden is not an input_html_option, it's an input style/type that maps the input to the Inputs::HiddenInput type; all it really does is render the input field as a hidden list item. Additionally, the way you're overriding input_html_options is not correct:
# Let's say this is a line in your form input:
input_html: { value: 'Eat plants.' }
# Your code is changing `input_html_options` to be the same as if you'd
# included this in your form input declaration, which doesn't make sense:
input_html: {
value: 'Eat plants.',
input_html: { id: 'editor_field' },
as: :hidden
}
Please review the docu and source on the methods you are trying to override:
Formtastic::Inputs::HiddenInput
Formtastic::Inputs::TextInput
An overridden input_html_options method would be something like this if you're looking to override the existing ID:
# Don't recommend
def input_html_options
# Note: this is dangerous because the 'id' attribute should be unique
# and merging it here instead of passing it in the field's `input_html`
# hash forces every input of this type to have the same id
super.merge(id: 'editor_field')
end
Presuming that you're really looking to reliably know the ID so some JS can find the element and swap it out, you could either specify it in the input_html hash when declaring your form input or, more cleanly, simply use the autogenerated ID. Generally the ID is the the underscore-separated form object root key + the attribute name; so if your form object is coffee: { name: 'Goblin Sludge', origin_country: 'Columbia' }, I believe the default is for f.input :name to render with id='coffee_name' and f.input :origin_country to render with id='coffee_origin_country'. This is stuff that you can easily figure out by using the devtools inspector on the rendered form.
It seems like you really are looking to override to_html. You need something like this:
# app/input/editor_js_input.rb
class EditorJsInput < Formtastic::Inputs::TextInput
def to_html
input_wrapping do
builder.hidden_field(method, input_html_options)
end
end
end
# Variation that creates a div
class EditorJsInput < Formtastic::Inputs::TextInput
def to_html
input_wrapping do
builder.content_tag(
:div,
'',
style: 'width: 100%; background: white;',
id: "#{input_html_options[:id]}_editor"
) << builder.hidden_field(method, input_html_options)
end
end
end
# Example of what this would generate:
# "<div style=\"width: 100%; background: white;\" id=\"coffee_name_editor\"></div><input maxlength=\"255\" id=\"coffee_name\" value=\"\" type=\"hidden\" name=\"coffee[name]\" />"
# PARTIAL
# This will render the div you have above:
div style: 'width: 100%; background: white;', id: 'editorjs'
# This (or a similar variant of form declaration) will render the form
active_admin_form_for resource do |f|
f.inputs do
f.input :name,
input_html: { value: f.object.name },
as: :editor_js
f.input :origin_country,
input_html: { value: f.object.origin_country },
as: :editor_js
end
end
This should build the input for f.object.name as something like <input id="coffee_name" type="hidden" value="Goblin Sludge" name="coffee[name]"> (see FormBuilder#hidden_field
Additional thoughts:
Try using something like Pry to put a binding inside these methods to get a better understanding of what they look like and how they work. Note that you will need to reboot your server for it to load changes to overridden methods in custom input classes.
Read the documentation referenced above and the Formtastic README; there is a lot of really accessible info that would have helped you here. You could also have learned how to render a div from ActiveAdmin's docu here (as well as other pages at activeadmin.info that give examples of styled elements)
Evaluate whether you need to implement a Custom FormBuilder

I18n translated values do not show up if changed from helper method rails

Hello not sure how to explain this but i will give it my best shot.
I hv a model like this
class Answer < ActiveRecord::Base
EMPLOYMENT_TYPE = [
['Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.employed')],
['Self-Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.self_employed')],
['Unemployed / Retired', I18n.t('self_evaluation.self_evaluation_form.employment_status.unemployed')]
]
end
I also have a helper method that calls this
module AnswerHelper
def get_employment_type_list
return Answer::EMPLOYMENT_TYPE
end
end
And a partial view that should display the values like this
_step_1.html.erb
<% employment_list = get_employment_type_list %>
<%= employment_list %>
<div class="flex_center flex_column button-wrap">
<%= collection_radio_buttons("application[answer_attributes]", :employment_type, employment_list, :first, :last, item_wrapper_tag: false, checked: #selected_employment_type) do |b|
b.label(class: 'employment_type_radio') { b.radio_button(:onchange => "return_employment_type(value)", class:'radio-label') + b.label(class:'button-label'){b.text}}
end %>
</div>
_step_2.html.erb
<% employer_list = get_employer_list(chosen_employment_type(#decoded_self_evaluation)) %>
<% employer_type = #decoded_self_evaluation.try(:loan_application).try(:kyc_answer).try(:employer_type) %>
<div class="flex_center flex_column button-wrap">
<%= collection_radio_buttons("loan_application[kyc_answer_attributes]", :employer_type, employer_list, :first, :last, item_wrapper_tag: false, checked: employer_type) do |b|
b.label(class: 'employer_type_radio') { b.radio_button(:onchange => "is_business_registered(value)", class:'radio-label') + b.label(class:'button-label'){b.text}}
end %>
</div>
identity.js
$.ajax({
url:'/self_evaluations/' + self_evaluation_id + '/employment_type',
method:"POST",
data:JSON.stringify({employment_type: employment_type, key: $('#token_key').val()}),
contentType:"application/json; charset=utf-8",
dataType:"script",
async: false,
success: function(data) {
$valid = true;
}
})
employment_list.js.erb
$('#id_employer').empty();
$('#id_employer').append("<%=j render 'self_evaluations/self_evaluation_partials/step_2' %>");
Now the problem is that i have already added all that's needed for I18n translations to happen in Rails and for texts that are translated on the html view it works correctly when i switch from the different languages but for texts that are been gotten via a helper method like this. Based on initial help, on initial load, all page work fine but if i change the language and then run an ajax call that loads the employment_list.js.erb my locale seems to still be the primary language the the values do not change accordingly
Not sure why this is happening anyone faced something like this before?
Because you declared the translations as a constant in your model, so the translations were loaded just one time when Answer model was loaded in the first time.
In production environment, the model will be loaded after starting server. In development environment, the model will be loaded when the file content is changed, ...
In order to solve your issue, you can add the translations in your helper instead:
module AnswerHelper
def get_employment_type_list
[
['Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.employed')],
['Self-Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.self_employed')],
['Unemployed / Retired', I18n.t('self_evaluation.self_evaluation_form.employment_status.unemployed')]
]
end
end
I usually use this approach, please have a look!
class Answer < ActiveRecord::Base
EMPLOYMENT_TYPE = [:employed, :self_employed, :unemployed_or_retired]
end
module AnswerHelper
def get_employment_type_list
i18n_scope = 'self_evaluation.self_evaluation_form.employment_status'
Answer::EMPLOYMENT_TYPE.map do |type|
[type, t(type, scope: i18n_scope)]
end
end
end

Rails Parse Form Input

I'm using a form to transfer some data from one part of a controller to another (new to create), but I'm having some trouble with it. When I try to get the data after submitting the form, it just gives me a nil value.
Here's the form code:
<%= f.hidden_field :owner_id, :value => #tool.user_id %>
<%= f.hidden_field :tool_id, :value => #tool.id %>
<%= f.hidden_field :borrower_id, :value => current_user.id %>
And this is the create action in the controller:
def create
render text: params[:rental_agreement].inspect
#rental_agreement = RentalAgreement.create
#rental_agreement.owner_id = params[:owner_id]
# render text: #rental_agreement.inspect
end
When I hit the "Submit" button on the form, I see this:
{"owner_id"=>"3", "tool_id"=>"1", "borrower_id"=>"4"}
That's fine, but when I change which inspection renders (comment out the top line, uncomment the bottom), all it displays is:
#
And if I look in the Rails console at this object, all of the fields in it are nil (except the id and created_at fields).
I'm just trying to figure out how to assign the variables from the form (owner_id, tool_id, and borrower_id) to the rental_agreement variable. Any help is much appreciated!
Your create method seems wrong. Try this.
def create
#rental_agreement = RentalAgreement.new(params[:rental_agreement])
#rental_agreement.save
end

Ruby on Rails - JS Input token, an issue when validation fails

I have a company model which can have many tags. It works fine, but in one occasion it does not work. The occasion is when company model validation fails. After :render => 'edit' it does not show tags in the view. I suspect the data-pre is not taking the data correctly. I would also like for tags to be preserved when solving validations.
I got this idea from here: http://railscasts.com/episodes/167-more-on-virtual-attributes
I use Input token control: http://loopj.com/jquery-tokeninput/
This is what I have in Company model regarding the tag_tokens:
before_save :save_tag_tokens
attr_writer :tag_tokens
attr_accessible :tag_tokens
def tag_tokens
#tag_tokens || tags.to_json(:only => [:id, :name])
end
def save_tag_tokens
if #tag_tokens
#tag_tokens.gsub!(/CREATE_(.+?)_END/) do
Tag.create!(:name => $1.strip.downcase).id
end
self.tag_ids = #tag_tokens.split(",")
end
end
Here is the code from the view:
<div class="input text no-border">
<% Tag.include_root_in_json = false %>
<%= company_form.label :tag_tokens, t('form.account.company.edit.company_tags_html')%>
<%= company_form.text_field :tag_tokens, :id => 'company_tag_tokens', "data-pre" => #company.tag_tokens%>
<p class="tip"><%= t('form.account.company.edit.tag_tip') %></p>
</div>
EDIT:
OK, so I see what is the problem with the above code.
When i load edit page data-pre contains this: data-pre="[{"id":1704,"name":"dump truck"}]". when I submit the form with validation error the data-pre contains: data-pre="1704".
if i change the code to this:
def tag_tokens
tags.to_json(:only => [:id, :name])
end
new tags that were not yet save to the company model are removed, because they are read from the DB everytime. How can I preserve the entered data between form transitions?
OK, I've written a solution, it might not be the nicest one, but it works to me! It parses the input token value to JSON format (when validation fails), which is used when loading the page. Under page load it just loads tags from DB.
def tag_tokens
if #tag_tokens
#if there is user info, parse it to json format. create an array
array = #tag_tokens.split(",")
tokens_json = []
#loop through each tag and check if it's new or existing
array.each do |tag|
if tag.to_s.match(/^CREATE_/)
#if new generate json part like this:
tag.gsub!(/CREATE_(.+?)_END/) do
tokens_json << "{\"id\":\"CREATE_#{$1.strip.downcase}_END\",\"name\":\"Add: #{$1.strip.downcase}\"}"
end
else
#if tag is already in db, generate json part like this:
tokens_json << "{\"id\":#{tag},\"name\":\"#{Tag.find_by_id(tag).name}\"}"
end
end
#encapsulate the value for token input with [] and add all tags from array
"[#{tokens_json.to_sentence(:last_word_connector => ',', :words_connector => ',', :two_words_connector => ',')}]"
else
#if there is no user input already load from DB
tags.to_json(:only => [:id, :name])
end
end

Create select based on routing, how?

I am trying to implement navigation like in Tree Based Navigation but based on URLs defined in routes.rb (named routes, resources, ...).
Is it possible to retreive a collection of all routes defined in routes.rb?
So I can use it in a select like this:
<%= f.collection_select :url, Route.all, :url, :name %>
Tnx!
ActionController::Routing::Routes.routes
Will list available routes for the application. Will require some parsing to pull out applicable details.
Thanx to the hint of David Lyod I solved it!
Here is my code:
helper-method
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
def routes_url
routes = ActionController::Routing::Routes.routes.collect do |route|
segs = route.segments.inject("") { |str,s| str << s.to_s }
segs.chop! if segs.length > 1
segs.chomp("(.:format)")
end
routes.delete_if {|x| x.index(':id')}
return routes.compact.uniq.sort
end
end
and in my view I put:
<%= select("page", "url", options_for_select(routes_url), {:include_blank => true}) %>

Resources