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


84

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>

f.select생성 중인 HTML 스 니펫을 게시 할 수 있습니까? 또한이 동작은 생성시에도 발생합니까, 아니면 업데이트시에만 발생합니까?
Mike A.

생성 된 출력 HTML 마크 업의 편집 추가f.select
robmclarty

@ mike-a 생성 및 업데이트에 대해 동일한 동작 확인
robmclarty

내가 사용하고있는 브라우저가 문제의 일부일 수 있는지 궁금했습니다. 선택 태그와 이름이 같은 숨겨진 입력 태그의 의미를 해석하고 표현하는 방법입니다. 그래서 크롬, 사파리, 파이어 폭스, 오페라에서 내 앱을 사용해 보았는데 각각 같은 결과가 나왔습니다.
robmclarty

1
사용하는 모든 솔루션 include_hidden: false에는 문제가 있습니다. 선택 상자에서 모든 값을 제거하면 관용어에 model.update(something_params)해당 필드가 포함되지 않습니다. TL; DR 당신은 필드를 비울 수 없습니다.
Damon Aw

답변:


51

숨겨진 필드가 문제의 원인입니다. 하지만 그럴만 한 이유가 있습니다. 모든 값이 선택 해제 되어도 하위 집합 _ 배열 매개 변수가 계속 수신됩니다. 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

1
버그가 반드시 Rails에있는 것은 아니지만이 특정 요소 기능에 대한 모호한 사양 및 구현에있는 것 같습니다. 의 방법은 사용자 에이전트는 상태의 변화를 표현해야 새로 비워 빈 (또는 비 선택) 폼 요소를 고려하는 경우 양식 프로세서에 하지성공적으로 제어 함으로써 되지 형태의 내용으로 제출?
robmclarty

그렇다면 이것이 버그라면 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

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


감사합니다 Bogdan. 나는 이것이 문제를 해결하기 위해 내 앱에서 구현할 유형이라고 생각합니다. 이것은 사용자 에이전트의 HTML 사양 구현 등의 문제를 해결하는 것보다 훨씬 쉽습니다.
robmclarty

Rails 핵심 팀원에게 우려 사항을 전달하십시오. 그들은 패치를 검토하고 승인 할 권한이 있으며 그에 따른 문제에 대한 책임을집니다.
Bogdan Gusiev 2013 년

보그 단, 어쨌든 다중이 사실이라면 숨겨진 필드를 끌 수 있습니까? 이것은 3.2로 업그레이드 할 때 내 앱의 많은 부분을 망가 뜨립니다. Rails 매직이 빈 값을 추가하기 때문에 컨트롤러에서 물건을 정리해야한다는 사실이 정말 마음에 들지 않습니다.
taelor

레일 4에서 upcomming 정보와 내 대답을 업데이트
보그 Gusiev에게

2
false로 include_hidden 설정해야합니다 @Donato (include_hidden : 거짓)
플로리안 Widtmann

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

이것이 가장 간단한 대답입니다! 감사!
William Hampshire

11

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

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

5

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


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.