埋め込み配列を使用して、Railsの複数選択で最初の要素が常に空白になるのはなぜですか?


84

Rails3.2.0.rc2を使用しています。私が持ってModelいる私は、静的持って、Array私は、ユーザーがのサブセットを選択できるように、フォームをアップ提供していますArrayし、1つの列に格納されているデータベースにその選択を保存しますModelArrayRailsがユーザーの選択をYamlに正しく変換している(そしてその列を読み取るときに配列に戻る)データベース列でserializeを使用しました。複数選択フォーム入力を使用して選択を行っています。

私の問題は、現在の方法では、サーバーに送信されるときにユーザーのサブセット配列の最初の要素が常に空白になることを除いて、すべてが期待どおりに機能することです。

これは大したことではなく、事後にそれを切り取るコードを書くことはできますが、デフォルトのRailsの動作が意図的に行われるとは思えないため、何らかの構文エラーを起こしているように感じます。なんらかの理由なしにこの空白の要素を追加します。何かを見逃したか、ある種の設定を無効にするのを忘れたに違いありません。私が欠けているものを理解するのを手伝ってください(または、私がインターチューブで見つけたものよりも詳細にこれを説明しているいくつかの良いドキュメントを教えてください)。

MySQLデータベーステーブル 'モデル':

  • subset_arrayTEXTフィールドであるという名前の列が含まれています

クラスモデルには、次の設定が含まれています。

  • serialize :subset_array
  • ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]

モデルを編集するためのフォームには、次の入力オプションが含まれています。

  • f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array

クライアントからサーバーへのPUTは次のようになります。

  • value1とvalue3のみが選択されていると仮定します
  • "model" => { "subset_array" => ["", value1, value3] }

データベースの更新は次のようになります。

  • UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'

ご覧のとおり、送信されてデータベースに設定されている配列には、この余分な空白の要素があります。どうすればそれを取り除くことができますか?f.select通話に欠けているパラメータはありますか?

どうもありがとうございました:)

編集:これは、f.selectステートメントから生成されたHTMLコードです。私の問題の原因である可能性のある隠された入力が生成されているように見えますか?なぜそこにあるのですか?

<input name="model[subset_array][]" type="hidden" value>
<select id="model_subset_array" multiple="multiple" name="model[subset_array][]" selected="selected">
    <option value="value1" selected="selected">Value1</option>
    <option value="value2">Value2</option>
    <option value="value3" selected="selected">Value3</option>
    <option...>...</option>
</select>

f.select生成されているHTMLスニペットを投稿できますか?また、この動作は作成時でも発生しますか、それとも更新時だけですか?
マイクA.

から生成された出力HTMLマークアップの編集を追加f.select
robmclarty 2012年

@
mike-

私が使用していたブラウザが問題の一部であるのではないかと思いました。それは、selectタグと同じ名前の非表示の入力タグの意味をどのように解釈して表現するかです。そこで、Chrome、Safari、Firefox、Operaでアプリを試してみたところ、それぞれ同じ結果が得られました。
robmclarty 2012年

1
使用include_hidden: falseするすべてのソリューションには落とし穴が付いていることに注意してください。選択ボックスからすべての値を削除すると、慣用句model.update(something_params)にはそのフィールドが含まれなくなります。TL; DRフィールドを空にすることはできません。
Damon Aw 2014年

回答:


51

隠しフィールドが問題の原因です。ただし、これには正当な理由があります。すべての値の選択を解除しても、subset_arrayパラメーターを受け取ります。Railsのドキュメントから(これをすべて表示するには、右にスクロールする必要がある場合があります):

  # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
  # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
  # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
  # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
  # any mass-assignment idiom like
  #
  #   @user.update_attributes(params[:user])
  #
  # wouldn't update roles.
  #
  # To prevent this the helper generates an auxiliary hidden field before
  # every multiple select. The hidden field has the same name as multiple select and blank value.
  #
  # This way, the client either sends only the hidden field (representing
  # the deselected multiple select box), or both fields. Since the HTML specification
  # says key/value pairs have to be sent in the same order they appear in the
  # form, and parameters extraction gets the last occurrence of any repeated
  # key in the query string, that works for ordinary forms.

編集:最後の段落は、何かが選択された場合に空のものが表示されるべきではないことを示唆していますが、それは間違っていると思います。Railsにこのコミットを行った人(https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885を参照)は、Railsがチェックボックスに使用するのと同じトリックを実行しようとしています(ここで説明:https//github.com / rails / rails / pull / 1552)ですが、この場合、送信されるパラメーターが配列を形成し、値が無視されないため、複数の選択ボックスでは機能しないと思います。

ですから、これはバグだと思います。


1
問題を適切に処理する方法を理解しようとしているときに、問題を示すサンプルアプリを作成しまし:P
robmclarty 2012年

1
バグは必ずしもRailsにあるとは限らないが、この特定の要素機能の仕様と実装があいまいであるように感じます。どのユーザエージェントは、状態の変化を表現する必要があり、新たに空の空の(または非選択)フォーム要素が考慮される場合、フォームプロセッサにないことが成功したコントロール、したがってないフォーム内容に提出しますか?
robmclarty 2012年

それで、これがバグである場合、それはRails課題追跡システムに文書化されていますか?
bmihelac 2012

69

Rails 4:

:include_hiddenオプションを渡すことができます。https://github.com/rails/rails/pull/5414/files

今のところ簡単な修正として:モデルで今すぐ使用できます:

before_validation do |model|
  model.subset_array.reject!(&:blank?) if model.subset_array
end

これにより、モデルレベルですべての空白値が削除されます。


ボグダンに感謝します。これは、この問題を回避するためにアプリに実装するようなものだと思います。これは、ユーザーエージェントのHTML仕様の実装などの問題を回避しようとするよりもはるかに簡単です。
robmclarty 2012年

Railsのコアチームメンバーに懸念事項を伝えてください。彼らはパッチをレビューして受け入れ、結果として生じる問題に対して責任を負う権限があります。
ボグダングシエフ2013年

ボグダン、複数が真の場合、隠しフィールドをオフにする方法はありますか?これは、3.2にアップグレードするときに、私のアプリのかなりの部分を壊します。Railsの魔法が余分な空の値を追加するため、コントローラー内のものをクリーンアップする必要があるという事実は本当に好きではありません。
taelor 2013年


2
@Donatoにはinclude_hiddenをfalseに設定する必要があります(include_hidden:false)
Florian Widtmann 2015

14

Rails 4以降では、select_tagの:include_hiddenをfalseに設定します

<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>

これは断然最も簡単な答えです!ありがとう!
ウィリアムハンプシャー

11

もう1つの簡単な修正は、このコントローラーフィルターを使用することです。

def clean_select_multiple_params hash = params
  hash.each do |k, v|
    case v
    when Array then v.reject!(&:blank?)
    when Hash then clean_select_multiple_params(v)
    end
  end
end

この方法は、モデルレイヤーに触れることなく、コントローラー間で再利用できます。


ありがとう。モデルでやりたくない場合に備えて、これをトリックのバッグに追加しています;)
robmclarty 2012

5

http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box

ガッチャ

HTML仕様では、チェックされていないチェックボックスまたは選択は成功しないと記載されているため、Webブラウザはそれらを送信しません。残念ながら、これには落とし穴があります。請求書モデルに有料フラグがあり、有料請求書を編集するフォームでユーザーがチェックボックスをオフにすると、有料パラメーターは送信されません。したがって、次のような大量割り当てのイディオム

@ invoice.update(params [:invoice])はフラグを更新しませんでした。

これを防ぐために、ヘルパーはチェックボックスの前に補助の非表示フィールドを生成します。非表示のフィールドは同じ名前であり、その属性はチェックされていないチェックボックスを模倣しています。

このように、クライアントは非表示フィールドのみを送信するか(チェックボックスがオフになっていることを表す)、または両方のフィールドを送信します。HTML仕様では、キーと値のペアはフォームに表示されるのと同じ順序で送信する必要があると規定されているため、パラメーター抽出では、クエリ文字列内で繰り返されるキーが最後に出現します。これは通常のフォームで機能します。

空白の値を削除するには:

  def myfield=(value)
    value.reject!(&:blank?)
    write_attribute(:myfield, value)
  end


0

params[:review][:staff_ids].delete("")更新前にコントローラーのを使用して修正しました。

私からしてみれば:

= form_for @review do |f|
  = f.collection_select :staff_ids, @business.staff, :id, :full_name, {}, {multiple:true}
= f.submit 'Submit Review'

私のコントローラーでは:

class ReviewsController < ApplicationController
  def create
  ....
    params[:review][:staff_ids].delete("")
    @review.update_attribute(:staff_ids, params[:review][:staff_ids].join(","))
  ....
  end
end

0

ページのJavascript部分にこれを書くことでそれを機能させます:

$("#model_subset_array").val( <%= @model.subset_array %> );

鉱山は次のように見えます:

$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );

これが将来私に頭痛の種になるかどうかはわかりませんが、今では問題なく動作します。


-3

jQueryを使用する:

$('select option:empty').remove(); 

ドロップダウンから空白のオプションを削除するオプション。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.