Używam Rails 3.2.0.rc2 . Mam plik Model
, w którym mam statyczny, Array
który oferuję za pośrednictwem formularza, tak aby użytkownicy mogli wybrać podzbiór Array
i zapisać swój wybór w bazie danych, przechowywanej w jednej kolumnie w formacie Model
. Użyłem serializacji w kolumnie bazy danych, która przechowuje, Array
a Railsy poprawnie konwertują wybory użytkowników na Yaml (iz powrotem na tablicę podczas czytania tej kolumny). Do zaznaczania używam pola wyboru wielokrotnego.
Mój problem polega na tym, że tak jak obecnie, wszystko działa tak, jak bym się spodziewał, z wyjątkiem tego, że tablica podzbiorów użytkownika zawsze ma pusty pierwszy element, gdy jest wysyłana na serwer.
To nie jest wielka sprawa i mógłbym napisać kod, który to wyciął po fakcie, ale czuję, że robię jakiś błąd składniowy, ponieważ nie wydaje mi się, że domyślne zachowanie Railsów celowo dodaj ten pusty element bez powodu. Musiałem coś przeoczyć lub zapomnieć o wyłączeniu jakiegoś ustawienia. Proszę, pomóż mi zrozumieć, czego mi brakuje (lub wskaż mi dobrą dokumentację, która opisuje to z większą głębią niż to, co udało mi się znaleźć na intertubach).
Tabela bazy danych MySQL „modele”:
- zawiera kolumnę o nazwie,
subset_array
która jest polem TEKST
Model klasy zawiera następujące ustawienia:
serialize :subset_array
ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]
Formularz do edycji modeli zawiera następującą opcję wprowadzania danych:
f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array
PUT na serwer z klienta wygląda mniej więcej tak:
- zakładając, że wybrane są tylko value1 i value3
"model" => { "subset_array" => ["", value1, value3] }
Aktualizacja bazy danych wygląda następująco:
UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'
Jak widać, ten dodatkowy, pusty element tablicy jest wysyłany i ustawiany w bazie danych. Jak się tego pozbyć? Czy brakuje mi parametru w moim f.select
wywołaniu?
Wielkie dzięki :)
EDYCJA : To jest kod HTML wygenerowany z f.select
instrukcji. Wygląda na to, że generowane są ukryte dane wejściowe, które mogą być przyczyną mojego problemu? Dlaczego tak jest?
<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>
źródło
f.select
generowany fragment kodu HTML ? Czy to zachowanie występuje nawet podczas tworzenia, czy tylko podczas aktualizacji?f.select
include_hidden: false
. Po usunięciu wszystkich wartości z pola wyboru, idiomatycznymodel.update(something_params)
nie uwzględni tego pola. TL; DR nie będziesz w stanie uczynić tego pola pustym.Odpowiedzi:
Ukryte pole jest przyczyną problemu. Ale jest z dobrego powodu: gdy wszystkie wartości są odznaczone, nadal otrzymujesz parametr subset_array. Z dokumentów Rails (być może będziesz musiał przewinąć w prawo, aby zobaczyć to wszystko):
# 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.
EDYCJA: Ostatni akapit sugeruje, że nie powinieneś widzieć pustego w przypadku, gdy coś jest zaznaczone, ale myślę, że jest źle. Osoba, która zobowiązała się do tego w Railsach (patrz https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885 ), próbuje zrobić tę samą sztuczkę, której Rails używa dla pól wyboru (jak wspomniano tutaj: https://github.com / rails / rails / pull / 1552 ), ale nie sądzę, że może to działać dla pola wielokrotnego wyboru, ponieważ parametry przesyłane w tym przypadku tworzą tablicę, a więc żadna wartość nie jest ignorowana.
Więc mam wrażenie, że to błąd.
źródło
W Rails 4:
Będziesz mógł przejść
:include_hidden
opcję. https://github.com/rails/rails/pull/5414/filesNa razie szybkie rozwiązanie: możesz teraz użyć w swoim modelu:
before_validation do |model| model.subset_array.reject!(&:blank?) if model.subset_array end
Spowoduje to po prostu usunięcie wszystkich pustych wartości na poziomie modelu.
źródło
W Railsach 4+ ustaw: include_hidden na select_tag na false
<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>
źródło
Innym szybkim rozwiązaniem jest użycie tego filtra kontrolera:
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
W ten sposób można go ponownie wykorzystać w kontrolerach bez dotykania warstwy modelu.
źródło
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
Aby usunąć puste wartości:
def myfield=(value) value.reject!(&:blank?) write_attribute(:myfield, value) end
źródło
W kontrolerze:
arr = arr.delete_if { |x| x.empty? }
źródło
Naprawiłem to używając
params[:review][:staff_ids].delete("")
w kontrolerze przed aktualizacją.Według mnie:
= form_for @review do |f| = f.collection_select :staff_ids, @business.staff, :id, :full_name, {}, {multiple:true} = f.submit 'Submit Review'
W moim kontrolerze:
class ReviewsController < ApplicationController def create .... params[:review][:staff_ids].delete("") @review.update_attribute(:staff_ids, params[:review][:staff_ids].join(",")) .... end end
źródło
Uczyniłem to, pisząc w części Javascript strony:
$("#model_subset_array").val( <%= @model.subset_array %> );
Mój wygląda bardziej następująco:
$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );
Nie jestem pewien, czy to przyniesie mi ból głowy w przyszłości, ale teraz działa dobrze.
źródło
Użyj jQuery:
$('select option:empty').remove();
Możliwość usunięcia pustych opcji z listy rozwijanej.
źródło