Last active
April 27, 2022 20:42
-
-
Save ftes/eb464a1f9f074ede5d72ecd48bd55f74 to your computer and use it in GitHub Desktop.
Rails bulma form builder with errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-# layouts/_error_messages.html.haml | |
- if f.object.errors.any? | |
.notification.is-danger Please review the problems below: | |
- if f.object.errors[:base].present? | |
.notification.is-danger= f.object.errors[:base].join(', ') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%h2.title Log in | |
= form_with model: resource, as: resource_name, url: session_path(resource_name), local: true do |f| | |
= render "layouts/error_messages", f: f | |
= f.text_field_with_label :email, autofocus: true, autocomplete: 'email' | |
= f.password_field_with_label :password, autocomplete: 'current-password' | |
- if devise_mapping.rememberable? | |
= f.check_box_with_label :remember_me, as: :boolean | |
.buttons | |
= f.submit 'Log in' | |
= render 'devise/shared/links' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Based on https://medium.com/@dyanagi/a-bulma-form-builder-for-ruby-on-rails-applications-aef780808bab | |
# Additions: | |
# - Add `field`` div around input elements | |
# - Show error beneath each input element | |
class BulmaFormBuilder < ActionView::Helpers::FormBuilder | |
alias_method :parent_label, :label | |
# Label for most types of input tags (text, password, email...) | |
def label(method, text = nil, options = {}, &block) | |
super(method, text, merge_class(options, 'label'), &block) | |
end | |
def text_field(method, options = {}) | |
div_field do | |
(block_given? ? yield : nothing) + div_control do | |
super(method, merge_class(options, 'input')) + errors(method) | |
end | |
end | |
end | |
def text_field_with_label(method, options = {}) | |
text_field(method, options) do | |
label(method) | |
end | |
end | |
# Email field with an icon | |
def email_field(method, options = {}) | |
div_field do | |
(block_given? ? yield : nothing) + div_control_for_icons do | |
super(method, merge_class(options, 'input')) + email_icon + errors(method) | |
end | |
end | |
end | |
# Email field with an icon, plus the label for it | |
def email_field_with_label(method, options = {}) | |
email_field(method, options) do | |
label(method) | |
end | |
end | |
# Password field with an icon | |
def password_field(method, options = {}) | |
div_field do | |
(block_given? ? yield : nothing) + div_control_for_icons do | |
super(method, merge_class(options, 'input')) + password_icon + errors(method) | |
end | |
end | |
end | |
# Password field with an icon, plus the label for it | |
def password_field_with_label(method, options = {}) | |
password_field(method, options) do | |
label(method) | |
end | |
end | |
# Omit &block (custom option rendering) | |
def select(method, choices = nil, options = {}, html_options = {}) | |
div_field do | |
(block_given? ? yield : nothing) + div_control do | |
div_select do | |
super | |
end + errors(method) | |
end | |
end | |
end | |
# Omit &block (custom option rendering) | |
def select_with_label(method, choices = nil, options = {}, html_options = {}) | |
select(method, choices = nil, options, html_options) do | |
label(method) | |
end | |
end | |
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) | |
div_field do | |
(block_given? ? yield : nothing) + div_control do | |
div_select do | |
super | |
end + errors(method) | |
end | |
end | |
end | |
def collection_select_with_label(method, collection, value_method, text_method, options = {}, html_options = {}) | |
collection_select(method, collection, value_method, text_method, options, html_options) do | |
label(method) | |
end | |
end | |
def check_box_with_label(method, options = {}, checked_value = "1", unchecked_value = "0") | |
div_field do | |
div_control do | |
check_box(method, options = {}, checked_value, unchecked_value) + parent_label(method, nil, {class: 'checkbox m-l-5'}) | |
end | |
end | |
end | |
# Submit button without colour | |
def submit(value = nil, options = {}) | |
super(value, merge_class(options, 'button')) | |
end | |
# Submit button with the primary colour for most forms | |
def submit_primary(value = nil, options = {}) | |
submit(value, merge_class(options, 'is-primary')) | |
end | |
private | |
# @param options [Hash] | |
# @param value [String] | |
def merge_class_attribute_value(options, value) | |
new_options = options.clone | |
new_options[:class] = [value, new_options[:class]].join(" ") | |
new_options | |
end | |
alias_method :merge_class, :merge_class_attribute_value | |
def div_control | |
@template.content_tag(:div, class: 'control') do | |
yield | |
end | |
end | |
def div_field | |
@template.content_tag(:div, class: 'field') do | |
yield | |
end | |
end | |
def div_select | |
@template.content_tag(:div, class: 'select') do | |
yield | |
end | |
end | |
def div_control_for_icons | |
@template.content_tag(:div, class: 'control has-icons-left') do | |
yield | |
end | |
end | |
def email_icon | |
@template.content_tag(:span, class: 'icon is-left') do | |
@template.content_tag(:i, "", class: 'fas fa-envelope') | |
end | |
end | |
def password_icon | |
@template.content_tag(:span, class: 'icon is-left') do | |
@template.content_tag(:i, "", class: 'fas fa-lock') | |
end | |
end | |
def nothing | |
ActiveSupport::SafeBuffer.new() | |
end | |
# Errors for method (and for relations without `_id` suffix) | |
def errors(method) | |
return nothing if object.errors.none? | |
methods = [method] | |
methods.push(method.to_s.chomp '_id') if method.to_s.end_with? '_id' | |
@template.content_tag(:p, class: 'help is-danger') do | |
methods.flat_map { |m| object.errors.full_messages_for(m) }.join(', ') | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment