【问题标题】:How to extend a core Rails FormBuilder field如何扩展核心 Rails FormBuilder 字段
【发布时间】:2014-04-06 16:04:18
【问题描述】:

我正在使用带有 Rails 4 的 Bootstrap 3,我想创建一个自定义 FormBuilder 来处理 Bootstrap 的一些独特的 HTML 语法。具体来说,我需要一个自定义助手来创建围绕表单字段的form-group div 包装器,因为Bootstrap applies error state to this wrapper, and not the field itself...

<div class="form-group has-error">
  <label class="col-md-3 control-label" for="user_email">Email</label>
  <div class='col-md-9'>
    <input class="form-control required-input" id="user_email" name="user[email]" placeholder="peter@example.com" type="email" value="someone@example.com" />
  </div>
</div>

注意外部 div 中的额外类 has-error...

不管怎样,我写了那个助手,效果很好!

def form_group(method, options={})
  class_def = 'form-group'
  class_def << ' has-error' unless @object.errors[method].blank?
  class_def << " #{options[:class]}" if options[:class].present?
  options[:class] = class_def
  @template.content_tag(:div, options) { yield }
end

# Here's a HAML sample...

= f.form_group :email do
  = f.label :email, nil, class: 'col-md-3 control-label'
  .col-md-9
    = f.email_field :email, class: 'form-control required-input', placeholder: t('sample.email')

现在我想利用 Bootstrap 的 form help text 来显示错误消息。这需要我扩展 Rails 原生助手(例如上面示例中的 text_field),然后在 f.form_group 的块中调用它们。

解决方案似乎很简单:调用父级,并将我的 span 块附加到末尾...

def text_field(method, options={})
  @template.text_field(method, options)
  if !@object.errors[method].blank?
    @template.content_tag(:span, @object.errors.full_messages_for(method), class: 'help-block')
  end
end

只有它不会输出任何 HTML,div 只会显示为空。我尝试了一堆差异语法方法:

  • super vs text_field vs text_field_tag
  • concat-ing 结果 -- @template.concat(@template.content_tag( [...] ))
  • 动态变量,例如def text_field(method, *args) 然后options = args.extract_options!.symbolize_keys!

我只会遇到奇怪的语法错误或空的div。在某些情况下,input 字段会出现,但帮助文本 span 不会出现,反之亦然。

我确定我搞砸了一些简单的事情,我只是没看到。

【问题讨论】:

    标签: ruby-on-rails ruby forms ruby-on-rails-4 twitter-bootstrap-3


    【解决方案1】:

    花了几天时间,但我最终偶然发现了正确的语法。希望它能拯救别人的理智!

    Ruby 的return automagic 与 Rails 有时复杂的作用域相结合,让我感到困惑。具体来说,@template.text_field 绘制内容,但它必须由辅助方法返回才能出现在调用块内。但是我们必须返回 两次 调用的结果...

    def text_field(method, options={})
      field_errors = object.errors[method].join(', ') if !@object.errors[method].blank?
      content = super
      content << (@template.content_tag(:span, @object.errors.full_messages_for(method), class: 'help-block') if field_errors)
      return content
    end
    

    我们必须返回父方法(通过super)加上我们自定义的@template.content_tag(:span, 注入的结果。我们可以使用 Ruby 的 plus + 运算符将其缩短一点,它将返回结果连接起来。

    def text_field(method, options={})
      field_errors = object.errors[method].join(', ') if !@object.errors[method].blank?
      super + (@template.content_tag(:span, @object.errors.full_messages_for(method), class: 'help-block') if field_errors)
    end
    

    注意:表单是使用 ActiveModel 对象启动的,这就是我们可以访问 @object 的原因。在不将 form_for 与模型关联的情况下实现 form_for 将需要您改为扩展 text_field_tag

    这是我完成的自定义 FormBuilder

    class BootstrapFormBuilder < ActionView::Helpers::FormBuilder
    
      def form_group(method, options={})
        class_def = 'form-group'
        class_def << ' has-error' unless @object.errors[method].blank?
        class_def << " #{options[:class]}" if options[:class].present?
        options[:class] = class_def
        @template.content_tag(:div, options) { yield }
      end
    
      def text_field(method, options={})
        field_errors = object.errors[method].join(', ') if !@object.errors[method].blank?
        super + (@template.content_tag(:span, @object.errors.full_messages_for(method), class: 'help-block') if field_errors)
      end
    
    end
    

    别忘了告诉form_for

    form_for(:user, :builder => BootstrapFormBuilder [, ...])
    

    编辑:这里有一些有用的链接帮助我走上了启蒙之路。 Link-juice 向作者致敬!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-27
      • 2015-07-06
      • 2014-10-18
      • 2011-03-04
      • 2023-03-12
      • 1970-01-01
      • 2023-03-11
      相关资源
      最近更新 更多