【问题标题】:Tornadofx Javafx - How to reload a view / componentTornadofx Javafx - 如何重新加载视图/组件
【发布时间】:2017-04-29 14:37:31
【问题描述】:

所以这是一个基本问题。
我想要实现的是从另一个视图中刷新视图。

假设我有一个 EmployeeTableView 视图,它通过调用 REST API 以表格形式显示员工。
在另一个视图中,我有一个过滤器 EmployeeFilterView,其中我有性别、工资范围、员工类型等。
我还有一个 userContext 对象,用于存储用户首选项。所以默认情况下,假设我将性别过滤器的值存储为男性,工资范围为 ALL,等等。这个对象作为参数发送到 EmployeeTableView。

加载 EmployeeTableView 后,我使用 userContext 值调用 restAPI 以获取员工详细信息。所以效果很好。现在我将性别过滤器更改为女性并在我的 userContext 中分配这个值。
现在,如果我可以使用 userContext 对象重新加载 EmployeeTableView,restapi 调用将获取更新后的值。

但是我该怎么做呢?
如果你有的话,也建议一个更好的方法。

【问题讨论】:

    标签: javafx kotlin tornadofx


    【解决方案1】:

    EventBus 是一种有效的解决方案。另一种方法是使用 ViewModel 或 Controller 作为 UserContext 对象,并让它包含实际可观察​​的员工列表,然后将该列表绑定到 EmployeeTableView 中的 TableView。每当上下文中的列表更新时,TableView 也会更新。

    过滤视图会调用 UserContext 中的一个函数来执行实际的 REST 调用,并根据它更新员工列表。

    您可以创建一个单独的 EmployeeQuery 对象,该对象可以注入到 EmployeeFilterViewUserContext 中,这样它就可以提取选定的过滤器值来执行查询。此查询对象包含您要传递给服务器的所有搜索参数的列表。

    如果这对您的架构有意义,您还可以考虑创建一个单独的范围以将这些组件分开。

    您如何定义这些组件主要是个人喜好问题,这里有一个建议。我使用 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)
        }
    }
    

    【讨论】:

    • 哇,这是一种巧妙的做法。谢谢 !!对我帮助很大。
    • 太棒了! ViewModel,尤其是 ItemViewModel 是将不同的 UI 组件绑定在一起的绝佳工具。我经常使用它。
    【解决方案2】:

    我最终使用 Tornadofx -> EventBus

    基本上,当我更改任何过滤器时,我会触发一个使用更新值重建节点的偶数。

    不确定该方法是否正确,这就是为什么仍然保持开放以供讨论。

    【讨论】:

    • 好电话。今晚我将发布另一个建议的答案:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 2015-08-29
    • 1970-01-01
    • 2011-04-26
    • 2018-12-24
    • 2019-06-03
    • 2018-02-22
    相关资源
    最近更新 更多