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

Reply via email to