aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis <denis@vaadin.com>2017-02-01 15:28:40 +0200
committerGitHub <noreply@github.com>2017-02-01 15:28:40 +0200
commit38b475330868d2d7b0d0b2da0a14be4040ca89ae (patch)
tree60b65819fec1931b9405dd1f7eb6943d68a34215
parent07d56a9ca050d976dec4f598ed39471ae369c588 (diff)
downloadvaadin-framework-38b475330868d2d7b0d0b2da0a14be4040ca89ae.tar.gz
vaadin-framework-38b475330868d2d7b0d0b2da0a14be4040ca89ae.zip
Introduce Binder.addFialdValueChangeListener (#8273)
Fixes #8273
-rw-r--r--server/src/main/java/com/vaadin/data/Binder.java28
-rw-r--r--server/src/test/java/com/vaadin/data/BinderValueChangeTest.java167
2 files changed, 195 insertions, 0 deletions
diff --git a/server/src/main/java/com/vaadin/data/Binder.java b/server/src/main/java/com/vaadin/data/Binder.java
index 4cbcd18eb1..f291c791b7 100644
--- a/server/src/main/java/com/vaadin/data/Binder.java
+++ b/server/src/main/java/com/vaadin/data/Binder.java
@@ -39,6 +39,7 @@ import java.util.stream.Stream;
import com.googlecode.gentyref.GenericTypeReflector;
import com.vaadin.annotations.PropertyId;
import com.vaadin.data.HasValue.ValueChangeEvent;
+import com.vaadin.data.HasValue.ValueChangeListener;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.data.validator.BeanValidator;
import com.vaadin.event.EventRouter;
@@ -928,6 +929,7 @@ public class Binder<BEAN> implements Serializable {
binderValidationResults);
getBinder().getValidationStatusHandler().statusChange(status);
getBinder().fireStatusChangeEvent(status.hasErrors());
+ getBinder().fireValueChangeEvent(event);
}
/**
@@ -1735,6 +1737,29 @@ public class Binder<BEAN> implements Serializable {
}
/**
+ * Adds field value change listener to all the fields in the binder.
+ * <p>
+ * Added listener is notified every time whenever any bound field value is
+ * changed. The same functionality can be achieved by adding a
+ * {@link ValueChangeListener} to all fields in the {@link Binder}.
+ * <p>
+ * The listener is added to all fields regardless of whether the method is
+ * invoked before or after field is bound.
+ *
+ * @see ValueChangeEvent
+ * @see ValueChangeListener
+ *
+ * @param listener
+ * a field value change listener
+ * @return a registration for the listener
+ */
+ public Registration addValueChangeListener(
+ ValueChangeListener<?> listener) {
+ return getEventRouter().addListener(ValueChangeEvent.class, listener,
+ ValueChangeListener.class.getDeclaredMethods()[0]);
+ }
+
+ /**
* Creates a new binding with the given field.
*
* @param <FIELDVALUE>
@@ -2239,4 +2264,7 @@ public class Binder<BEAN> implements Serializable {
return fieldName.toLowerCase(Locale.ENGLISH).replace("_", "");
}
+ private <V> void fireValueChangeEvent(ValueChangeEvent<V> event) {
+ getEventRouter().fireEvent(event);
+ }
}
diff --git a/server/src/test/java/com/vaadin/data/BinderValueChangeTest.java b/server/src/test/java/com/vaadin/data/BinderValueChangeTest.java
new file mode 100644
index 0000000000..985d0cc650
--- /dev/null
+++ b/server/src/test/java/com/vaadin/data/BinderValueChangeTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.data;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.data.Binder.BindingBuilder;
+import com.vaadin.data.HasValue.ValueChangeEvent;
+import com.vaadin.data.converter.StringToIntegerConverter;
+import com.vaadin.tests.data.bean.Person;
+import com.vaadin.ui.TextField;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class BinderValueChangeTest
+ extends BinderTestBase<Binder<Person>, Person> {
+
+ private AtomicReference<ValueChangeEvent<?>> event;
+
+ private static class TestTextField extends TextField {
+ @Override
+ protected boolean setValue(String value, boolean userOriginated) {
+ return super.setValue(value, userOriginated);
+ }
+ }
+
+ @Before
+ public void setUp() {
+ binder = new Binder<>();
+ item = new Person();
+ event = new AtomicReference<>();
+ }
+
+ @Test
+ public void unboundField_noEvents() {
+ binder.addValueChangeListener(this::statusChanged);
+
+ BindingBuilder<Person, String> binding = binder.forField(nameField);
+
+ nameField.setValue("");
+ Assert.assertNull(event.get());
+
+ binding.bind(Person::getFirstName, Person::setFirstName);
+ Assert.assertNull(event.get());
+ }
+
+ @Test
+ public void setBean_unbound_noEvents() {
+ binder.addValueChangeListener(this::statusChanged);
+
+ Assert.assertNull(event.get());
+
+ binder.setBean(item);
+
+ Assert.assertNull(event.get());
+ }
+
+ @Test
+ public void readBean_unbound_noEvents() {
+ binder.addValueChangeListener(this::statusChanged);
+
+ Assert.assertNull(event.get());
+
+ binder.readBean(item);
+
+ Assert.assertNull(event.get());
+ }
+
+ @Test
+ public void setValue_unbound_singleEventOnSetValue() {
+ binder.forField(nameField).bind(Person::getFirstName,
+ Person::setFirstName);
+ binder.forField(ageField)
+ .withConverter(new StringToIntegerConverter(""))
+ .bind(Person::getAge, Person::setAge);
+
+ binder.addValueChangeListener(this::statusChanged);
+
+ Assert.assertNull(event.get());
+ nameField.setValue("foo");
+ verifyEvent(nameField);
+ }
+
+ @Test
+ public void setValue_bound_singleEventOnSetValue() {
+ binder.forField(nameField).bind(Person::getFirstName,
+ Person::setFirstName);
+ binder.forField(ageField)
+ .withConverter(new StringToIntegerConverter(""))
+ .bind(Person::getAge, Person::setAge);
+ binder.setBean(item);
+
+ binder.addValueChangeListener(this::statusChanged);
+
+ Assert.assertNull(event.get());
+ nameField.setValue("foo");
+ verifyEvent(nameField);
+ }
+
+ @Test
+ public void userOriginatedUpdate_unbound_singleEventOnSetValue() {
+ TestTextField field = new TestTextField();
+
+ binder.forField(field).bind(Person::getFirstName, Person::setFirstName);
+ binder.forField(ageField)
+ .withConverter(new StringToIntegerConverter(""))
+ .bind(Person::getAge, Person::setAge);
+
+ binder.addValueChangeListener(this::statusChanged);
+
+ Assert.assertNull(event.get());
+ field.setValue("foo", true);
+ verifyEvent(field, true);
+ }
+
+ @Test
+ public void addListenerFirst_bound_singleEventOnSetValue() {
+ binder.addValueChangeListener(this::statusChanged);
+
+ binder.forField(nameField).bind(Person::getFirstName,
+ Person::setFirstName);
+ binder.forField(ageField)
+ .withConverter(new StringToIntegerConverter(""))
+ .bind(Person::getAge, Person::setAge);
+ binder.setBean(item);
+
+ Assert.assertNull(event.get());
+ ageField.setValue(String.valueOf(1));
+ verifyEvent(ageField);
+ }
+
+ private void verifyEvent(HasValue<?> field) {
+ verifyEvent(field, false);
+ }
+
+ private void verifyEvent(HasValue<?> field, boolean isUserOriginated) {
+ ValueChangeEvent<?> changeEvent = event.get();
+ Assert.assertNotNull(changeEvent);
+ Assert.assertEquals(field, changeEvent.getSource());
+ Assert.assertEquals(field, changeEvent.getComponent());
+ Assert.assertEquals(isUserOriginated, changeEvent.isUserOriginated());
+ }
+
+ private void statusChanged(ValueChangeEvent<?> evt) {
+ Assert.assertNull(event.get());
+ event.set(evt);
+ }
+}