Vaadin:如何绑定实体 "meta data"
Vaadin: How to bind entity "meta data"
我正在尝试弄清楚如何绑定关于实体的元数据,而该实体不是实体中的实际字段。例如,考虑以下内容:
Task:
title: string
description: string
...
notes: List<TaskNote>
我希望能够在网格或“编辑器”表单中显示 Task
并显示,比如与 [=12] 关联的 TaskNote
的计数=].因此,在这种情况下,计数只是 notes
列表中的项目数。
如何为这样的东西创建数据绑定?什么是最好的practices/techniques?
Binder 用于将 Fields
绑定到特定 bean 的 Properties
。 Person
bean 可以有 Properties
“名字”、“姓氏”和“出生年份”。但是,Field
和Property
是比较抽象的概念,给大家留下了很大的回旋余地。
- A
Field
是实现 HasValue
接口的东西,通常是 Component
。 HasValue
接口(因此 Field
)有一个 Java 类型,例如 String
.
- 一对
Property
本质上是一对getter/setter。该对必须具有匹配的类型 - getter 的 return 类型必须与 setter 作为输入的类型匹配。
-
Property
的 getter 可以通过实现 ValueProvider
函数接口来提供,在简单的形式中,可以是 Lambda 表达式,例如 person -> person.getFirstName()
- setter 可以提供
Setter
功能接口的实现。作为 Lambda 的一个简单示例是 (person, newFirstName) -> person.setFirstName(newFirstName)
.
创建 Binder
时,您以某种方式提供了 ValueProvider
和 Setter
;最明确的方式是当你像这样绑定时:
Binder<Person> binder = new Binder<>();
TextField firstNameField = new TextField("First name");
binder.forField(firstNameField).bind(valueProvider, setter);
当您处理属于 bean 而不是 Property
的部分时,您有几个不同的选择。例如,人的“年龄”可以作为他们“出生年份”的函数来计算。假设我们只希望能够编辑出生年份,但我们还希望将年龄显示为只读值。它也可以是基于年龄或其他东西的折扣百分比。
第一种也是最简单的方法是使用 Field
,但只有 ValueProvider
而没有 Setter
。所以你可以有这样的东西(为简单起见使用 TextField
):
Binder<Person> binder = new Binder<>();
TextField ageField = new TextField("Age (calculated)");
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
这种方法的缺点是您仍在使用 TextField
,它是一个编辑器组件。您可以将其设置为只读模式,这样用户就无法更改该值,但它仍然看起来您也许可以在某个时候对其进行编辑。
您可以使用的另一件事是 ReadOnlyHasValue
,这是一个用于创建没有 Setter
的 Property
的界面。它有点笨拙,但您可以将其与绑定一起使用:
Span ageDisplay = new Span();
ReadOnlyHasValue<String> ageField = new ReadOnlyHasValue<>(text -> ageDisplay.setText(text));
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
最后,由于 ValueProvider
和 Setter
只是 Java 接口,您可以使用任何类型的副作用来实现它们。因此,您可以将任何逻辑搭载到 属性 的更改中:
Binder<Person> binder = new Binder<>();
Span ageDisplay = new Span();
TextField birthYear = new TextField();
binder.forField(birthYear)
.bind(person -> { // ValueProvider
return person.getBirthYear();
}, (person, newBirthYear) -> { // Setter
person.setBirthYear(newBirthYear);
ageDisplay.setText(calculateAge(newBirthYear));
});
我正在尝试弄清楚如何绑定关于实体的元数据,而该实体不是实体中的实际字段。例如,考虑以下内容:
Task:
title: string
description: string
...
notes: List<TaskNote>
我希望能够在网格或“编辑器”表单中显示 Task
并显示,比如与 [=12] 关联的 TaskNote
的计数=].因此,在这种情况下,计数只是 notes
列表中的项目数。
如何为这样的东西创建数据绑定?什么是最好的practices/techniques?
Binder 用于将 Fields
绑定到特定 bean 的 Properties
。 Person
bean 可以有 Properties
“名字”、“姓氏”和“出生年份”。但是,Field
和Property
是比较抽象的概念,给大家留下了很大的回旋余地。
- A
Field
是实现HasValue
接口的东西,通常是Component
。HasValue
接口(因此Field
)有一个 Java 类型,例如String
. - 一对
Property
本质上是一对getter/setter。该对必须具有匹配的类型 - getter 的 return 类型必须与 setter 作为输入的类型匹配。-
Property
的 getter 可以通过实现ValueProvider
函数接口来提供,在简单的形式中,可以是 Lambda 表达式,例如person -> person.getFirstName()
- setter 可以提供
Setter
功能接口的实现。作为 Lambda 的一个简单示例是(person, newFirstName) -> person.setFirstName(newFirstName)
.
-
创建 Binder
时,您以某种方式提供了 ValueProvider
和 Setter
;最明确的方式是当你像这样绑定时:
Binder<Person> binder = new Binder<>();
TextField firstNameField = new TextField("First name");
binder.forField(firstNameField).bind(valueProvider, setter);
当您处理属于 bean 而不是 Property
的部分时,您有几个不同的选择。例如,人的“年龄”可以作为他们“出生年份”的函数来计算。假设我们只希望能够编辑出生年份,但我们还希望将年龄显示为只读值。它也可以是基于年龄或其他东西的折扣百分比。
第一种也是最简单的方法是使用 Field
,但只有 ValueProvider
而没有 Setter
。所以你可以有这样的东西(为简单起见使用 TextField
):
Binder<Person> binder = new Binder<>();
TextField ageField = new TextField("Age (calculated)");
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
这种方法的缺点是您仍在使用 TextField
,它是一个编辑器组件。您可以将其设置为只读模式,这样用户就无法更改该值,但它仍然看起来您也许可以在某个时候对其进行编辑。
您可以使用的另一件事是 ReadOnlyHasValue
,这是一个用于创建没有 Setter
的 Property
的界面。它有点笨拙,但您可以将其与绑定一起使用:
Span ageDisplay = new Span();
ReadOnlyHasValue<String> ageField = new ReadOnlyHasValue<>(text -> ageDisplay.setText(text));
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
最后,由于 ValueProvider
和 Setter
只是 Java 接口,您可以使用任何类型的副作用来实现它们。因此,您可以将任何逻辑搭载到 属性 的更改中:
Binder<Person> binder = new Binder<>();
Span ageDisplay = new Span();
TextField birthYear = new TextField();
binder.forField(birthYear)
.bind(person -> { // ValueProvider
return person.getBirthYear();
}, (person, newBirthYear) -> { // Setter
person.setBirthYear(newBirthYear);
ageDisplay.setText(calculateAge(newBirthYear));
});