Skip to content

Instantly share code, notes, and snippets.

@2called-chaos
Last active January 23, 2019 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 2called-chaos/ccd522dbc7a70134443dde37d7b24b80 to your computer and use it in GitHub Desktop.
Save 2called-chaos/ccd522dbc7a70134443dde37d7b24b80 to your computer and use it in GitHub Desktop.
Bootstrap 3 rails form builder
<%= bs_form_for [:backend, @publisher] do |f| %>
<%= f.lock %>
<%= f.error_messages %>
<%= f.labeled_text_field :name %>
<%= f.labeled_text_field :support_link, placeholder: "http://help.ea.com", hint: "Possibly visible to customers if given." %>
<%= f.labeled_text_field :support_email, placeholder: "support@ea.com", hint: "Only for internal use." %>
<%= f.indicate_tenant(:global) unless @publisher.persisted? %>
<%= f.actions do %>
<%= f.submit "submit" %> or <%= link_to "cancel", :back %>
<% end %>
<% end %>
<% target, field = *[*target] %>
<% if field %>
<% if target.error?(field) %>
<span class="help-block text-danger">
<% errors = target.errors[field] %>
<% if errors.count > 1 %>
<ul>
<% errors.each do |err| %>
<li><%= err %></li>
<% end %>
</ul>
<% else %>
<%= errors.first %>
<% end %>
</span>
<% end %>
<% else %>
<% if target.errors[:base].any? %>
<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert">&times;</button>
<h4>Errors occured</h4>
<div>
<ul>
<% target.errors.full_messages_for(:base).each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<% end %>
module ApplicationHelper
def bs_form_for(object, options = {}, &block)
options[:builder] = Bootstrap3FormBuilder
options[:bs] ||= {}
options[:html] ||= {}
options[:html][:class] ||= "form-horizontal"
form_for(object, options, &block)
end
end
class Bootstrap3FormBuilder < ActionView::Helpers::FormBuilder
delegate :content_tag, :tag, :capture, to: :@template
def label_cols
@options[:bs].try(:[], :label_cols) || 2
end
def controls_cols
@options[:bs].try(:[], :controls_cols) || 10
end
# labeled tags
%w[text_field text_area password_field select file_field].each do |method_name|
define_method("labeled_#{method_name}") do |name, *args, &block|
labeled_custom(name, *args) do |errors_or_hint|
content = block.try(:call) || __send__(method_name, name, *args)
content + errors_or_hint
end
end
end
def bs_resize lcols = 2, ccols = 10
if block_given?
lcols_was = @options[:bs].try(:[], :label_cols)
ccols_was = @options[:bs].try(:[], :controls_cols)
end
@options[:bs] ||= {}
@options[:bs][:label_cols] = lcols
@options[:bs][:controls_cols] = ccols
yield if block_given?
ensure
if block_given?
@options[:bs][:label_cols] = lcols_was
@options[:bs][:controls_cols] = ccols_was
end
end
# default input classes
%w[text_field text_area password_field file_field].each do |method_name|
define_method("#{method_name}") do |name, *args, &block|
opts = args.extract_options!
opts[:class] ||= "form-control"
if object.class.respond_to?(:columns)
col = object.class.columns.detect{|c| [:string, :text].include?(c.type) && c.name == name.to_s }
if col && col.limit
opts[:data] ||= {}
opts[:data][:maxlength] ||= col.limit
end
end
super(name, *args, opts)
end
end
def select name, choices, opts = {}
opts[:html] ||= {}
opts[:html][:class] ||= "form-control"
html_opts = opts.delete(:html)
super(name, choices, opts, html_opts)
end
def submit *args
opts = args.extract_options!
opts[:class] ||= "btn btn-primary"
super(*args, opts)
end
def labeled_static name, text = "", opts = {}, &block
labeled_custom(name, opts) {|hint| static((text.presence || "#{capture(&block) if block}".presence || "#{object.try(name)}").html_safe + hint) }
end
def labeled_date_select name, opts = {}
labeled_datetime_select(name, opts.merge(time: false))
end
def labeled_time_select name, opts = {}
labeled_datetime_select(name, opts.merge(date: false))
end
def labeled_datetime_select name, opts = {}
opts = opts.reverse_merge(pos: "before", date: true, time: true, locale: I18n.locale)
icon_class = opts[:date] ? "calendar" : "clock-o"
sopts = (opts.delete(:opts) || {}).reverse_merge(
pickDate: opts[:date],
pickTime: opts[:time],
language: opts[:locale],
useSeconds: false,
minuteStepping: 15,
startDate: "2000-1-1",
)
labeled_custom(name, opts) do |errors_or_hint|
content = content_tag :div, data: sopts, class: "input-group bs-#{"date" if opts[:date]}#{"time" if opts[:time]}-select" do
tf = text_field(name, value: object.send(name))
ico = content_tag(:span, class: "input-group-addon") do
content_tag(:span, "", class: "fa fa-#{icon_class}", data: { "icon-element" => "" })
end
clear = content_tag(:a, @template.icon(:times), href: "#", class: "datetime-clear times")
opts[:pos] == "before" ? ico + clear + tf : tf + clear + ico
end
content + errors_or_hint
end
end
# ===========
# = generic =
# ===========
def errors_or_hint name, hint
(object.try(:error?, name) ? field_errors(name) : hint)
end
def labeled_custom name, *args, &block
opts = args.last.is_a?(Hash) ? args.last : {}
group name, opts[:group] do
hint = self.hint(opts.delete(:hint)) if opts[:hint]
lopts = opts[:label].is_a?(String) ? { text: opts[:label] } : (opts[:label] || {})
field_label(name, lopts[:text], lopts) + controls(capture("#{errors_or_hint(name, hint)}".html_safe, &block))
end
end
def hint hints = {}
hints = case hints
when String, Array then { muted: hints }
else hints
end
"".html_safe.tap do |o|
hints.each do |type, msgs|
[*msgs].each do |msg|
o << content_tag(:span, content_tag(:span, msg, class: "text-#{type}"), class: "help-block")
end
end
end
end
def group name = nil, opts = {}, &block
opts ||= {}
has_error = name && object.try(:error?, name) ? "has-error" : ""
content_tag :div, opts.merge(class: "form-group #{has_error}") do
capture(&block)
end
end
def field_label(name, text = nil, opts = {})
opts = opts.is_a?(String) ? { text: opts } : opts
opts[:text] ||= text
opts[:class] ||= "col-md-#{label_cols} control-label"
if object.class.respond_to?(:validators_on)
opts[:class] << " required" if object.class.validators_on(name).any? { |v| v.kind_of? ActiveModel::Validations::PresenceValidator }
end
label(name, opts[:text], opts)
end
def controls text = nil, &block
content_tag :div, class: "col-md-#{controls_cols}" do
text || capture(&block)
end
end
def static text = nil, &block
content_tag :p, class: "form-control-static" do
text || capture(&block)
end
end
def actions text = nil, &block
content_tag(:hr) +
content_tag(:div, class: "form-group") do
content_tag :div, class: "col-md-offset-#{label_cols} col-md-#{controls_cols}" do
text || capture(&block)
end
end
end
def indicate_tenant which = nil
value = case which
when :all then "All existing tenants"
when :global then @template.icon(:globe, "Global").to_s
else (which || Tenant.current).try(:full_desc)
end
labeled_static :tenant_id, value
end
def error_messages
@template.render "shared/form_errors", target: object
end
def field_errors name
@template.render "shared/form_errors", target: [object, name]
end
def lock
hidden_field :original_updated_at
end
private
def objectify_options(options)
super.except(:label)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment