在 Vaadin 8 的活页夹中自动更新相关计算 属性 的显示
Auto-update display of a related calculated property in a binder in Vaadin 8
在 Vaadin 8 中将 Binder
与具有只读计算 属性 且其值源自另一个 属性 的 bean 一起使用时,如何获得 TextField
当驱动 属性 的值发生变化时自动更新派生计算结果的显示?
在下面的例子中,当用户更改“出生年份”字段时,如何让“年龄”字段更新其计算?
Vaadin 8 的完整工作示例。
package com.example.val;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.Binder;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.*;
import javax.servlet.annotation.WebServlet;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of a html page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Theme ( "mytheme" )
public class MyUI extends UI {
Person person;
Binder < Person > binder;
@Override
protected void init ( VaadinRequest vaadinRequest ) {
// Data model
this.person = new Person ( "Jean-Luc", 1955 );
// Widgets
final TextField nameField = new TextField ( "Type the person’s name here:" );
final TextField yearOfBirthField = new TextField ( "Type the year of birth here:" );
final TextField ageField = new TextField ( "Approximate age:" );
ageField.setReadOnly ( true );
final Label beanToString = new Label ( );
// Binder
this.binder = new Binder <> ( );
binder.forField ( nameField )
.bind ( Person:: getName, Person:: setName );
binder.forField ( yearOfBirthField )
.withConverter ( new StringToIntegerConverter ( "Input must be Integer" ) )
.bind ( Person:: getYearOfBirth, Person:: setYearOfBirth );
binder.forField ( ageField )
.withConverter ( new StringToIntegerConverter ( "" ) )
.bind ( Person:: getAge, null );
binder.setBean ( this.person );
final Button button = new Button ( "Save" );
button.addClickListener ( event -> {
if ( binder.validate ( ).isOk ( ) ) {
// With `setBear`, the Person object is always up-to-date as long as there are no validation errors.
// MyBackend.updatePersonInDatabase(person);
beanToString.setValue ( this.person.toString ( ) );
} else { // Else bean flunks validation.
beanToString.setValue ( "The Person bean has invalid state." );
}
} );
this.setContent ( new VerticalLayout ( nameField, yearOfBirthField, ageField, button, beanToString ) );
}
@WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
class Person {
private Integer yearOfBirth;
private String name;
public Person ( String name_, Integer yearOfBirth_ ) {
this.name = name_;
this.yearOfBirth = yearOfBirth_;
}
public String getName ( ) {
return name;
}
public void setName ( String name ) {
this.name = name;
}
public Integer getYearOfBirth ( ) {
return yearOfBirth;
}
public void setYearOfBirth ( Integer yearOfBirth ) {
this.yearOfBirth = yearOfBirth;
}
// Read-only property 'age', calculated rather than stored.
public Integer getAge ( ) {
LocalDate today = LocalDate.now ( ZoneId.systemDefault ( ) );
Integer years = ( today.getYear ( ) - this.yearOfBirth );
return years;
}
@Override
public String toString ( ) {
return "Person{ " +
"yearOfBirth=" + yearOfBirth +
", age='" + this.getAge ( ) + "'" +
", name='" + name + "'" +
" }";
}
}
我发现最简单的方法是修改yearOfBirthField
的绑定代码如下:
binder.forField(yearOfBirthField)
.withConverter(new StringToIntegerConverter("Input must be Integer"))
.bind(Person::getYearOfBirth, (Setter<Person, Integer>) (person1, integer) -> {
person1.setYearOfBirth(integer);
ageField.setValue(person1.getAge().toString());
});
这有效地将 yearOfBirthField
绑定到 yearOfBirth
属性 并更新了 person bean 的 age
属性。
以任何方式改变 person bean 的成员,例如通过调用 person.setYearOfBirth(1977)
不会立即对字段产生影响。 Vaadin 的更新机制只能在一个方向上工作。字段修改被转移到 bean 但反之亦然。
编辑
在 Vaadin 8 中,没有内置方法可以在 bean 属性更改时自动更新字段。当 yearOfBirthField
的值改变时,新值被传播到 person bean 并且 person 的 yearOfBirth
成员通过绑定自动改变。但是之后 Vaadin 不会获取 bean 的所有绑定字段的当前值。因此 ageField
未更新且不反映当前值。
因此,要使 ageField
显示更新后的值,您必须以编程方式更新 ageField
。您可以在绑定器上再次设置整个 bean,这会导致调用所有 getter,但最简单的方法是在设置 yearOfBirth
之后设置 ageField 的值。这是在 setter 绑定的建议修改中完成的。
显然,Binder API 的行为发生了变化,快速浏览了源代码后,似乎不支持这种刷新功能。话虽如此,Vaadin 的家伙 considering 这种情况...
我想说最干净/最安全的方法是在字段本身上添加一个侦听器,而不依赖于此 new 绑定 API 的某些行为。事实上,好吧...... "new",它可能会在即将发布的 Vaadin 版本中得到增强。此外,UI 字段的更新可能是即时的。
编辑
再想一想,您至少可以在验证成功后重新读取 bean(这与@Morfic 建议的再次设置 bean 没有太大区别):
if (binder.validate().isOk()) {
// Force update of the Fields...
binder.readBean(binder.getBean());
beanToString.setValue(person.toString());
} else {
beanToString.setValue("The Person bean has invalid state.");
}
在 Vaadin 8 中将 Binder
与具有只读计算 属性 且其值源自另一个 属性 的 bean 一起使用时,如何获得 TextField
当驱动 属性 的值发生变化时自动更新派生计算结果的显示?
在下面的例子中,当用户更改“出生年份”字段时,如何让“年龄”字段更新其计算?
Vaadin 8 的完整工作示例。
package com.example.val;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.Binder;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.*;
import javax.servlet.annotation.WebServlet;
import java.time.LocalDate;
import java.time.ZoneId;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of a html page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
@Theme ( "mytheme" )
public class MyUI extends UI {
Person person;
Binder < Person > binder;
@Override
protected void init ( VaadinRequest vaadinRequest ) {
// Data model
this.person = new Person ( "Jean-Luc", 1955 );
// Widgets
final TextField nameField = new TextField ( "Type the person’s name here:" );
final TextField yearOfBirthField = new TextField ( "Type the year of birth here:" );
final TextField ageField = new TextField ( "Approximate age:" );
ageField.setReadOnly ( true );
final Label beanToString = new Label ( );
// Binder
this.binder = new Binder <> ( );
binder.forField ( nameField )
.bind ( Person:: getName, Person:: setName );
binder.forField ( yearOfBirthField )
.withConverter ( new StringToIntegerConverter ( "Input must be Integer" ) )
.bind ( Person:: getYearOfBirth, Person:: setYearOfBirth );
binder.forField ( ageField )
.withConverter ( new StringToIntegerConverter ( "" ) )
.bind ( Person:: getAge, null );
binder.setBean ( this.person );
final Button button = new Button ( "Save" );
button.addClickListener ( event -> {
if ( binder.validate ( ).isOk ( ) ) {
// With `setBear`, the Person object is always up-to-date as long as there are no validation errors.
// MyBackend.updatePersonInDatabase(person);
beanToString.setValue ( this.person.toString ( ) );
} else { // Else bean flunks validation.
beanToString.setValue ( "The Person bean has invalid state." );
}
} );
this.setContent ( new VerticalLayout ( nameField, yearOfBirthField, ageField, button, beanToString ) );
}
@WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
class Person {
private Integer yearOfBirth;
private String name;
public Person ( String name_, Integer yearOfBirth_ ) {
this.name = name_;
this.yearOfBirth = yearOfBirth_;
}
public String getName ( ) {
return name;
}
public void setName ( String name ) {
this.name = name;
}
public Integer getYearOfBirth ( ) {
return yearOfBirth;
}
public void setYearOfBirth ( Integer yearOfBirth ) {
this.yearOfBirth = yearOfBirth;
}
// Read-only property 'age', calculated rather than stored.
public Integer getAge ( ) {
LocalDate today = LocalDate.now ( ZoneId.systemDefault ( ) );
Integer years = ( today.getYear ( ) - this.yearOfBirth );
return years;
}
@Override
public String toString ( ) {
return "Person{ " +
"yearOfBirth=" + yearOfBirth +
", age='" + this.getAge ( ) + "'" +
", name='" + name + "'" +
" }";
}
}
我发现最简单的方法是修改yearOfBirthField
的绑定代码如下:
binder.forField(yearOfBirthField)
.withConverter(new StringToIntegerConverter("Input must be Integer"))
.bind(Person::getYearOfBirth, (Setter<Person, Integer>) (person1, integer) -> {
person1.setYearOfBirth(integer);
ageField.setValue(person1.getAge().toString());
});
这有效地将 yearOfBirthField
绑定到 yearOfBirth
属性 并更新了 person bean 的 age
属性。
以任何方式改变 person bean 的成员,例如通过调用 person.setYearOfBirth(1977)
不会立即对字段产生影响。 Vaadin 的更新机制只能在一个方向上工作。字段修改被转移到 bean 但反之亦然。
编辑
在 Vaadin 8 中,没有内置方法可以在 bean 属性更改时自动更新字段。当 yearOfBirthField
的值改变时,新值被传播到 person bean 并且 person 的 yearOfBirth
成员通过绑定自动改变。但是之后 Vaadin 不会获取 bean 的所有绑定字段的当前值。因此 ageField
未更新且不反映当前值。
因此,要使 ageField
显示更新后的值,您必须以编程方式更新 ageField
。您可以在绑定器上再次设置整个 bean,这会导致调用所有 getter,但最简单的方法是在设置 yearOfBirth
之后设置 ageField 的值。这是在 setter 绑定的建议修改中完成的。
显然,Binder API 的行为发生了变化,快速浏览了源代码后,似乎不支持这种刷新功能。话虽如此,Vaadin 的家伙 considering 这种情况...
我想说最干净/最安全的方法是在字段本身上添加一个侦听器,而不依赖于此 new 绑定 API 的某些行为。事实上,好吧...... "new",它可能会在即将发布的 Vaadin 版本中得到增强。此外,UI 字段的更新可能是即时的。
编辑
再想一想,您至少可以在验证成功后重新读取 bean(这与@Morfic 建议的再次设置 bean 没有太大区别):
if (binder.validate().isOk()) {
// Force update of the Fields...
binder.readBean(binder.getBean());
beanToString.setValue(person.toString());
} else {
beanToString.setValue("The Person bean has invalid state.");
}