Browse Source

Implement ReadOnlyHasValue helper (#10643)

tags/8.4.0.alpha1
Ilia Motornyi 6 years ago
parent
commit
940a1ef584
No account linked to committer's email address

+ 12
- 0
documentation/datamodel/datamodel-forms.asciidoc View File

@@ -91,6 +91,18 @@ binder.bind(nameField,
}
});
----
== Binding non-modifiable Data

Non-modifiable data can be also bound to any component or component property with [classname]#ReadOnlyHasValue# helper class.
For example, `Panel` caption can display a person full name:

[source, java]
----
Panel infoPanel = new Panel();
ReadOnlyHasValue<Person> panelTitle = new ReadOnlyHasValue<>(
person -> infoPanel.setCaption(person.getLastName() + ", " + person.getFirstName()));
binder.forField(panelTitle).bind(person -> person, null);
----

== Validating and Converting User Input


+ 119
- 0
server/src/main/java/com/vaadin/data/ReadOnlyHasValue.java View File

@@ -0,0 +1,119 @@
/*
* 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 com.vaadin.server.SerializableConsumer;
import com.vaadin.shared.Registration;
import com.vaadin.ui.Label;

import java.io.Serializable;
import java.util.LinkedHashSet;
import java.util.Objects;

/**
* Generic {@link HasValue} to use any type of component with Vaadin data binding.
* <p>
* Example:
* <pre>
* Label label = new Label();
* ReadOnlyHasValue&lt;String&gt; hasValue = new ReadOnlyHasValue&lt;&gt;(label::setCaption);
* binder.forField(hasValue).bind(SomeBean::getName);
* </pre>
*
* @param <V> the value type
* @since
*/
public class ReadOnlyHasValue<V> implements HasValue<V>, Serializable {
private V value;
private final SerializableConsumer<V> valueProcessor;
private final V emptyValue;
private LinkedHashSet<ValueChangeListener<V>> listenerList;

/**
* Creates new {@code ReadOnlyHasValue}
*
* @param valueProcessor the value valueProcessor, e.g. {@link Label#setValue}
* @param emptyValue the value to be used as empty, {@code null} by default
*/
public ReadOnlyHasValue(SerializableConsumer<V> valueProcessor, V emptyValue) {
this.valueProcessor = valueProcessor;
this.emptyValue = emptyValue;
}

/**
* Creates new {@code ReadOnlyHasValue} with {@code null} as an empty value.
*
* @param valueProcessor the value valueProcessor, e.g. {@link Label#setValue}
*/
public ReadOnlyHasValue(SerializableConsumer<V> valueProcessor) {
this(valueProcessor,null);
}

@Override
public void setValue(V value) {
V oldValue = this.value;
this.value = value;
valueProcessor.accept(value);
if (listenerList != null && ! Objects.equals(oldValue, value)) {
for (ValueChangeListener<V> valueChangeListener : listenerList) {
valueChangeListener.valueChange(
new ValueChangeEvent<>(null, this, oldValue, false));
}
}
}

@Override
public V getValue() {
return value;
}

@Override
public Registration addValueChangeListener(
ValueChangeListener<V> listener) {
Objects.requireNonNull(listener, "Listener must not be null.");
if (listenerList == null) {
listenerList = new LinkedHashSet<>();
}
listenerList.add(listener);

return () -> listenerList.remove(listener);
}

@Override
public boolean isRequiredIndicatorVisible() {
return false;
}

@Override
public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
if (requiredIndicatorVisible) throw new IllegalArgumentException("Not Writable");
}

@Override
public void setReadOnly(boolean readOnly) {
if (!readOnly) throw new IllegalArgumentException("Not Writable");
}

@Override
public boolean isReadOnly() {
return true;
}

@Override
public V getEmptyValue() {
return emptyValue;
}
}

+ 116
- 0
server/src/test/java/com/vaadin/data/ReadOnlyHasValueTest.java View File

@@ -0,0 +1,116 @@
package com.vaadin.data;

import com.vaadin.shared.Registration;
import com.vaadin.ui.Label;
import org.junit.Before;
import org.junit.Test;

import java.util.Objects;

import static org.junit.Assert.*;

public class ReadOnlyHasValueTest {
private static final String SAY_SOMETHING = "Say something";
private static final String SAY_SOMETHING_ELSE = "Say something else";
private static final String NO_VALUE = "-no-value-";
private Label label;
private ReadOnlyHasValue<String> hasValue;

@Before
public void setup() {
label = new Label();
hasValue = new ReadOnlyHasValue<>(label::setCaption);
}

@Test
public void testBase() {
hasValue.setReadOnly(true);
hasValue.setRequiredIndicatorVisible(false);
Registration registration = hasValue.addValueChangeListener(e -> {
});
registration.remove();
hasValue.setValue(SAY_SOMETHING);
assertEquals(SAY_SOMETHING, hasValue.getValue());
assertEquals(SAY_SOMETHING, label.getCaption());
hasValue.setValue(SAY_SOMETHING_ELSE);
assertEquals(SAY_SOMETHING_ELSE, hasValue.getValue());
assertEquals(SAY_SOMETHING_ELSE, label.getCaption());
}

@Test(expected = IllegalArgumentException.class)
public void testRO() {
hasValue.setReadOnly(false);
}

@Test(expected = IllegalArgumentException.class)
public void testIndicator() {
hasValue.setRequiredIndicatorVisible(true);
}

@Test
public void testBind() {
Binder<Bean> beanBinder = new Binder<>(Bean.class);
Label label = new Label();
ReadOnlyHasValue<Long> intHasValue = new ReadOnlyHasValue<>(
i -> label.setValue(Objects.toString(i, "")));

beanBinder.forField(intHasValue).bind("v");

beanBinder.readBean(new Bean(42));
assertEquals("42", label.getValue());
assertEquals(42L, intHasValue.getValue().longValue());

Registration registration = intHasValue.addValueChangeListener(e -> {
assertEquals(42L, e.getOldValue().longValue());
assertSame(intHasValue, e.getSource());
assertSame(null, e.getComponent());
assertSame(null, e.getComponent());
assertFalse(e.isUserOriginated());
});
beanBinder.readBean(new Bean(1984));
assertEquals("1984", label.getValue());
assertEquals(1984L, intHasValue.getValue().longValue());

registration.remove();

beanBinder.readBean(null);
assertEquals("", label.getValue());
assertEquals(null, intHasValue.getValue());

}

@Test
public void testEmptyValue() {
Binder<Bean> beanBinder = new Binder<>(Bean.class);
Label label = new Label();
ReadOnlyHasValue<String> strHasValue =
new ReadOnlyHasValue<>(label::setValue, NO_VALUE);

beanBinder.forField(strHasValue)
.withConverter(Long::parseLong,(Long i)->"" + i)
.bind("v");

beanBinder.readBean(new Bean(42));
assertEquals("42", label.getValue());

beanBinder.readBean(null);
assertEquals(NO_VALUE, label.getValue());
assertTrue(strHasValue.isEmpty());
}

public static class Bean {
public Bean(long v) {
this.v = v;
}

private long v;

public long getV() {
return v;
}

public void setV(long v) {
this.v = v;
}
}
}

Loading…
Cancel
Save