Posted at

SimpleFormのBootstrap4対応

More than 1 year has passed since last update.

足跡を残しておく。


HTMLの構造が違う

https://getbootstrap.com/docs/4.0/components/forms/#server-side

invalid-feedback などとりあえず形が違う。

config/initializers/simple_form_boosstrap.rb を編集する。自分のところでは下記のようになった。error_class の名前を is-invalid にしたり、 エラー時のspanのクラスをinvalid-feedbackにしたり。

horizontal_formがデフォルトになっていたり、:full_errorになっているのはこちらの都合なので、適宜直していただければ。

# Use this setup block to configure all options available in SimpleForm.

# @see https://suin.io/546
SimpleForm.setup do |config|
config.error_notification_class = 'alert alert-danger'
config.button_class = 'btn btn-default'
config.boolean_label_class = nil

config.wrappers :vertical_form, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'control-label'

b.use :input, class: 'form-control'
b.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end

config.wrappers :vertical_file_input, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :readonly
b.use :label, class: 'control-label'

b.use :input
b.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end

config.wrappers :vertical_boolean, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.optional :readonly

b.wrapper tag: 'div', class: 'checkbox' do |ba|
ba.use :label_input
end

b.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end

config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'control-label'
b.use :input
b.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end

config.wrappers :horizontal_form, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'

b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-control'
ba.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end

config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'

b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input
ba.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end

config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.optional :readonly

b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr|
wr.wrapper tag: 'div', class: 'checkbox' do |ba|
ba.use :label_input
end

wr.use :error, wrap_with: { tag: 'span', class: 'help-block' }
wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end

config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.optional :readonly

b.use :label, class: 'col-sm-3 control-label'

b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input
ba.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end

config.wrappers :inline_form, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'sr-only'

b.use :input, class: 'form-control'
b.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end

config.wrappers :multi_select, tag: 'div', class: 'form-group row', error_class: 'is-invalid' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'control-label col-sm-3'
b.wrapper tag: 'div', class: 'form-inline col-sm-9' do |ba|
ba.use :input, class: 'form-control'
ba.use :full_error, wrap_with: { tag: 'span', class: 'invalid-feedback' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
# Wrappers for forms and inputs using the Bootstrap toolkit.
# Check the Bootstrap docs (http://getbootstrap.com)
# to learn about the different styles for forms and inputs,
# buttons and other elements.
config.default_wrapper = :horizontal_form
config.wrapper_mappings = {
check_boxes: :horizontal_radio_and_checkboxes,
radio_buttons: :horizontal_radio_and_checkboxes,
file: :horizontal_file_input,
boolean: :horizontal_boolean,
datetime: :multi_select,
date: :multi_select,
time: :multi_select
}
end


バリデーションが飾られない

上記をやるだけでは画面にフィードバックも出ないし、色分けもされない。

とりあえず、formに was-validatedを付けると表示されるようにはなるので、下記のようなヘルパーを書いた。多分SimpleFormがすぐに追随すると思う(これは無駄な努力感)。

module ApplicationHelper

def simple_form_ex_for(record, options = {}, &block)
classes = ['form-horizontal']
if record.is_a?(Array)
classes.push('was-validated') if record.last.try(:errors).present?
else
classes.push('was-validated') if record.try(:errors).present?
end

simple_form_for(record, html: {class: classes.join(' ')}, &block)
end
end


invalidなのにグリーンの枠

form-control:validは、ブラウザ側のバリデーションのセレクタだが、これが悪さしてしまう様子。よくわからなくて、下記のようなscssを書いて逃げた。

/* for bootstrap4 validation */

form.was-validated {
.is-invalid {
.form-control {
border-color: #dc3545;
}
}
}


おしまいに

やりたいこと出来たのだけど、教材の内容で使おうとしていたのでこれはオーバースペック。おとなしくBootstrap3にすることにした。