ActiveAdmin invalid form still submit - ruby-on-rails

In ActiveAdmin I have a form with typed inputs and required fields, and even If I let required fields blank or put numbers in email typed input, I am still able to perform submit even if the form is not valid.
my code :
ActiveAdmin.register User do
actions :all, except: [:destroy, :edit]
permit_params :email
form do |f|
f.semantic_errors
f.inputs 'Invite a new user' do
f.input :email, required: true, input_html: {required: true, type: "email" }, as: :email
f.input :first_name
f.input :last_name
end
f.actions
end

This issue is due to the fact that formtastic puts "novalidate" by default on all forms :
https://github.com/formtastic/formtastic/issues/1001
The fix is to create an initializer and update default formtastic config where it make sense for you such as :
# config/initializers/formtastic.rb
# Formtastic is the form builder used by ActiveAdmin
# You can find the original config file here:
# https://github.com/formtastic/formtastic/blob/master/lib/generators/templates/formtastic.rb
# You can opt-in to Formtastic's use of the HTML5 `required` attribute on `<input>`, `<select>`
# and `<textarea>` tags by setting this to true (defaults to false).
Formtastic::FormBuilder.use_required_attribute = false
# You can opt-in to new HTML5 browser validations (for things like email and url inputs) by setting
# this to true. Doing so will omit the `novalidate` attribute from the `<form>` tag.
# See http://diveintohtml5.org/forms.html#validation for more info.
Formtastic::FormBuilder.perform_browser_validations = true

Related

ActiveAdmin / Formtastic - How to have an input that is not associated to the model?

I'm using activeadmin and in a form associated with a model I would need to add a checkbox that is not tied to any of the model attributes. It should be just a checkbox that won't allow to submit the form until it's checked - I'll also add some Javascript to achieve this.
I've checked the documentation of ActiveAdmin and Formtastic but I want to be able to find anything for this purpose. How to add the custom checkbox and where I should add the Javascript functionality?
There’s a couple ways to achieve this, but you’ll likely want to submit it as a param as well.
If it’s implemented using simply JS the end user can get around its necessity for submission, while your backend could filter for such submissions.
Though you may not particularly care about this.
One of my favourite tools for JS scripts is
AlpineJS and is how I’d go about implementing a JS version for this (and how I’ve done similar implementations in the past).
By tying the value of a checkbox to a AlpineJS variable you can then use x-bind to toggle classes on the submission buttons for disabling/enabling.
You can add an
attr_accessor :accepts_terms
attr_accessor :from_browser
to your model and create a standard ActiveRecord validation for it.
validate :needs_acceptance
def needs_acceptance
if from_browser
errors.add(:accepts_terms, 'Pleace accept terms') unless accepts_terms
end
end
Add it to the permitted_params in you ActiveAdmin register block
permit_params :accepts_terms, :from_browser
Now you can add it to the ActiveAdmin form do block with custom html using the arbre syntax like so:
form do |f|
# ...
f.inputs do
# ...
div(class: 'mystyle') do
span(class: flows') do
f.input :from_browser, as: :hidden, input_html: { value: true }
f.input :accepts_terms, as: :boolean
end
end
end
f.actions
end
In this specific case (Having a "Are you sure?" checkbox) the solution was using Rails' Acceptance validation which makes sure that the user ticked a checkbox when the form was submitted.
If we need to manually edit the template, I've found a way by looking at the Arbre docs. I've added an example that might be useful for checking different features:
# app/models/car.rb
class Car < ApplicationRecord
belongs_to :brand
validates :model, presence: true
validates :engine, presence: true
validates :terms_of_service, acceptance: {message: "Please check this box if you really want to buy a Ferrari"}, if: -> { brand.to_s == "Ferrari" }
end
# app/admin/car.rb
ActiveAdmin.register Car do
permit_params :brand, :model, :engine, :terms_of_service
filter :brand
index do
html {
body {
para "Compile the form and remember to read and accept the <strong>Terms of service</strong> if you want to buy a Ferrari.".html_safe
}
}
id_column
column(:brand) { |car| link_to(car.brand.to_s, admin_brand_path(car.brand)) }
column(:model)
column(:engine)
actions
end
controller do
def scoped_collection
resource_class.includes(:brand)
end
end
form partial: "form"
end
# app/views/admin/cars/_form.html.arb - yes Arbre templates are .arb
active_admin_form_for [:admin, resource] do |f|
f.inputs do
f.input :brand
f.input :model
f.input :engine
f.input :terms_of_service, as: :boolean, label: "Accept the terms of service for buying a Ferrari"
end
f.actions
end
# This is how I've added some JS to the form. Maybe there's a better way (e.g. put it in a separate file)
html {
body {
script do
raw "
const submitButton = document.querySelector('form.car input[type=submit]')
const termsOfServiceCheckbox = document.getElementsByName('app_quota_limit[terms_of_service]')[1]
const disableSubmitButton = function() {
submitButton.disabled = true
submitButton.classList.add('disabled')
}
const enableSubmitButton = function() {
submitButton.disabled = false
submitButton.classList.remove('disabled')
}
const termsOfServiceCheckboxUseEffect = function() {
if (termsOfServiceCheckbox.checked) {
disableSubmitButton()
}
else {
enableSubmitButton()
}
}
// Disable button at the beginning
disableSubmitButton()
termsOfServiceCheckbox.addEventListener('input', termsOfServiceCheckboxUseEffect)
"
end
}
}

How to delete attribute name from invalid feedback below simple form checkbox?

On the screenshot, you'll see that invalid feedback is specifying the attribute name from the input, here "Adulthood". I don't want it and i can't delete it. It's not in the acceptance message from the model, not in the view. Where does it come from ?
Code from the model :
validates :adulthood, acceptance: { message: "Only adults can signup on La Voyageuse" }, on: :create
Code from the view :
<%= f.input :adulthood, as: :boolean, label: t('.adulthood?'), class:"form-checkbox" %>
You have not inspected your view part which display error log correctly. Following will help you to inspect and handle your issue
u = User.new
u.valid? # => false
u.errors.messages # => {:email=>["This field is required."], :password=>["This field is required."]}
u.errors.full_messages # # => ["Email This field is required.", "Password This field is required."]
You was just subjected to show,
u.errors.messages[:email] # => "This field is required."
Inspect and edit your code in view.
I added a custom error message on the input to fix the problem :
<%= f.input :adulthood, as: :boolean, label: t('.adulthood?'), class:"form-checkbox", error: "You need to be an adult" %>
It will be internationalized so i'll call the same i18n tag than the model.

ActiveAdmin: form input without corresponding table attribute

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

Simple_form always sets pattern for email input element

I use simple_form to display an email field:
= f.simple_fields_for :user do |f|
= f.input :email, wrapper: :append, pattern: false do
= f.input_field :email, type: "email"
Somehow it always set's the pattern for the input field but I want to use the HTML5 validation of the email input field instead.
Is there a way to prevent simpleform from setting the pattern?
You can monkey-patch SimpleForm:
module SimpleForm
module Components
module Pattern
def pattern
# Deactivated for good:
#input_html_options[:pattern] ||= pattern_source
nil
end
end
end
end
Now, pattern will no longer be generated from validations. You need to add pattern manually to each input that requires a pattern. Example:
= f.input :name, pattern: "[a-zA-Z]+"
Had the same problem ... found a workaround but its kind of hacky
add
:pattern => ".*"
to your field like this
<%= f.input_field :email, :autofocus => true, :pattern => ".*", :class => "span12", :placeholder => t('placeholder.email') %>
I would rather make a string_input.rb file in the app directory some where ... may be in a folder named 'inputs' then have this code
class StringInput < SimpleForm::Inputs::StringInput
def input
input_html_options[:class] = "input-xlarge #{input_html_options[:class]}"
unless string?
input_html_classes.unshift("string")
input_html_options[:type] ||= input_type if html5?
end
input_html_options[:pattern] = nil
add_size!
#builder.text_field(attribute_name, input_html_options)
end
end
This will have effect on all the pattern attributes so no need to put it in explicitly. of course you can also add a condition if the input type is email or not if you want to specify.
Happy coding :)

Deleting a Paperclip Attachment in Activeadmin

I'm using paperclip to add image attachments to several models and Activeadmin to provide a simple admin interface.
I have this code in my activeadmin model file which allows for image uploads:
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
f.input :name
f.input :subdomain
end
f.inputs "General Customisation" do
f.input :standalone_background, :hint => (("current image:<br/>").html_safe + f.template.image_tag(f.object.standalone_background.url(:thumb))).html_safe, :as => :file
end
end
which works fine. All of the images I'm attaching like this are optional and so I'd like to give the user the option to remove a previously added image but can't work out how to do this in Activeadmin. All of the example I've seen are for situations where the attachments are managed through a separate has_many association rather than being part of the main model.
Does anyone know of a way to do this?
In your active admin view
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
f.input :name
f.input :subdomain
end
f.inputs "General Customisation" do
f.input :standalone_background, :hint => (("current image:<br/>").html_safe + f.template.image_tag(f.object.standalone_background.url(:thumb))).html_safe, :as => :file
f.input :remove_standalone_background, as: :boolean, required: false, label: "remove standalone background"
end
end
In your model
You could define a status flag like bellow
attr_writer :remove_standalone_background
def remove_standalone_background
#remove_standalone_background || false
end
OR (depreciated in rails 3.2)
attr_accessor_with_default : standalone_background,false
before_save :before_save_callback
And
def before_save_callback
if self.remove_standalone_background
self.remove_standalone_background=nil
end
end
You could implement this by creating a custom method. This can be done
member_action :custom_action, :method => :get do
//code
end
Also you should add a custom column with a link such as
index do
column "Custom" do |item|
link_to "Custom action", "/admin/items/custom_action"
end
end
Another option is to have a status flag for the attachment or image. Before saving the edited object, you unlink the image.
Thank you for your help guys. This is the final working code...
admin/product.rb
f.input :image, required: false, hint: (("Current image:<br/>").html_safe + f.template.image_tag(f.object.image.url(:thumb))).html_safe
f.input :remove_image, as: :boolean, required: false, label: "Remove Image"
models/product.rb
attr_writer :remove_image
def remove_image
#remove_image || false
end
before_validation { self.image.clear if self.remove_image == '1' }
Although accepts_nested_attributes_for(:foo, allow_destroy: true) only works with ActiveRecord associations like belongs_to we can borrow from its design to have paperclip attachment deletion work in a similar way.
(To understand how nested attributes work in Rails see http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html).
Add a <attachment_name>_attributes= writer method like below to your model that already uses has_attached_file:
has_attached_file :standalone_background
def standalone_background_attributes=(attributes)
# Marks the attachment for destruction on next save,
# if the attributes hash contains a _destroy flag
# and a new file was not uploaded at the same time:
if has_destroy_flag?(attributes) && !standalone_background.dirty?
standalone_background.clear
end
end
The <attachment_name>_attributes= method calls Paperclip::Attachment#clear to mark the attachment for destruction when the model is next saved.
Next open the existing app/admin/your_model_here.rb file (use the correct file path for your app) and setup strong parameters to permit the _destroy flag nested attribute on <attachment_name>_attributes:
ActiveAdmin.register YourModelHere do
permit_params :name, :subdomain,
:standalone_background,
standalone_background_attributes: [:_destroy]
In the same file, add a nested _destroy checkbox to the ActiveAdmin form block. This checkbox must be nested within <attachment_name>_attributes using semantic_fields_for (or one of the other nested attributes methods provided by formtastic).
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
...
end
f.inputs "General Customisation" do
...
if f.object.standalone_background.present?
f.semantic_fields_for :standalone_background_attributes do |fields|
fields.input :_destroy, as: :boolean, label: 'Delete?'
end
end
end
end
Your form should now show a delete checkbox when there is an attachment present. Checking this checkbox and submitting a valid form ought to delete the attachment.
Source: https://github.com/activeadmin/activeadmin/wiki/Deleting-Paperclip-Attachments-with-ActiveAdmin

Resources