Frage Warum ist das erste Element in meinem Rails-Multiselect immer leer, wenn ein eingebettetes Array verwendet wird?


Ich benutze Schienen 3.2.0.rc2. Ich habe ein Modelin dem ich eine statische habe Array welche ich über ein Formular anbiete, so dass Benutzer eine Teilmenge davon auswählen können Array und speichern Sie ihre Auswahl in der Datenbank, in einer einzigen Spalte in gespeichert Model. Ich habe serialize auf der Datenbankspalte verwendet, die das speichert Array und Rails konvertiert die Auswahlen der Benutzer korrekt in Yaml (und zurück in ein Array beim Lesen dieser Spalte). Ich verwende eine Mehrfachauswahl-Formulareingabe, um eine Auswahl zu treffen.

Mein Problem ist, dass, so 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 hat, wenn es an den Server gesendet wird.

Das ist keine große Sache, und ich könnte Code schreiben, um das hinterher zu eliminieren, aber ich habe das Gefühl, dass ich gerade einen syntaktischen Fehler mache, da es mir nicht scheint, dass das Standardverhalten von Rails absichtlich wäre füge dieses leere Element ohne Grund hinzu. Ich muss etwas verpasst haben oder vergessen, irgendeine Art von Einstellung zu deaktivieren. Bitte helfen Sie mir zu verstehen, was mir fehlt (oder weisen Sie auf eine gute Dokumentation hin, die das tiefer beschreibt als das, was ich auf den Intertubes gefunden habe).

MySQL Datenbank Tabelle 'models':

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

Das Klassenmodell enthält die folgenden Einstellungen:

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

Formular zum Bearbeiten Modelle enthält die folgende Eingabemöglichkeit:

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

PUT to Server vom Client sieht in etwa so aus:

  • Angenommen, nur Wert1 und Wert3 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 können, gibt es dieses zusätzliche, leere Element im Array, das gesendet und in der Datenbank festgelegt wird. Wie werde ich das los? Gibt es einen Parameter, der mir fehlt? f.select Anruf?

Viel Dankeschön :)

BEARBEITEN: Dies ist der generierte HTML-Code von der f.select Erklärung. Es sieht so aus, als würde eine versteckte Eingabe generiert, 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>

76
2018-01-19 16:02


Ursprung


Antworten:


Das versteckte Feld verursacht das Problem. Aber es gibt einen guten Grund: Wenn alle Werte abgewählt sind, erhalten Sie immer noch einen Parameter subset_array. Aus den Rails-Dokumenten (möglicherweise müssen Sie 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.

BEARBEITEN: Der letzte Abschnitt schlägt vor, dass Sie den leeren nicht sehen sollten, wenn etwas ausgewählt wird, aber ich denke, dass es falsch ist. Die Person, die dies zu Rails gemacht hat (siehe https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885) versucht den gleichen Trick, den Rails für Checkboxen verwendet (wie hier erwähnt): https://github.com/rails/rails/pull/1552), aber ich denke nicht, dass es für ein Mehrfachauswahlfeld funktionieren kann, da die übermittelten Parameter in diesem Fall ein Array bilden und daher kein Wert ignoriert wird.

Mein Gefühl ist, dass dies ein Fehler ist.


51
2018-01-19 20:50



In Schienen 4:

Du wirst bestehen können :include_hidden Möglichkeit. https://github.com/rails/rails/pull/5414/files

Als schnelle Lösung für den Moment: Sie können sofort in Ihrem Modell verwenden:

before_validation do |model|
  model.subset_array.reject!(&:blank?) if model.subset_array
end

Dadurch werden einfach alle leeren Werte auf Modellebene gelöscht.


63
2018-01-20 09:27



Eine weitere schnelle Lösung ist die Verwendung dieses Controller-Filters:

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

Dieser Weg kann über Controller hinweg genutzt werden, ohne die Modellschicht zu berühren.


10
2018-01-31 19:21



http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box

Erwischt

Die HTML-Spezifikation sagt, dass nicht markierte Kontrollkästchen oder Auswahlmöglichkeiten nicht erfolgreich sind.   und deshalb senden Webbrowser sie nicht. Leider führt dies ein   eine Frage: Wenn ein Rechnungsmodell eine bezahlte Flagge hat, und in der Form   bearbeitet eine bezahlte Rechnung der Benutzer deaktiviert sein Kontrollkästchen, nicht bezahlt   Parameter wird gesendet. Also, jede Massen-Idiom wie

@ invoice.update (params [: invoice]) würde das Flag nicht aktualisieren.

Um dies zu verhindern, generiert der Helfer zuvor ein verstecktes Hilfsfeld   das sehr Kontrollkästchen. Das versteckte Feld hat denselben Namen und seine   Attribute imitieren ein nicht markiertes Kontrollkästchen.


5
2018-04-03 05:25



In der Steuerung:

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

3
2018-05-18 16:34



In Rails 4+ set: include_hidden auf select_tag auf false

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

3
2018-05-01 15:07



Ich habe es mit dem params[:review][:staff_ids].delete("") in der Steuerung vor dem Update.

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
2017-10-14 17:30