Skip to content

Instantly share code, notes, and snippets.

@christoph-frick
Last active April 25, 2017 12:23
Show Gist options
  • Save christoph-frick/947fec59cfd09f7140933b52102cc041 to your computer and use it in GitHub Desktop.
Save christoph-frick/947fec59cfd09f7140933b52102cc041 to your computer and use it in GitHub Desktop.
Vaadin CustomField for a Value-type, that wrapps a field for a Presentation-type and uses a Converter to transform between the two
package net.ofnir.vaadin.form
import com.vaadin.data.BeanValidationBinder
import com.vaadin.data.Binder
import com.vaadin.data.Converter
import com.vaadin.data.HasValue
import com.vaadin.shared.Registration
import com.vaadin.ui.AbstractField
import com.vaadin.ui.Component
import com.vaadin.ui.CustomField
import groovy.transform.EqualsAndHashCode
import groovy.transform.TupleConstructor
/**
* Adapts a Field to another type via a Converter.
* @param < VALUE > Type this field can handle
* @param < PRESENTATION > Type of the wrapped field
*/
class ConverterField<VALUE, PRESENTATION> extends CustomField<VALUE> {
private final AbstractField<PRESENTATION> wrappedField
private final Converter<PRESENTATION, VALUE> converter
private final Binder<ValueContainer<VALUE>> binder
private VALUE oldValue
/**
* @param wrappedField The field to edit the converted value.
* @param converter Converter to transform between the edited type and the type of the wrapped field.
* @param nullRepresentation Optional `null` applied before the converter
*/
ConverterField(AbstractField<PRESENTATION> wrappedField, Converter<PRESENTATION, VALUE> converter, Optional<PRESENTATION> nullRepresentation = Optional.empty()) {
this.wrappedField = wrappedField
caption = wrappedField.caption
this.converter = converter
binder = new BeanValidationBinder<ValueContainer<VALUE>>(ValueContainer)
def builder = binder.forField(wrappedField)
nullRepresentation.ifPresent{
builder = builder.withNullRepresentation(it) // before converter!
}
builder.withConverter(converter).bind('value')
doSetValue(null)
}
// @Override
protected Component initContent() {
return wrappedField
}
// @Override
protected void doSetValue(VALUE value) {
oldValue = value
binder.bean = new ValueContainer<VALUE>(value)
}
@Override
VALUE getValue() {
return binder.bean.value
}
@Override
Registration addValueChangeListener(HasValue.ValueChangeListener<VALUE> listener) {
return binder.addValueChangeListener({ HasValue.ValueChangeEvent<PRESENTATION> event ->
assert event.source == wrappedField, "A binder will only ever fire change events for the bound fields"
listener.valueChange(new HasValue.ValueChangeEvent<VALUE>(this, oldValue, event.userOriginated))
oldValue = getValue()
} as HasValue.ValueChangeListener<VALUE>)
}
@TupleConstructor
@EqualsAndHashCode(includes = ['value'])
static class ValueContainer<VALUE> {
VALUE value
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment