Open chrosim opened 1 year ago
Do you consider {code="ABC", name="Arrival ABC"}
to be the "same" object as {code="ABC", name="Departure ABC"}
? If that's the case, then you should maybe just use "ABC"
for serialization and instead let each combo box have a unique item label generator that takes care of the differences?
Alternatively, you can just treat them as distinct values since the user doesn't have any way of selecting a departure from the arrival list and vice versa.
The point of the extra serialization that is needed with Collaboration Engine is only so that when user A selects a specific value from a list, then there should be a way of letting user B also know exactly which value was selected so that they stay in sync. It's not used for cross-referencing different fields and it's not directly used for presentation.
@Legioth No i don't consider them to be the same.
My bad, i was talking about ComboBox but actualy am using MultiSelectComboBox
.
In my concrete case i have four MultiSelectComboBox<CodeName>
'es with four different ItemLists provided by four different external api calls.
I do not know the possible values of the four different List<CodeName>
's since they might change.
I need to bind those MultiSelectComboBox<CodeName>
'es to a Pojo
storing only the code
's in a list.
so I do the following for now, which is not collision safe:
List<CodeName> itemsA,itemsB,itemsC,itemsD;
public void createAndBindFields() {
binder.setSerializer(CodeName.class, CodeName::getCode,
value -> value == null ? null
: Stream.concat(Stream.concat(Stream.concat(itemsA.stream(), itemsB.stream()), itemsC.stream()), itemsD.stream())
.filter(item -> item.getValue().equals(value)).findFirst().orElse(null));
itemsAMultiSelect = new MultiSelectComboBox<CodeName>(getTranslation("Items A"));
itemsAMultiSelect.setItems(itemsA);
binder.forField(itemsAMultiSelect, Set.class, CodeName.class).withConverter(createCodeNameConverter(itemsA)).bind("itemsA");
itemsBMultiSelect = new MultiSelectComboBox<CodeName>(getTranslation("Items B"));
itemsBMultiSelect.setItems(itemsB);
binder.forField(itemsBMultiSelect, Set.class, CodeName.class).withConverter(createCodeNameConverter(itemsB)).bind("itemsB");
itemsCMultiSelect = new MultiSelectComboBox<CodeName>(getTranslation("Items C"));
itemsCMultiSelect.setItems(itemsC);
binder.forField(itemsCMultiSelect, Set.class, CodeName.class).withConverter(createCodeNameConverter(itemsC)).bind("itemsC");
itemsDMultiSelect = new MultiSelectComboBox<CodeName>(getTranslation("Items D"));
itemsDMultiSelect.setItems(itemsD);
binder.forField(itemsDMultiSelect, Set.class, CodeName.class).withConverter(createCodeNameConverter(itemsD)).bind("itemsD");
}
private Converter<Set<CodeName>, List<String>> createCodeNameConverter(Collection<CodeName> items) {
return new Converter<Set<CodeName>, List<String>>() {
@Override
public Result<List<String>> convertToModel(Set<CodeName> value, ValueContext context) {
return Result.ok(value == null ? null : value.stream().map(CodeName::getCode).collect(Collectors.toList()));
}
@Override
public Set<CodeName> convertToPresentation(List<String> value, ValueContext context) {
if (value == null)
return null;
return items.stream().filter(item -> value.contains(item.getCode())).collect(Collectors.toSet());
}
};
}
CodeName
looks like a value object for which I would suggest including all the data in the serialized form. If you can be sure that code
doesn't contain special characters such as :
, then you could serialize as codeName.code + ':' + codeName.name
and deserialize using string.split(":", 2)
and then using the two array items for finding the right instance. If you cannot have a character reserved as a separator or if you end up with more than two fields, then it might be best to use a real serialization library such as Jackson. You could also consider going further in the value object direction by treating CodeName
as real value object by implementing equals
and hashCode
and creating new instances when deserializing instead of trying to reuse an existing instance (potentially by changing it into a record class if you're using Java 16 or newer).
Hopefully this workaround will be enough for now. We should still consider implementing the suggested feature now when the use case is understood but I'm not sure about how it would be prioritized relative to all the other improvements that we could also consider making.
Thanks for pointing out the different way of serialization. I really did not think about it 😄 Unfortunately this workaround is not always a possibility for us, since the name value might be different for each users locale. So it's good to hear that you are considering introducing this feature.
For that case, I guess an alternative workaround might work. You would basically need the serialized format to contain the code and a value identifying which of the four lists it belongs to (e.g. "context"). Deserialization could then use the context value to know where to look for the correct instance.
If you can modify the CodeName
class, then you could add the context as an instance field there. If that's not possible, then you could add a wrapper class with one instance field for the CodeName
instance and another field for the context. You would then also need to use a converter to make the field binding work and configure the combo boxes to have an item renderer that unwraps the object.
At the moment it is possible to
setSerializer
for non "default" Classes on theCE-Binder
per Class only. Let's assume we need twoComboBoxes
with two different item Lists of the classCodeName
. To select the departureAirport and the arrivalAirport for aFlightRoute
we bind the comboboxes to the departureAirportCode and arrivalAirportCode.It is not possible to make sure the serializer would load the correct value per binding.
As a developer i would like to enable/set serialization for a specific binding. As a alternative it would be nice to use the converter on a binding for the serialization.