简体   繁体   中英

Tornadofx Javafx - How to reload a view / component

So its a basic question.
What I am trying to achieve is refreshing views from another views.

Lets say I have a view EmployeeTableView which shows a tabular representation of employees by doing a REST API call.
In another view, I have a the filter EmployeeFilterView wherein I have gender, salary range, employee type, etc.
I also have a userContext object in which I store the user preferences. So by default lets say I have stored the value of gender filter to be Male, salary range to be ALL, etc. This object is send as a parameter to the EmployeeTableView.

When the EmployeeTableView is loaded I do a restAPI call with the userContext values to get the employee details. So that works fine. Now I change the gender filter to Female and assign this value in my userContext.
Now if I could just reload the EmployeeTableView with the userContext object, the restapi call would get the updated values.

But how can I do that ?
Also suggest a better approach if you have.

The EventBus is one valid solution to this. Another would be to use a ViewModel or Controller as the UserContext object and let that include the actual observable list of employees and then bind that list to the TableView in EmployeeTableView . Whenever the list in the context is updated, the TableView will update as well.

The filter view would call a function in the UserContext to perform the actual REST call and update the list of employees based on that.

You could create a separate EmployeeQuery object that can be injected into both the EmployeeFilterView and the UserContext so it can extract the selected filter values to perform the query. This query object contains a list of all the search parameters you want to pass to the server.

You could also consider creating a separate scope to keep these components separated if that makes sense to your architecture.

Exactly how you define these components are mostly a matter of taste, here is one suggestion. I used the RangeSlider from ControlsFX for the mock search UI.

To make it easier to imagine how this ties together, here is a screenshot:

(All names and salaries are fiction :)

员工应用

/**
 * 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)
    }
}

I ended up using Tornadofx -> EventBus

Basically, when I change any of the filters, I fire an even which rebuilds the Node with the updated values.

Not sure whether the approach is right, that's why still keeping it open for discussion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM