This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 7ba1142 ISIS-2340: vaa/fx: starting to harmonize managed-feature
binding
7ba1142 is described below
commit 7ba11421ab72cb019f252841d0b7731222ebb359
Author: Andi Huber <[email protected]>
AuthorDate: Tue Aug 25 17:33:03 2020 +0200
ISIS-2340: vaa/fx: starting to harmonize managed-feature binding
---
.../binding/{BinderUtil.java => BindingsVaa.java} | 169 +++++++++++++++++----
.../components/temporal/TemporalFieldFactory.java | 6 +-
.../ui/components/text/TextFieldFactory.java | 16 +-
.../ui/components/text/uuid/UuidFieldFactory.java | 4 +-
.../model/components/UiComponentFactory.java | 9 +-
5 files changed, 154 insertions(+), 50 deletions(-)
diff --git
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
similarity index 57%
rename from
incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
rename to
incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
index 45f4a5b..6fdae5c 100644
---
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BinderUtil.java
+++
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/binding/BindingsVaa.java
@@ -21,6 +21,7 @@ package org.apache.isis.incubator.viewer.vaadin.ui.binding;
import java.time.LocalDate;
import java.util.function.Function;
+import com.vaadin.flow.component.HasValidation;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.Binder.BindingBuilder;
@@ -30,33 +31,133 @@ import com.vaadin.flow.data.converter.Converter;
import com.vaadin.flow.data.converter.DateToSqlDateConverter;
import com.vaadin.flow.data.converter.LocalDateToDateConverter;
+import org.apache.isis.core.commons.binding.Bindable;
+import org.apache.isis.core.commons.binding.Observable;
+import org.apache.isis.core.commons.internal.base._Casts;
+import org.apache.isis.core.commons.internal.base._Strings;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.metamodel.interactions.managed.InteractionVeto;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedFeature;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedParameter;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedProperty;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
import
org.apache.isis.viewer.common.model.components.UiComponentFactory.ComponentRequest;
+import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
import lombok.experimental.UtilityClass;
@UtilityClass
-public final class BinderUtil {
+public final class BindingsVaa {
+
+ /**
+ * Binds the uiField's (rendered) value to an {@link Observable}.
+ * @param <V>
+ * @param uiField
+ * @param value
+ */
+ public static <V> void bindValue(
+ final @NonNull HasValue<?, V> uiField,
+ final @NonNull Observable<ManagedObject> value) {
+
+ uiField.setReadOnly(true);
+ value.addListener((e, oldValue, newValue)->{
+ uiField.setValue(_Casts.uncheckedCast(newValue.getPojo()));
+ });
+ }
+
+ /**
+ * Binds the uiField's (rendered) value to a {@link Bindable}.
+ * @param <V>
+ * @param uiField
+ * @param value
+ */
+ public static <V> void bindValueBidirectional(
+ final @NonNull HasValue<?, V> uiField,
+ final @NonNull Bindable<ManagedObject> value) {
+
+ uiField.setReadOnly(true);
+ value.addListener((e, oldValue, newValue)->{
+ uiField.setValue(_Casts.uncheckedCast(newValue.getPojo()));
+ });
+ }
+
+ /**
+ * Binds the uiField's (rendered) validation feedback to an {@link
Observable}.
+ * @param <F>
+ * @param uiField
+ * @param validationFeedbackMessage
+ */
+ public static <F extends HasValidation>
+ void bindValidationFeedback(
+ final @NonNull F uiField,
+ final @NonNull Observable<String> validationFeedbackMessage) {
+
+ validationFeedbackMessage.addListener((e, oldValue, newValue)->{
+ uiField.setErrorMessage(newValue);
+ uiField.setInvalid(_Strings.isNotEmpty(newValue));
+ });
+ }
+
+
+ public static <F extends HasValue<?, ?> & HasValidation>
+ void bindFeature(F uiField, ManagedFeature managedFeature) {
+
+ if(managedFeature instanceof ManagedParameter) {
+
+ val managedParameter = (ManagedParameter)managedFeature;
+ val isReadOnly = false;
+ uiField.setReadOnly(isReadOnly);
+
+ // r/w binding
+ bindValueBidirectional(uiField, managedParameter.getValue());
+
+ // bind parameter validation feedback
+ bindValidationFeedback(uiField,
managedParameter.getValidationMessage());
+
+ } else if(managedFeature instanceof ManagedProperty) {
+
+ val managedProperty = (ManagedProperty)managedFeature;
+ val isReadOnly = managedProperty.checkUsability().isPresent();
+ uiField.setReadOnly(isReadOnly);
+
+ if(isReadOnly) {
+ // readonly binding
+ bindValue(uiField, managedProperty.getValue());
+
+ } else {
+
+ //TODO allow property (inline) editing
+ //using readonly as fallback for now
+ uiField.setReadOnly(true);
+ bindValue(uiField, managedProperty.getValue());
+ }
+
+ } else {
+ throw _Exceptions.unexpectedCodeReach();
+ }
+
+ }
+
public static <P, M> Binder<ComponentRequest> requestBinder(
final HasValue<?, P> uiField,
final Class<M> modelValueType,
final Function<BindingBuilder<ComponentRequest, P>,
BindingBuilder<ComponentRequest, M>> chain) {
-
+
val binder = new Binder<ComponentRequest>();
val propagator = new Propagator<M>(modelValueType);
-
+
chain.apply(binder.forField(uiField))
.withConverter(propagator)
.bind(
propagator::init,
propagator::propagate
- );
+ );
return binder;
}
-
+
/**
*
* @param <P> presentation type (field value type)
@@ -68,19 +169,19 @@ public final class BinderUtil {
public static <P> Binder<ComponentRequest> requestBinder(
final HasValue<?, P> uiField,
final Class<P> fieldValueType) {
-
+
val binder = new Binder<ComponentRequest>();
val propagator = new Propagator<P>(fieldValueType);
-
+
binder.forField(uiField)
.withConverter(propagator)
.bind(
propagator::init,
propagator::propagate
- );
+ );
return binder;
}
-
+
/**
*
* @param <P> presentation type (field value type)
@@ -94,73 +195,81 @@ public final class BinderUtil {
final HasValue<?, P> uiField,
final Class<M> modelValueType,
final Converter<P, M> converter) {
-
+
return requestBinder(uiField, modelValueType,
binder->binder.withConverter(converter));
}
-
+
@RequiredArgsConstructor
private static class Propagator<P> implements Converter<P, P> {
-
+
private static final long serialVersionUID = 1L;
private final Class<P> pojoType;
private ComponentRequest request;
-
+
@Override
public Result<P> convertToModel(P newValue, ValueContext context) {
+
// propagate new value down the domain model, and handle
validation feedback
+
val validationMessage = request.setFeatureValue(newValue)
.map(InteractionVeto::getReason)
.orElse(null);
-
+
return validationMessage==null
? Result.ok(newValue)
- : Result.error(validationMessage);
+ : Result.error(validationMessage);
}
-
+
@Override
public P convertToPresentation(P value, ValueContext context) {
return value; // identity function
}
-
+
public P init(ComponentRequest request) {
this.request = request;
return request.getFeatureValue(pojoType).orElse(null);
}
-
+
public P propagate(ComponentRequest request, P newValue) {
return newValue; // identity function
}
-
+
}
-
+
// -- SHORTCUTS
-
+
public static enum DateBinder {
-
+
JAVA_TIME_LOCAL_DATE{
@Override
public Binder<ComponentRequest> bind(HasValue<?, LocalDate>
uiField) {
- return BinderUtil.requestBinder(uiField, LocalDate.class);
+ return BindingsVaa.requestBinder(uiField, LocalDate.class);
}
-
+
},
JAVA_SQL_DATE{
@Override
public Binder<ComponentRequest> bind(HasValue<?, LocalDate>
uiField) {
- return BinderUtil.requestBinderWithConverter(uiField,
java.sql.Date.class,
+ return BindingsVaa.requestBinderWithConverter(uiField,
java.sql.Date.class,
new LocalDateToDateConverter().chain(new
DateToSqlDateConverter()));
}
-
+
};
-
+
public abstract Binder<ComponentRequest> bind(final HasValue<?,
LocalDate> uiField);
-
+
}
-
-
+
+
+
+
+
+
+
+
}
diff --git
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
index 5af64a8..1084526 100644
---
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
+++
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/temporal/TemporalFieldFactory.java
@@ -30,7 +30,7 @@ import
org.apache.isis.core.commons.internal.exceptions._Exceptions;
import org.apache.isis.core.metamodel.facets.value.temporal.TemporalValueFacet;
import
org.apache.isis.core.metamodel.facets.value.temporal.TemporalValueFacet.OffsetCharacteristic;
import
org.apache.isis.core.metamodel.facets.value.temporal.TemporalValueFacet.TemporalCharacteristic;
-import org.apache.isis.incubator.viewer.vaadin.ui.binding.BinderUtil;
+import org.apache.isis.incubator.viewer.vaadin.ui.binding.BindingsVaa;
import
org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
import
org.apache.isis.viewer.common.model.components.UiComponentFactory.ComponentRequest;
@@ -64,9 +64,9 @@ public class TemporalFieldFactory implements
UiComponentHandlerVaa {
final Binder<ComponentRequest> binder;
if(request.isFeatureTypeEqualTo(LocalDate.class)) {
- binder =
BinderUtil.DateBinder.JAVA_TIME_LOCAL_DATE.bind(uiField);
+ binder =
BindingsVaa.DateBinder.JAVA_TIME_LOCAL_DATE.bind(uiField);
} else if(request.isFeatureTypeEqualTo(java.sql.Date.class)) {
- binder = BinderUtil.DateBinder.JAVA_SQL_DATE.bind(uiField);
+ binder = BindingsVaa.DateBinder.JAVA_SQL_DATE.bind(uiField);
} else {
throw _Exceptions.unmatchedCase(request.getFeatureType());
}
diff --git
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/TextFieldFactory.java
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/TextFieldFactory.java
index 735b43f..3477ae9 100644
---
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/TextFieldFactory.java
+++
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/TextFieldFactory.java
@@ -25,7 +25,7 @@ import org.springframework.core.annotation.Order;
import org.apache.isis.applib.annotation.OrderPrecedence;
import org.apache.isis.core.metamodel.facets.value.string.StringValueFacet;
-import org.apache.isis.incubator.viewer.vaadin.ui.binding.BinderUtil;
+import org.apache.isis.incubator.viewer.vaadin.ui.binding.BindingsVaa;
import
org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
import
org.apache.isis.viewer.common.model.components.UiComponentFactory.ComponentRequest;
@@ -42,19 +42,13 @@ public class TextFieldFactory implements
UiComponentHandlerVaa {
@Override
public Component handle(ComponentRequest request) {
-
- val uiField = new TextField(request.getDisplayLabel());
- val binder = BinderUtil.requestBinder(uiField, String.class,
- builder->builder.withNullRepresentation(""));
+ val uiField = new TextField(request.getDisplayLabel());
- if(request.isReadOnly()) {
- uiField.setReadOnly(true);
- binder.readBean(request);
- } else {
- binder.setBean(request);
- }
+ val managedFeature = request.getManagedFeature();
+ BindingsVaa.bindFeature(uiField, managedFeature);
+
return uiField;
}
diff --git
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/uuid/UuidFieldFactory.java
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/uuid/UuidFieldFactory.java
index aa58a55..734122c 100644
---
a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/uuid/UuidFieldFactory.java
+++
b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/text/uuid/UuidFieldFactory.java
@@ -29,7 +29,7 @@ import com.vaadin.flow.data.converter.Converter;
import org.springframework.core.annotation.Order;
import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.incubator.viewer.vaadin.ui.binding.BinderUtil;
+import org.apache.isis.incubator.viewer.vaadin.ui.binding.BindingsVaa;
import
org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
import
org.apache.isis.viewer.common.model.components.UiComponentFactory.ComponentRequest;
@@ -49,7 +49,7 @@ public class UuidFieldFactory implements
UiComponentHandlerVaa {
val uiField = new TextField(request.getDisplayLabel());
- val binder = BinderUtil.requestBinderWithConverter(uiField,
UUID.class, new StringToUuidConverter());
+ val binder = BindingsVaa.requestBinderWithConverter(uiField,
UUID.class, new StringToUuidConverter());
binder.setBean(request);
return uiField;
diff --git
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/components/UiComponentFactory.java
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/components/UiComponentFactory.java
index 57c1e3b..d46c823 100644
---
a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/components/UiComponentFactory.java
+++
b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/components/UiComponentFactory.java
@@ -120,6 +120,11 @@ public interface UiComponentFactory<B, C> {
.anyMatch(_NullSafe::isPresent);
}
+ //TODO are there ever parameters that might render readonly?
+ public boolean isReadOnly() {
+ return
((ManagedProperty)managedFeature).checkUsability().isPresent();
+ }
+
@Deprecated
public <T> Optional<T> getFeatureValue(@Nullable Class<T> type) {
val managedProperty = (ManagedProperty)managedFeature;
@@ -131,10 +136,6 @@ public interface UiComponentFactory<B, C> {
.map(type::cast);
}
- public boolean isReadOnly() {
- return
((ManagedProperty)managedFeature).checkUsability().isPresent();
- }
-
@Deprecated
public Optional<InteractionVeto> setFeatureValue(Object
proposedNewValuePojo) {
//TODO we are loosing any fields that are cached within
ManagedObject