Ich benutze Rails 3.2.0.rc2. Ich habe ein Model
, in dem ich ein statisches Array
habe, das ich über ein Formular anbiete, sodass Benutzer eine Teilmenge von Array
auswählen und ihre speichern können Auswahl in die Datenbank, gespeichert in einer einzelnen Spalte in Model
. Ich habe serialize für die Datenbankspalte verwendet, in der Array
gespeichert ist, und Rails konvertiert die Auswahl der Benutzer korrekt in Yaml (und zurück in ein Array, wenn diese Spalte gelesen wird). Ich verwende eine Mehrfachauswahl-Formulareingabe, um eine Auswahl zu treffen.
Mein Problem ist, dass, wie ich es derzeit habe, alles so funktioniert, wie ich es erwarten würde, außer dass das Subset-Array des Benutzers immer ein leeres erstes Element enthält, wenn es an den Server gesendet wird.
Dies ist keine große Sache, und ich könnte Code schreiben, um das nachträglich auszuschalten, aber ich habe das Gefühl, dass ich nur eine Art syntaktischen Fehler mache, da es mir nicht so vorkommt, als wäre es der Standard Rails ] Verhalten würde absichtlich dieses leere Element ohne Grund hinzufügen. Ich muss etwas verpasst oder vergessen haben, eine Einstellung zu deaktivieren. Bitte helfen Sie mir zu verstehen, was ich vermisse (oder weisen Sie mich auf eine gute Dokumentation hin, die dies genauer beschreibt als das, was ich auf den Zwischenrohren finden konnte).
subset_array
, die ein TEXT-Feld istserialize :subset_array
ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]
f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array
"model" => { "subset_array" => ["", value1, value3] }
UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'
Wie Sie sehen, wird dieses zusätzliche, leere Element im Array gesendet und in der Datenbank festgelegt. Wie werde ich das los? Gibt es einen Parameter, den ich in meinem f.select
- Aufruf vermisse?
Vielen Dank :)
EDIT : Dies ist der generierte HTML-Code aus der Anweisung f.select
. Es sieht so aus, als ob eine versteckte Eingabe generiert wird, die die Ursache meines Problems sein könnte. Warum ist das da?
<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>
Das versteckte Feld ist das, was das Problem verursacht. Das hat aber einen guten Grund: Wenn alle Werte abgewählt sind, erhalten Sie immer noch einen subset_array-Parameter. In den Rails - Dokumenten (Sie müssen möglicherweise nach rechts scrollen, um all dies zu sehen):
# 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: Der letzte Absatz legt nahe, dass Sie den leeren nicht sehen sollten, wenn etwas ausgewählt ist, aber ich denke, dass es falsch ist. Die Person, die dieses Commit für Rails ausgeführt hat (siehe https: //github.com/Rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885 ), versucht den gleichen Trick wie Rails wird für Kontrollkästchen verwendet (wie hier erwähnt: https: //github.com/Rails/rails/pull/1552 ), aber ich glaube nicht, dass es für ein Mehrfachauswahlfeld funktionieren kann da die übermittelten Parameter in diesem Fall ein Array bilden und somit kein Wert ignoriert wird.
Mein Gefühl ist also, dass dies ein Fehler ist.
In Rails 4:
Sie werden in der Lage sein, :include_hidden
Möglichkeit. https://github.com/Rails/rails/pull/5414/files
Als schnelle Lösung fürs Erste: Sie können jetzt in Ihrem Modell Folgendes verwenden:
before_validation do |model|
model.subset_array.reject!(&:blank?) if model.subset_array
end
Dadurch werden nur alle leeren Werte auf Modellebene gelöscht.
Eine weitere schnelle Lösung besteht darin, diesen Controller-Filter zu verwenden:
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
Auf diese Weise können Sie den gesamten Controller verwenden, ohne die Modellebene zu berühren.
In Rails 4+ setze: include_hidden on select_tag auf false
<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
Gotcha
Die HTML-Spezifikation besagt, dass nicht aktivierte Kontrollkästchen oder Auswahlmöglichkeiten nicht erfolgreich sind und daher von Webbrowsern nicht gesendet werden. Leider führt dies zu einem "gotcha": Wenn ein Rechnungsmodell ein "paid" -Flag aufweist und der Benutzer in dem Formular, in dem eine bezahlte Rechnung bearbeitet wird, das Kontrollkästchen deaktiviert, wird kein bezahlter Parameter gesendet. Also, wie jede Massenzuweisungssprache
@ rechnung.update (params [: rechnung]) würde das Flag nicht aktualisieren.
Um dies zu verhindern, generiert der Helfer vor dem Kontrollkästchen ein verstecktes Hilfsfeld. Das ausgeblendete Feld hat denselben Namen und seine Attribute ahmen ein nicht angehaktes Kontrollkästchen nach.
Auf diese Weise sendet der Client entweder nur das ausgeblendete Feld (das das Kontrollkästchen darstellt, ist deaktiviert) oder beide Felder. Da in der HTML-Spezifikation angegeben ist, dass Schlüssel/Wert-Paare in derselben Reihenfolge gesendet werden müssen, in der sie im Formular erscheinen, wird bei der Parameterextraktion das letzte Vorkommen eines wiederholten Schlüssels in der Abfragezeichenfolge abgerufen, das für normale Formulare funktioniert.
So entfernen Sie leere Werte:
def myfield=(value)
value.reject!(&:blank?)
write_attribute(:myfield, value)
end
In der Steuerung:
arr = arr.delete_if { |x| x.empty? }
Ich habe es vor dem Update mit der Funktion params[:review][:staff_ids].delete("")
im Controller behoben.
Meiner Ansicht nach:
= form_for @review do |f|
= f.collection_select :staff_ids, @business.staff, :id, :full_name, {}, {multiple:true}
= f.submit 'Submit Review'
In meinem Controller:
class ReviewsController < ApplicationController
def create
....
params[:review][:staff_ids].delete("")
@review.update_attribute(:staff_ids, params[:review][:staff_ids].join(","))
....
end
end
Ich lasse es funktionieren, indem ich dies in den Javascript-Teil der Seite schreibe:
$("#model_subset_array").val( <%= @model.subset_array %> );
Meins sieht ungefähr so aus:
$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );
Ich bin mir nicht sicher, ob mir dies in Zukunft Kopfschmerzen bereiten wird, aber jetzt funktioniert es einwandfrei.