[html] 임베디드 배열을 사용하여 Rails 다중 선택에서 첫 번째 요소가 항상 비어있는 이유는 무엇입니까?

Rails 3.2.0.rc2를 사용 하고 있습니다. 나는를 가지고 Model있는 나는 정적이, Array나는 사용자의 하위 집합을 선택할 수 있도록 형태를 통해 제공하고있어 Array및 단일 열에 저장된 데이터베이스, 자신의 선택 사항을 저장을 Model. 를 저장하는 데이터베이스 열에서 직렬화를 사용 Array했으며 Rails가 사용자의 선택을 Yaml로 올바르게 변환하고 해당 열을 읽을 때 배열로 다시 변환합니다. 다중 선택 양식 입력을 사용하여 선택하고 있습니다.

내 문제는 내가 현재 가지고있는 방식으로, 사용자의 하위 집합 배열이 서버로 전송 될 때 항상 빈 첫 번째 요소가 있다는 점을 제외하고는 모든 것이 예상대로 작동한다는 것입니다.

이것은 큰 문제가 아니며, 사실 후에 그것을 잘라내는 코드를 작성할 수 있지만, 기본 Rails 동작이 의도적으로 작동하지 않는 것 같아서 일종의 구문 오류를 만드는 것 같습니다. 어떤 이유없이이 빈 요소를 추가합니다. 나는 뭔가를 놓쳤거나 어떤 종류의 설정을 비활성화하는 것을 잊었을 것입니다. 내가 무엇을 놓치고 있는지 이해하도록 도와주세요 (또는 intertubes에서 찾을 수 있었던 것보다 더 깊이 이것을 설명하는 좋은 문서를 알려주세요).

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>



답변

숨겨진 필드가 문제의 원인입니다. 하지만 그럴만 한 이유가 있습니다. 모든 값이 선택 해제 되어도 하위 집합 _ 배열 매개 변수가 계속 수신됩니다. 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 ),하지만이 경우 전송 된 매개 변수가 배열을 형성하므로 값이 무시되지 않기 때문에 다중 선택 상자에서 작동하지 않는다고 생각합니다.

제 느낌은 이것이 버그라는 것입니다.


답변

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

이렇게하면 모델 수준에서 모든 빈 값이 삭제됩니다.


답변

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 } %>


답변

또 다른 빠른 수정은 다음 컨트롤러 필터를 사용하는 것입니다.

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

이 방법은 모델 레이어를 건드리지 않고 컨트롤러에서 재사용 할 수 있습니다.


답변

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

Gotcha

HTML 사양은 선택하지 않은 확인란이나 선택이 성공하지 못하므로 웹 브라우저에서 보내지 않는다고 말합니다. 불행히도 이로 인해 문제가 발생합니다. 송장 모델에 유료 플래그가 있고 유료 송장을 편집하는 양식에서 사용자가 해당 확인란의 선택을 취소하면 유료 매개 변수가 전송되지 않습니다. 따라서 모든 대량 할당 관용구는

@ invoice.update (params [: invoice])는 플래그를 업데이트하지 않습니다.

이를 방지하기 위해 도우미는 바로 확인란 앞에 보조 숨겨진 필드를 생성합니다. 숨겨진 필드는 동일한 이름을 가지며 해당 속성은 선택되지 않은 확인란을 모방합니다.

이러한 방식으로 클라이언트는 숨겨진 필드 만 보내거나 (확인란이 선택되지 않음을 나타냄) 두 필드를 모두 보냅니다. HTML 사양에 따르면 키 / 값 쌍은 양식에 나타나는 것과 동일한 순서로 전송되어야하며 매개 변수 추출은 일반 양식에 대해 작동하는 쿼리 문자열에서 반복되는 키의 마지막 항목을 가져옵니다.

빈 값을 제거하려면 :

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


답변

컨트롤러에서 :

arr = arr.delete_if { |x| x.empty? }


답변

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