Чому перший елемент завжди є порожнім у моєму Rails мульти-виділення, використовуючи вбудований масив?


84

Я використовую Rails 3.2.0.rc2 . У мене є файл Model, в якому я маю статику, Arrayяку пропоную через форму, таку, що користувачі можуть вибрати підмножину Arrayта зберегти свій вибір у базі даних, що зберігається в одному стовпці в Model. Я використовував серіалізацію в стовпці бази даних, який зберігає, Arrayа Rails правильно перетворює виділення користувачів у Yaml (і повертається до масиву при читанні цього стовпця). Я використовую введення форми з декількома виборами, щоб робити вибір.

Моя проблема полягає в тому, що, як у мене зараз, все працює так, як я очікував, за винятком того, що масив підмножини користувача завжди має порожній перший елемент, коли він надсилається на сервер.

Це не є великою проблемою, і я міг би написати код, щоб це вирішити, але я відчуваю, що я просто роблю якусь синтаксичну помилку, оскільки мені не здається, що поведінка Rails за замовчуванням навмисно додати цей порожній елемент без будь-якої причини. Напевно, я щось пропустив або забув відключити якусь установку. Будь ласка, допоможіть мені зрозуміти, чого мені не вистачає (або вкажіть мені якусь хорошу документацію, яка описує це з більшою глибиною, ніж те, що мені вдалося знайти на міжтрубках).

"Моделі" таблиці бази даних MySQL:

  • включає стовпець, названий subset_arrayтекстовим полем

Модель класу включає такі налаштування:

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

Форма для редагування моделей включає такий варіант введення:

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

ПУТ на сервер з клієнта виглядає приблизно так:

  • припускаючи, що вибрано лише значення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>

Не могли б ви розмістити фрагмент HTML, який f.selectгенерує? Крім того, така поведінка трапляється навіть під час створення, чи це просто оновлення?
Mike A.

Додано EDIT вихідної розмітки HTML, створеної зf.select
robmclarty

@ mike-a Підтверджена однакова поведінка як для створення, так і для оновлення
robmclarty

Я подумав, чи може браузер, яким я користувався, бути частиною проблеми: як він інтерпретує та виражає значення прихованого вхідного тегу з тим самим іменем, що і тег select. Тож я спробував свій додаток у Chrome, Safari, Firefox та Opera, і кожен з них дав однакові результати.
robmclarty

1
Зверніть увагу, що всі рішення, які використовуються, include_hidden: falseпостачаються із затримкою. Коли ви видалите всі значення з поля вибору, ідіоматичне model.update(something_params)поле не включатиме цього поля. TL; DR ви не зможете заповнити поле порожнім.
Деймон Ав

Відповіді:


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.

EDIT: Останній абзац передбачає, що ви не повинні бачити порожній у випадку, коли щось вибрано, але я думаю, що це неправильно. Людина, яка зробила це зобов'язання для Rails (див. Https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885 ), намагається виконати той самий фокус, який Rails використовує для прапорців (як згадано тут: https://github.com / rails / rails / pull / 1552 ), але я не думаю, що це може працювати для багаторазового поля вибору, оскільки параметри, що надсилаються, утворюють масив у цьому випадку, тому жодне значення не ігнорується.

Тож я відчуваю, що це помилка.


1
Я створив приклад програми, щоб продемонструвати проблему, намагаючись зрозуміти, як правильно з нею впоратися: P
robmclarty

1
Я відчуваю, що помилка не обов’язково в Rails, а в неоднозначній специфікації та реалізації для даного функціоналу елемента. Як агент користувача повинен висловити зміна стану знову спорожнити у вигляді процесор , якщо порожні (або не вибрані елементи форми), вважаються НЕ бути успішним управлінням і , таким чином , НЕ представляються з вмістом форми?
robmclarty

Отже, якщо це помилка, чи це задокументовано у трекері випуску Rails?
bmihelac

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

Доведіть свою стурбованість до основних членів команди. Вони уповноважені переглядати та приймати виправлення та нести відповідальність за наступні проблеми.
Богдан Гусєв

Богдане, чи можна взагалі вимкнути приховане поле, якщо кратна істина? Це розбиває досить багато речей у моєму додатку під час оновлення до 3.2. Мені дуже не подобається той факт, що мені доводиться чистити речі в контролері через магію Rails, додаючи зайві порожні значення.
taelor 05.03.13

Оновіть мою відповідь актуальною інформацією від Rails 4
Богдан Гусєв

2
@Donato ви повинні встановити include_hidden як false (include_hidden: false)
Флоріан Відттман

14

У Rails 4+ встановіть: include_hidden on select_tag до false

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

Це, безумовно, найпростіша відповідь! Дякую!
Вільям Хемпшир

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

Маю

Специфікація 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(); 

Можливість видалити порожні параметри зі спадного меню.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.