Skip to content

Commit 2aa93a1

Browse files
Cleanup in NGPopUpButton
1 parent 538b80a commit 2aa93a1

File tree

1 file changed

+46
-52
lines changed

1 file changed

+46
-52
lines changed

ng-appserver/src/main/java/ng/appserver/templating/elements/NGPopUpButton.java

Lines changed: 46 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.HashMap;
77
import java.util.List;
88
import java.util.Map;
9+
import java.util.SequencedCollection;
910

1011
import ng.appserver.NGContext;
1112
import ng.appserver.NGRequest;
@@ -18,34 +19,24 @@
1819

1920
/**
2021
* FIXME: Add support for <optgroup> // Hugi 2024-07-13
22+
* FIXME: Currently always using an item's index in the original 'list' as an option's "value". We're missing a 'value' association to allow a custom value to go through to the generated request parameters // Hugi 2023-05-01
23+
* FIXME: Currently always replacing the value of the targeted "selections" binding with a new List. Having the option to modify an existing collection might be good.
2124
*/
2225

2326
public class NGPopUpButton extends NGDynamicElement {
2427

2528
private static final String NO_SELECTION_OPTION_VALUE = "NO_SELECTION_OPTION_VALUE";
2629

2730
private final NGAssociation _listAss;
28-
2931
private final NGAssociation _itemAss;
30-
3132
private final NGAssociation _displayStringAss;
32-
3333
private final NGAssociation _noSelectionStringAss;
34-
35-
private final NGAssociation _selectionAss;
36-
3734
private final NGAssociation _disabledAss;
38-
3935
private final NGAssociation _nameAss;
40-
4136
private final NGAssociation _indexAss;
42-
43-
/**
44-
* FIXME: We might want to offer the opportunity to replace the entire "selections" association in the target component, instead of modifying the existing collection // Hugi 2024-07-13
45-
*/
46-
private final NGAssociation _selectionsAss;
47-
4837
private final NGAssociation _multipleAss;
38+
private final NGAssociation _selectionAss;
39+
private final NGAssociation _selectionsAss;
4940

5041
/**
5142
* Pass-through attributes
@@ -73,23 +64,25 @@ public NGPopUpButton( String name, Map<String, NGAssociation> associations, NGEl
7364
public void takeValuesFromRequest( NGRequest request, NGContext context ) {
7465
if( !disabled( context ) ) {
7566
final String name = name( context );
76-
final List<String> valuesFromRequest = request.formValuesForKey( name );
67+
final List<String> selectedValuesFromRequest = request.formValuesForKey( name );
7768

78-
// FIXME: We need to handle "empty" for multiple selects. We're currently not handling the "no item selected" case // Hugi 2025-05-25
79-
if( !valuesFromRequest.isEmpty() ) {
80-
if( multiple( context ) ) {
81-
takeMultipleValuesFromRequest( context, name, valuesFromRequest );
82-
}
83-
else {
84-
takeSingleValueFromRequest( context, name, valuesFromRequest );
85-
}
69+
if( multiple( context ) ) {
70+
takeValuesFromRequestMultiple( context, name, selectedValuesFromRequest );
71+
}
72+
else {
73+
takeValuesFromRequestSingle( context, name, selectedValuesFromRequest );
8674
}
8775
}
8876
}
8977

90-
private void takeMultipleValuesFromRequest( final NGContext context, final String name, final List<String> valuesFromRequest ) {
78+
private void takeValuesFromRequestMultiple( final NGContext context, final String name, final List<String> valuesFromRequest ) {
9179

92-
// FIXME: We might want to offer th user the opportunity that we modify the original collection instead of passing in a new one // Hugi 2024-07-13
80+
// FIXME: OK, we have something of a problem with NGBrowser.
81+
// Selecting no objects in the UI results in it not being represented in the request's formValues at all (no value present for it's 'name').
82+
// This means that in the case of multiple forms on the same page and a form being submitted - we don't actually know if the value should be set to an empty list (we don't know if the containing form was submitted or if it was a different form).
83+
// The only way I can think of to make this work is if we can check if the actual *form* containing the current browser is being submitted. In that case, we can definitely interpret the absence of a value as "no values selected".
84+
// The current functionality means that if a browser is in one form on a page and a different form is submitted, the browser will always interpret that as "no values selected".
85+
// Hugi 2025-08-04
9386
final List<?> list = list( context );
9487

9588
final List selectedItems = new ArrayList<>();
@@ -101,24 +94,26 @@ private void takeMultipleValuesFromRequest( final NGContext context, final Strin
10194
_selectionsAss.setValue( selectedItems, context.component() );
10295
}
10396

104-
private void takeSingleValueFromRequest( final NGContext context, final String name, final List<String> valuesFromRequest ) {
97+
private void takeValuesFromRequestSingle( final NGContext context, final String name, final List<String> valuesFromRequest ) {
10598

106-
// If multiple form values are present for the same field name, the potential for an error condition is probably high enough to just go ahead and fail.
107-
if( valuesFromRequest.size() > 1 ) {
108-
throw new IllegalStateException( "The request contains %s form values named '%s'. I can only handle one at a time. The values you sent me are (%s).".formatted( valuesFromRequest.size(), name, valuesFromRequest ) );
109-
}
99+
if( !valuesFromRequest.isEmpty() ) {
100+
// If multiple form values are present for the same field name, the potential for an error condition is probably high enough to just go ahead and fail.
101+
if( valuesFromRequest.size() > 1 ) {
102+
throw new IllegalStateException( "The request contains %s form values named '%s'. I can only handle one at a time. The values you sent me are (%s).".formatted( valuesFromRequest.size(), name, valuesFromRequest ) );
103+
}
110104

111-
final String stringValueFromRequest = valuesFromRequest.get( 0 );
105+
final String stringValueFromRequest = valuesFromRequest.get( 0 );
112106

113-
// If nothing is selected, we push null to the selection
114-
if( NO_SELECTION_OPTION_VALUE.equals( stringValueFromRequest ) ) {
115-
_selectionAss.setValue( null, context.component() );
116-
}
117-
else {
118-
final int selectionIndex = Integer.parseInt( stringValueFromRequest );
119-
final List<?> list = list( context );
120-
final Object selectedItem = list.get( selectionIndex );
121-
_selectionAss.setValue( selectedItem, context.component() );
107+
// If nothing is selected, we push null to the selection
108+
if( NO_SELECTION_OPTION_VALUE.equals( stringValueFromRequest ) ) {
109+
_selectionAss.setValue( null, context.component() );
110+
}
111+
else {
112+
final int selectionIndex = Integer.parseInt( stringValueFromRequest );
113+
final List<?> list = list( context );
114+
final Object selectedItem = list.get( selectionIndex );
115+
_selectionAss.setValue( selectedItem, context.component() );
116+
}
122117
}
123118
}
124119

@@ -174,28 +169,23 @@ public void appendToResponse( final NGResponse response, final NGContext context
174169

175170
boolean isSelected = false;
176171

177-
// FIXME: Hacky way to get the currently selected item. We should be reusing logic from takeValuesFromRequest() here
178-
// FIXME: Handling of multiple selections should really only be in the elements that support those // Hugi 2025-05-25
179172
if( multiple( context ) ) {
180-
final List<String> selectedIndexes = context.request().formValuesForKey( name( context ) );
181-
isSelected = selectedIndexes.contains( String.valueOf( index ) );
182-
183-
// FIXME: We're doing a separate round here to check for current selections in the associated list. This is spaghetti logic by now, fix up // Hugi 2025-05-25
184-
final List selectedObjects = (List)_selectionsAss.valueInComponent( context.component() );
173+
final List selections = (List)_selectionsAss.valueInComponent( context.component() );
185174

186-
if( selectedObjects != null && selectedObjects.contains( object ) ) {
175+
if( selections != null && selections.contains( object ) ) {
187176
isSelected = true;
188177
}
189178
}
190179
else {
191-
final String indexValue = context.request().formValueForKey( name( context ) );
180+
final String selectedValue = context.request().formValueForKey( name( context ) );
192181

193-
if( indexValue != null && !indexValue.equals( NO_SELECTION_OPTION_VALUE ) && Integer.parseInt( indexValue ) == index ) {
182+
if( selectedValue != null && !selectedValue.equals( NO_SELECTION_OPTION_VALUE ) && Integer.parseInt( selectedValue ) == index ) {
194183
isSelected = true;
195184
}
196185

197186
if( _selectionAss != null ) {
198187
final Object selectedObject = _selectionAss.valueInComponent( context.component() );
188+
199189
if( object.equals( selectedObject ) ) {
200190
isSelected = true;
201191
}
@@ -204,7 +194,6 @@ public void appendToResponse( final NGResponse response, final NGContext context
204194

205195
final String selectedMarker = isSelected ? " selected" : "";
206196

207-
// FIXME: We're currently always using the index as "value". We're going to want to allow the passing in of a custom value attribute through the value binding // Hugi 2023-05-01
208197
response.appendContentString( "<option value=\"%s\"%s>%s</option>".formatted( index, selectedMarker, displayString ) );
209198
index++;
210199
}
@@ -264,6 +253,11 @@ private List<?> list( final NGContext context ) {
264253
return Arrays.asList( cast );
265254
}
266255

267-
throw new IllegalArgumentException( "NGRepetition only accepts java.util.List and java Arrays. You sent me a %s".formatted( listAssociationValue.getClass() ) );
256+
if( listAssociationValue instanceof SequencedCollection sc ) {
257+
// CHECKME: Ideally we don't want to convert the sequenced collection to a new List. However, making it redundant requires us to rethink/redesign how we're currently iterate over the list (by index) so here we are // Hugi 2024-09-30
258+
return List.of( sc.toArray() );
259+
}
260+
261+
throw new IllegalArgumentException( "%s only accepts java.util.List, java arrays and java.util.SequencedCollection. You sent me a %s".formatted( getClass().getSimpleName(), listAssociationValue.getClass() ) );
268262
}
269263
}

0 commit comments

Comments
 (0)