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

906 Views Asked by At

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?

3

There are 3 best solutions below

3
Chase McDougall On

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.

0
sekmo On

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
  }
}
0
Sjors Branderhorst On

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