Почему первый элемент всегда пуст в моем Rails multi-select, используя встроенный массив?
Я использую Rails 3.2.0.rc2. У меня есть Model
, в котором у меня есть статический Array
, который я предлагаю в форме, так что пользователи могут выбрать подмножество Array
и сохранить их выбор в базе данных, хранящейся в одной столбца в Model
. Я использовал сериализацию в столбце базы данных, в котором хранится Array
, а Rails правильно преобразует выбор пользователей в Ямл (и обратно в массив при чтении этой колонки). Я использую ввод формы с несколькими выборами для выбора.
Моя проблема в том, что, как я ее сейчас, все работает так, как я ожидал бы, за исключением того, что у массива подмножества пользователей всегда есть пустой первый элемент, когда он отправляется на сервер.
Это не большая проблема, и я мог написать код, чтобы вырезать это после факта, но мне кажется, что я просто делаю какую-то синтаксическую ошибку, поскольку мне не кажется, что Rails по умолчанию поведение преднамеренно добавит этот пустой элемент без какой-либо причины. Я, должно быть, что-то пропустил или забыл отключить какую-то настройку. Пожалуйста, помогите мне понять, что мне не хватает (или указать мне на хорошую документацию, которая описывает это с большей глубиной, чем то, что я смог найти на межтрубках).
Таблица базы данных MySQL:
- содержит столбец с именем
subset_array
, который является полем TEXT
Модель класса включает следующие настройки:
-
serialize :subset_array
-
ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]
Форма для редактирования Модели включают в себя следующую опцию ввода:
-
f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array
PUT для сервера от клиента выглядит примерно так:
- если выбраны только значения1 и значение3
-
"model" => { "subset_array" => ["", value1, value3] }
Обновление базы данных выглядит следующим образом:
-
UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'
Как вы можете видеть, этот лишний, пустой элемент в массиве отправляется и устанавливается в базе данных. Как я могу избавиться от этого? Есть ли параметр, отсутствующий в моем вызове f.select
?
Большое спасибо!)
EDIT. Это сгенерированный код HTML из инструкции f.select
. Похоже, что создается скрытый ввод, который может быть причиной моей проблемы? Почему это?
<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>
Ответы
Ответ 1
Скрытое поле - вот что вызывает проблему. Но он существует по уважительной причине: когда все значения отменены, вы все равно получаете параметр 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), но я не думаю, что он может работать для множественного окна выбора, потому что переданные параметры формируют массив в этом случае и поэтому никакое значение не игнорируется.
Так что я чувствую, что это ошибка.
Ответ 2
В 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
Это просто удалит все пустые значения на уровне модели.
Ответ 3
Еще одно быстрое решение - использовать этот фильтр контроллера:
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
Этот способ можно повторно использовать через контроллеры, не касаясь слоя модели.
Ответ 4
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
Гоча
В спецификации HTML указано, что флажки, отмеченные флажками, или выбор не увенчались успехом, и, таким образом, веб-браузеры не отправляют их. К сожалению, это вводит getcha: если у модели счета есть платный флаг и в форме, которая редактирует платный счет, пользователь отключает его флажок, без оплаты параметр отправлен. Таким образом, любая ипостась массового присвоения, такая как
@invoice.update(params [: invoice]) не будет обновлять флаг.
Чтобы предотвратить это, помощник создает вспомогательное скрытое поле до самый флажок. Скрытое поле имеет одно и то же имя и его атрибуты имитируют неконтролируемый флажок.
Таким образом, клиент отправляет только скрытое поле (представляющее флажок снят) или оба поля. Поскольку HTML спецификация говорит, что пары ключ/значение должны быть отправлены в том же порядке они появляются в форме, а извлечение параметров получает последнее появление любого повторяющегося ключа в строке запроса, который работает для обычные формы.
Чтобы удалить пустые значения:
def myfield=(value)
value.reject!(&:blank?)
write_attribute(:myfield, value)
end
Ответ 5
В контроллере:
arr = arr.delete_if { |x| x.empty? }
Ответ 6
Я заставляю его работать, написав это в части Javascript на странице:
$("#model_subset_array").val( <%= @model.subset_array %> );
Моя больше похожа на следующее:
$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );
Не уверен, что это вызовет у меня головную боль в будущем, но теперь все работает нормально.
Ответ 7
Я исправил его с помощью 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
Ответ 8
Использовать jQuery:
$('select option:empty').remove();
Возможность удаления пустых опций из раскрывающегося списка.