wake-up-neo.com

Warum ist das erste Element in meiner Rails Mehrfachauswahl unter Verwendung eines eingebetteten Arrays immer leer?

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).

MySQL-Datenbanktabelle 'Modelle':

  • enthält eine Spalte mit dem Namen subset_array, die ein TEXT-Feld ist

Das Klassenmodell enthält die folgenden Einstellungen:

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

Das Formular zum Bearbeiten von Modellen enthält die folgende Eingabemöglichkeit:

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

PUT vom Client zum Server sieht ungefähr so ​​aus:

  • vorausgesetzt, nur value1 und value3 sind ausgewählt
  • "model" => { "subset_array" => ["", value1, value3] }

Das Datenbank-Update sieht folgendermaßen aus:

  • 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>
80
robmclarty

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.

51
Mike A.

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.

67
Bogdan Gusiev

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.

11
Max

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

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
5
devishot

In der Steuerung:

arr = arr.delete_if { |x| x.empty? }
3
Mauro

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
0
Bruno

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.

0
imaginabit