From be16111c3438134c9c18ece2d793c89456cf378b Mon Sep 17 00:00:00 2001 From: Nolan J Tait Date: Tue, 2 Apr 2024 12:12:55 -0700 Subject: [PATCH 1/4] Adds multiple and include blank options to select --- lib/superform/rails.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/superform/rails.rb b/lib/superform/rails.rb index b93ede9..d26b7e0 100644 --- a/lib/superform/rails.rb +++ b/lib/superform/rails.rb @@ -299,16 +299,30 @@ def template(&content) end class SelectField < FieldComponent - def initialize(*, collection: [], **, &) + def initialize( + *, + collection: [], + multiple: false, + include_blank: false, + **, + & + ) super(*, **, &) @collection = collection + @multiple = multiple + @include_blank = include_blank end def template(&options) + input(type: "hidden", name: dom.name, value: "") if @multiple + if block_given? select(**attributes, &options) else - select(**attributes) { options(*@collection) } + select(**attributes) do + blank_option if @include_blank + options(*@collection) + end end end From e8c97e273ea83c33ff01cef19334193c2fb92120 Mon Sep 17 00:00:00 2001 From: Nolan J Tait Date: Tue, 2 Apr 2024 12:13:38 -0700 Subject: [PATCH 2/4] Adds overriding select name if multiple --- lib/superform/rails.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/superform/rails.rb b/lib/superform/rails.rb index d26b7e0..468b194 100644 --- a/lib/superform/rails.rb +++ b/lib/superform/rails.rb @@ -345,6 +345,13 @@ def false_option(&) end protected + + def field_attributes + super.merge( + name: @multiple ? "#{dom.name}[]" : dom.name, + ) + end + def map_options(collection) OptionMapper.new(collection) end From 155824e69a6fc6b67d8f4722ac361bbcaa9a0672 Mon Sep 17 00:00:00 2001 From: Nolan J Tait Date: Tue, 2 Apr 2024 12:16:19 -0700 Subject: [PATCH 3/4] Adds multiple to select html attributes --- lib/superform/rails.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/superform/rails.rb b/lib/superform/rails.rb index 468b194..03d118f 100644 --- a/lib/superform/rails.rb +++ b/lib/superform/rails.rb @@ -349,6 +349,7 @@ def false_option(&) def field_attributes super.merge( name: @multiple ? "#{dom.name}[]" : dom.name, + multiple: @multiple ) end From 8a04f5ae1df08198331ff9b076232cbe071a8fbb Mon Sep 17 00:00:00 2001 From: Nolan J Tait Date: Sun, 28 Apr 2024 02:06:21 -0700 Subject: [PATCH 4/4] Moves multiselect field to its own component --- lib/superform/rails.rb | 49 +++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/superform/rails.rb b/lib/superform/rails.rb index 03d118f..844fc5c 100644 --- a/lib/superform/rails.rb +++ b/lib/superform/rails.rb @@ -69,6 +69,10 @@ def select(*collection, **attributes, &) Components::SelectField.new(self, attributes: attributes, collection: collection, &) end + def multiple_select(*collection, **attributes, &) + Components::MultipleSelectField.new(self, attributes: attributes, collection: collection, &) + end + def title key.to_s.titleize end @@ -299,30 +303,16 @@ def template(&content) end class SelectField < FieldComponent - def initialize( - *, - collection: [], - multiple: false, - include_blank: false, - **, - & - ) + def initialize(*, collection: [], **, &) super(*, **, &) @collection = collection - @multiple = multiple - @include_blank = include_blank end def template(&options) - input(type: "hidden", name: dom.name, value: "") if @multiple - if block_given? select(**attributes, &options) else - select(**attributes) do - blank_option if @include_blank - options(*@collection) - end + select(**attributes) { options(*@collection) } end end @@ -344,18 +334,33 @@ def false_option(&) option(selected: field.value == false, value: false.to_s, &) end + protected + + def map_options(collection) + OptionMapper.new(collection) + end + end + + class MultipleSelectField < SelectField + def template(...) + # 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 a 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. + input(type: "hidden", name: dom.name, value: "") + super(...) + end + protected def field_attributes super.merge( - name: @multiple ? "#{dom.name}[]" : dom.name, - multiple: @multiple + name: "#{dom.name}[]", + multiple: true ) end - - def map_options(collection) - OptionMapper.new(collection) - end end end end