Tornadofx Javafx - 如何重新加载视图/组件
Tornadofx Javafx - How to reload a view / component
这是一个基本问题。
我想要实现的是从另一个角度刷新视图。
假设我有一个视图 EmployeeTableView,它通过执行 REST API 调用显示员工的表格表示。
在另一个视图中,我有一个过滤器 EmployeeFilterView,其中我有性别、薪水范围、员工类型等。
我还有一个 userContext 对象,我在其中存储用户首选项。所以默认情况下,假设我将性别过滤器的值存储为男性,将薪水范围存储为所有,等等。该对象作为参数发送到 EmployeeTableView。
加载 EmployeeTableView 后,我使用 userContext 值执行 restAPI 调用以获取员工详细信息。所以效果很好。现在我将性别筛选器更改为 Female 并在我的 userContext 中分配该值。
现在,如果我可以使用 userContext 对象重新加载 EmployeeTableView,restapi 调用将获得更新后的值。
但是我该怎么做呢?
如果你有,也建议一个更好的方法。
我最终使用了 Tornadofx -> EventBus
基本上,当我更改任何过滤器时,我会触发一个偶数,它会使用更新后的值重建节点。
不确定这个方法是否正确,所以仍然开放讨论。
EventBus 是一个有效的解决方案。另一种方法是使用 ViewModel 或 Controller 作为 UserContext 对象,让它包含实际可观察的员工列表,然后将该列表绑定到 EmployeeTableView
中的 TableView
。每当更新上下文中的列表时,TableView 也会更新。
过滤器视图将调用 UserContext 中的一个函数来执行实际的 REST 调用并根据该函数更新员工列表。
您可以创建一个单独的 EmployeeQuery 对象,该对象可以同时注入 EmployeeFilterView
和 UserContext
,以便它可以提取选定的过滤器值来执行查询。此查询对象包含要传递给服务器的所有搜索参数的列表。
如果这对您的体系结构有意义,您也可以考虑创建一个单独的作用域以将这些组件分开。
具体如何定义这些组件主要是个人喜好问题,这里有一个建议。我使用 ControlsFX 中的 RangeSlider
进行模拟搜索 UI.
为了更容易想象这是如何联系在一起的,这里有一张截图:
(所有姓名和薪水都是虚构的:)
/**
* The employee domain model, implementing JsonModel so it can be fetched
* via the REST API
*/
class Employee : JsonModel {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val salaryProperty = SimpleIntegerProperty()
var salary by salaryProperty
val genderProperty = SimpleObjectProperty<Gender>()
var gender by genderProperty
override fun updateModel(json: JsonObject) {
with (json) {
name = getString("name")
salary = getInt("salary")
gender = Gender.valueOf(getString("gender"))
}
}
}
enum class Gender { Male, Female }
/**
* Container for the list of employees as well as a search function called by the filter
* view whenever it should update the employee list.
*/
class EmployeeContext : Controller() {
val api: Rest by inject()
val query: EmployeeQuery by inject()
val employees = SimpleListProperty<Employee>()
fun search() {
runAsync {
FXCollections.observableArrayList(Employee().apply {
name = "Edvin Syse"
gender = Gender.Male
salary = 200_000
})
//api.post("employees/query", query).list().toModel<Employee>()
} ui {
employees.value = it
}
}
}
/**
* Query object used to define the query sent to the server
*/
class EmployeeQuery : ViewModel(), JsonModel {
val genderProperty = SimpleObjectProperty<Gender>(Gender.Female)
var gender by genderProperty
val salaryMinProperty = SimpleIntegerProperty(50_000)
var salaryMin by salaryMinProperty
val salaryMaxProperty = SimpleIntegerProperty(250_000)
var salaryMax by salaryMaxProperty
val salaryDescription = stringBinding(salaryMinProperty, salaryMaxProperty) {
"$$salaryMin - $$salaryMax"
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("gender", gender.toString())
add("salaryMin", salaryMin)
add("salaryMax", salaryMax)
}
}
}
/**
* The search/filter UI
*/
class EmployeeFilterView : View() {
val query: EmployeeQuery by inject()
val context: EmployeeContext by inject()
override val root = form {
fieldset("Employee Filter") {
field("Gender") {
combobox(query.genderProperty, Gender.values().toList())
}
field("Salary Range") {
vbox {
alignment = Pos.CENTER
add(RangeSlider().apply {
max = 500_000.0
lowValueProperty().bindBidirectional(query.salaryMinProperty)
highValueProperty().bindBidirectional(query.salaryMaxProperty)
})
label(query.salaryDescription)
}
}
button("Search").action {
context.search()
}
}
}
}
/**
* The UI that shows the search results
*/
class EmployeeTableView : View() {
val context: EmployeeContext by inject()
override val root = borderpane {
center {
tableview(context.employees) {
column("Name", Employee::nameProperty)
column("Gender", Employee::genderProperty)
column("Salary", Employee::salaryProperty)
}
}
}
}
/**
* A sample view that ties the filter UI and result UI together
*/
class MainView : View("Employee App") {
override val root = hbox {
add(EmployeeFilterView::class)
add(EmployeeTableView::class)
}
}
这是一个基本问题。
我想要实现的是从另一个角度刷新视图。
假设我有一个视图 EmployeeTableView,它通过执行 REST API 调用显示员工的表格表示。
在另一个视图中,我有一个过滤器 EmployeeFilterView,其中我有性别、薪水范围、员工类型等。
我还有一个 userContext 对象,我在其中存储用户首选项。所以默认情况下,假设我将性别过滤器的值存储为男性,将薪水范围存储为所有,等等。该对象作为参数发送到 EmployeeTableView。
加载 EmployeeTableView 后,我使用 userContext 值执行 restAPI 调用以获取员工详细信息。所以效果很好。现在我将性别筛选器更改为 Female 并在我的 userContext 中分配该值。
现在,如果我可以使用 userContext 对象重新加载 EmployeeTableView,restapi 调用将获得更新后的值。
但是我该怎么做呢?
如果你有,也建议一个更好的方法。
我最终使用了 Tornadofx -> EventBus
基本上,当我更改任何过滤器时,我会触发一个偶数,它会使用更新后的值重建节点。
不确定这个方法是否正确,所以仍然开放讨论。
EventBus 是一个有效的解决方案。另一种方法是使用 ViewModel 或 Controller 作为 UserContext 对象,让它包含实际可观察的员工列表,然后将该列表绑定到 EmployeeTableView
中的 TableView
。每当更新上下文中的列表时,TableView 也会更新。
过滤器视图将调用 UserContext 中的一个函数来执行实际的 REST 调用并根据该函数更新员工列表。
您可以创建一个单独的 EmployeeQuery 对象,该对象可以同时注入 EmployeeFilterView
和 UserContext
,以便它可以提取选定的过滤器值来执行查询。此查询对象包含要传递给服务器的所有搜索参数的列表。
如果这对您的体系结构有意义,您也可以考虑创建一个单独的作用域以将这些组件分开。
具体如何定义这些组件主要是个人喜好问题,这里有一个建议。我使用 ControlsFX 中的 RangeSlider
进行模拟搜索 UI.
为了更容易想象这是如何联系在一起的,这里有一张截图:
(所有姓名和薪水都是虚构的:)
/**
* The employee domain model, implementing JsonModel so it can be fetched
* via the REST API
*/
class Employee : JsonModel {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val salaryProperty = SimpleIntegerProperty()
var salary by salaryProperty
val genderProperty = SimpleObjectProperty<Gender>()
var gender by genderProperty
override fun updateModel(json: JsonObject) {
with (json) {
name = getString("name")
salary = getInt("salary")
gender = Gender.valueOf(getString("gender"))
}
}
}
enum class Gender { Male, Female }
/**
* Container for the list of employees as well as a search function called by the filter
* view whenever it should update the employee list.
*/
class EmployeeContext : Controller() {
val api: Rest by inject()
val query: EmployeeQuery by inject()
val employees = SimpleListProperty<Employee>()
fun search() {
runAsync {
FXCollections.observableArrayList(Employee().apply {
name = "Edvin Syse"
gender = Gender.Male
salary = 200_000
})
//api.post("employees/query", query).list().toModel<Employee>()
} ui {
employees.value = it
}
}
}
/**
* Query object used to define the query sent to the server
*/
class EmployeeQuery : ViewModel(), JsonModel {
val genderProperty = SimpleObjectProperty<Gender>(Gender.Female)
var gender by genderProperty
val salaryMinProperty = SimpleIntegerProperty(50_000)
var salaryMin by salaryMinProperty
val salaryMaxProperty = SimpleIntegerProperty(250_000)
var salaryMax by salaryMaxProperty
val salaryDescription = stringBinding(salaryMinProperty, salaryMaxProperty) {
"$$salaryMin - $$salaryMax"
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("gender", gender.toString())
add("salaryMin", salaryMin)
add("salaryMax", salaryMax)
}
}
}
/**
* The search/filter UI
*/
class EmployeeFilterView : View() {
val query: EmployeeQuery by inject()
val context: EmployeeContext by inject()
override val root = form {
fieldset("Employee Filter") {
field("Gender") {
combobox(query.genderProperty, Gender.values().toList())
}
field("Salary Range") {
vbox {
alignment = Pos.CENTER
add(RangeSlider().apply {
max = 500_000.0
lowValueProperty().bindBidirectional(query.salaryMinProperty)
highValueProperty().bindBidirectional(query.salaryMaxProperty)
})
label(query.salaryDescription)
}
}
button("Search").action {
context.search()
}
}
}
}
/**
* The UI that shows the search results
*/
class EmployeeTableView : View() {
val context: EmployeeContext by inject()
override val root = borderpane {
center {
tableview(context.employees) {
column("Name", Employee::nameProperty)
column("Gender", Employee::genderProperty)
column("Salary", Employee::salaryProperty)
}
}
}
}
/**
* A sample view that ties the filter UI and result UI together
*/
class MainView : View("Employee App") {
override val root = hbox {
add(EmployeeFilterView::class)
add(EmployeeTableView::class)
}
}