1

私たちのRailsアプリでは、対応するデータベースフィールドに記録できるよりも多くの文字をユーザーがテキストフィールドに物理的に入力できないようにしたいと考えています。これは、タイプしすぎて、後でやり直すように言われるよりも、はるかにフレンドリーに思えます。

つまり、すべてのビュー入力テキスト フィールドには、対応する ActiveRecord モデルフィールドmaxlength=と同じ属性が設定されている必要があります。:limitvarchar()

これを自動的に発生させる方法はありますか?

そうでない場合、それを可能にする DRY イディオム、ヘルパー関数、またはメタプログラミング ハックはありますか?

4

2 に答える 2

3

Rails 4 ソリューション

以下はテスト済みで動作しています。

# config/initializers/text_field_extensions.rb
module ActionView
  module Helpers
    module Tags
      class TextField

        alias_method :render_old, :render

        def render
          prototype = @object.class
          unless prototype == NilClass
            maxlength = nil
            validator = prototype.validators.detect do |v|
              v.instance_of?(ActiveModel::Validations::LengthValidator) &&
              v.attributes.include?(@method_name.to_sym) &&
              v.options.include?(:maximum)
            end
            if !validator.nil?
              maxlength = validator.options[:maximum]
            else
              column = prototype.columns.detect do |c|
                c.name == @method_name &&
                c.respond_to?(:limit)
              end
              maxlength = column.limit unless column.nil?
            end
            @options.reverse_merge!(maxlength: maxlength) unless maxlength.nil?
          end
          render_old
        end

      end
    end
  end
end

このソリューションはDeefour の answerから借用していますが、代わりにパッチを ActionView のTextFieldクラスに適用します。他のすべてのテキストベースの入力タイプ ( password_fieldemail_fieldsearch_fieldなど) は、 から継承するタグを使用しますTextField。つまり、この修正はそれらにも適用されます。これに対する唯一の例外text_areaは、別のタグを使用するメソッドであり、このパッチが に個別に適用されない限り、同じように機能しませんActionView::Helpers::Tags::TextArea

Deefour のソリューションは、ActiveRecord データベースの列を参照して最大長を決定します。これは Gene の質問に完全に答えましたが、データベース列の制限は通常、フィールドで実際に必要な制限とは無関係 (つまり、それよりも高い) であることがわかりました。最も望ましい最大長は、通常LengthValidator、モデルに設定した から得られます。したがって、最初に、このパッチはLengthValidator、(a) フィールドに使用されている属性に適用され、(b) 最大長を提供するモデルの を探します。それから何も得られない場合は、データベース列で指定された制限が使用されます。

最後に、Deefour の回答と同様に、 の使用法は、引数にa を指定することでフィールドの属性をオーバーライドできることを@options.reverse_merge!意味します。maxlength:maxlengthoptions

<%= form_for @user do |f| %>
  <%= f.text_field :first_name, :maxlength => 25 %>
<% end %>

に設定:maxlengthするnilと、属性が完全に削除されます。

最後に、maxlengtha オプションを指定すると、入力要素の属性TextFieldが自動的に同じ値に設定されることに注意してください。個人的には、この属性は時代遅れであり、CSS によって廃止されたとsize思うので、削除することにしました。sizeこれを行うには、関連する行を@options.reverse_merge!次のように置き換えます。

@options.reverse_merge!(maxlength: maxlength, size: nil) unless maxlength.nil?
于 2014-12-07T18:46:50.730 に答える
1

次の(テストされていない)モンキーパッチのようなものが役立つかもしれません

module ActionView
  module Helpers

    old_text_field = instance_method(:text_field)                                                           # store a reference to the original text_field method

    define_method(:text_field) do |object_name, method, options = {}|                                       # completely redefine the text_field method
      klass     = InstanceTag.new(object_name, method, self, options.delete(:object)).retrieve_object.class # get the class for the object bound to the field
      column    = klass.columns.reject { |c| c.name != method.to_s }.first if klass.present?                # get the column from the class
      maxlength = column.limit if defined?(column) && column.respond_to?(:limit)                            # set the maxlength to the limit for the column if it exists

      options.reverse_merge!( maxlength: maxlength ) if defined?(maxlength)                                 # merge the maxlength option in with the rest

      old_text_field.bind(self).(object_name, method, options)                                              # pass it up to the original text_field method
    end
  end
end
于 2012-09-06T02:02:54.803 に答える