绑定不同视图模型的脏属性
Bind dirty properties of different view-models
我有一个 tornadoFX 应用程序遵循模型的 MVVM 模式:
data class Person (
val name: String,
val cars: List<Car>
)
data class Car (
val brand: String,
val model: String
)
应用程序定义了以下视图:
有一个列出所有人员的列表视图。除了 listView 之外,还有一个详细信息视图,其中包含人员姓名的文本字段和人员汽车的 table 视图。
双击 table 中的汽车条目会打开一个对话框,您可以在其中编辑汽车的属性。
我想,如果我打开汽车详细信息并编辑一个条目,所做的更改将反映在 table- 视图中。因为我不能通过添加 fx-properties 来改变汽车模型(这是一个 immutable 类型),所以我想出了以下视图模型:
class PersonViewModel(): ItemViewModel<Person> {
val name = bind(Person::name)
val cars = bind { SimpleListProperty<CarViewModel>(item?.cars?.map{CarViewModel(it)}?.observable()) }
override fun onCommit {
// create new person based on ViewModel and store it
}
}
class CarViewModel(item: Car): ItemViewModel<Car> {
val brand = bind(Car::name)
val model = bind(Car::model)
init {
this.item = item
}
}
这样,如果双击table视图中的汽车条目并打开汽车详细信息视图,则汽车的更新将直接反映在table-视图。
我的问题是,我找不到将 table 中所有 CarViewModel 的脏属性绑定到 PersonViewModel 的方法。因此,如果我换车,PersonViewModel 不会被标记为脏。
有没有办法绑定 PersonViewModel 和 CarViewModel 的脏属性? (如果选择了另一个人,还要重新绑定他们)。
或者是否有更好的方法来定义我的视图模型?
我对框架进行了更改,以允许 ViewModel 绑定到列表以观察 ListChange 事件。这使您能够通过以某种方式更改列表来触发列表 属性 的脏状态。仅更改列表中项目内的 属性 不会触发它,因此在下面的示例中,我只是在提交之前获取 Car 的索引,并将 Car 重新分配给相同的索引。这将触发框架现在侦听的 ListChange 事件。
汽车对话框保存功能中发生的重要操作:
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
在提交值之前记录汽车的索引(以确保equals/hashCode匹配相同的条目),然后将新提交的项目插入相同的索引中,从而触发更改事件在名单上。
这是一个完整的示例,使用可变的 JavaFX 属性,因为它们是惯用的 JavaFX 方式。您可以很容易地使它适应使用不可变项,或使用包装器。
class Person(name: String, cars: List<Car>) {
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
val carsProperty = SimpleListProperty<Car>(FXCollections.observableArrayList(cars))
var cars by carsProperty
}
class PersonModel : ItemViewModel<Person>() {
val name = bind(Person::nameProperty)
val cars: SimpleListProperty<Car> = bind(Person::carsProperty)
}
class Car(brand: String, model: String) {
val brandProperty = SimpleStringProperty(brand)
var brand by brandProperty
val modelProperty = SimpleStringProperty(model)
var model by modelProperty
}
class CarModel(car: Car? = null) : ItemViewModel<Car>(car) {
val brand = bind(Car::brandProperty)
val model = bind(Car::modelProperty)
}
class DataController : Controller() {
val people = FXCollections.observableArrayList<Person>()
init {
people.add(
Person("Person 1", listOf(Car("BMW", "M3"), Car("Ford", "Fiesta")))
)
}
}
class PersonMainView : View() {
val data: DataController by inject()
val selectedPerson: PersonModel by inject()
override val root = borderpane {
center {
tableview(data.people) {
column("Name", Person::nameProperty)
bindSelected(selectedPerson)
}
}
right(PersonEditor::class)
}
}
class PersonEditor : View() {
val person: PersonModel by inject()
val selectedCar : CarModel by inject()
override val root = form {
fieldset {
field("Name") {
textfield(person.name).required()
}
field("Cars") {
tableview(person.cars) {
column("Brand", Car::brandProperty)
column("Model", Car::modelProperty)
bindSelected(selectedCar)
onUserSelect(2) {
find<CarEditor>().openModal()
}
}
}
button("Save") {
enableWhen(person.dirty)
action {
person.commit()
}
}
}
}
}
class CarEditor : View() {
val car: CarModel by inject()
val person: PersonModel by inject()
override val root = form {
fieldset {
field("Brand") {
textfield(car.brand).required()
}
field("Model") {
textfield(car.model).required()
}
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
}
}
}
该功能在 TornadoFX 1.7.17-SNAPSHOT 中可用。
我有一个 tornadoFX 应用程序遵循模型的 MVVM 模式:
data class Person (
val name: String,
val cars: List<Car>
)
data class Car (
val brand: String,
val model: String
)
应用程序定义了以下视图:
有一个列出所有人员的列表视图。除了 listView 之外,还有一个详细信息视图,其中包含人员姓名的文本字段和人员汽车的 table 视图。
双击 table 中的汽车条目会打开一个对话框,您可以在其中编辑汽车的属性。
我想,如果我打开汽车详细信息并编辑一个条目,所做的更改将反映在 table- 视图中。因为我不能通过添加 fx-properties 来改变汽车模型(这是一个 immutable 类型),所以我想出了以下视图模型:
class PersonViewModel(): ItemViewModel<Person> {
val name = bind(Person::name)
val cars = bind { SimpleListProperty<CarViewModel>(item?.cars?.map{CarViewModel(it)}?.observable()) }
override fun onCommit {
// create new person based on ViewModel and store it
}
}
class CarViewModel(item: Car): ItemViewModel<Car> {
val brand = bind(Car::name)
val model = bind(Car::model)
init {
this.item = item
}
}
这样,如果双击table视图中的汽车条目并打开汽车详细信息视图,则汽车的更新将直接反映在table-视图。
我的问题是,我找不到将 table 中所有 CarViewModel 的脏属性绑定到 PersonViewModel 的方法。因此,如果我换车,PersonViewModel 不会被标记为脏。
有没有办法绑定 PersonViewModel 和 CarViewModel 的脏属性? (如果选择了另一个人,还要重新绑定他们)。
或者是否有更好的方法来定义我的视图模型?
我对框架进行了更改,以允许 ViewModel 绑定到列表以观察 ListChange 事件。这使您能够通过以某种方式更改列表来触发列表 属性 的脏状态。仅更改列表中项目内的 属性 不会触发它,因此在下面的示例中,我只是在提交之前获取 Car 的索引,并将 Car 重新分配给相同的索引。这将触发框架现在侦听的 ListChange 事件。
汽车对话框保存功能中发生的重要操作:
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
在提交值之前记录汽车的索引(以确保equals/hashCode匹配相同的条目),然后将新提交的项目插入相同的索引中,从而触发更改事件在名单上。
这是一个完整的示例,使用可变的 JavaFX 属性,因为它们是惯用的 JavaFX 方式。您可以很容易地使它适应使用不可变项,或使用包装器。
class Person(name: String, cars: List<Car>) {
val nameProperty = SimpleStringProperty(name)
var name by nameProperty
val carsProperty = SimpleListProperty<Car>(FXCollections.observableArrayList(cars))
var cars by carsProperty
}
class PersonModel : ItemViewModel<Person>() {
val name = bind(Person::nameProperty)
val cars: SimpleListProperty<Car> = bind(Person::carsProperty)
}
class Car(brand: String, model: String) {
val brandProperty = SimpleStringProperty(brand)
var brand by brandProperty
val modelProperty = SimpleStringProperty(model)
var model by modelProperty
}
class CarModel(car: Car? = null) : ItemViewModel<Car>(car) {
val brand = bind(Car::brandProperty)
val model = bind(Car::modelProperty)
}
class DataController : Controller() {
val people = FXCollections.observableArrayList<Person>()
init {
people.add(
Person("Person 1", listOf(Car("BMW", "M3"), Car("Ford", "Fiesta")))
)
}
}
class PersonMainView : View() {
val data: DataController by inject()
val selectedPerson: PersonModel by inject()
override val root = borderpane {
center {
tableview(data.people) {
column("Name", Person::nameProperty)
bindSelected(selectedPerson)
}
}
right(PersonEditor::class)
}
}
class PersonEditor : View() {
val person: PersonModel by inject()
val selectedCar : CarModel by inject()
override val root = form {
fieldset {
field("Name") {
textfield(person.name).required()
}
field("Cars") {
tableview(person.cars) {
column("Brand", Car::brandProperty)
column("Model", Car::modelProperty)
bindSelected(selectedCar)
onUserSelect(2) {
find<CarEditor>().openModal()
}
}
}
button("Save") {
enableWhen(person.dirty)
action {
person.commit()
}
}
}
}
}
class CarEditor : View() {
val car: CarModel by inject()
val person: PersonModel by inject()
override val root = form {
fieldset {
field("Brand") {
textfield(car.brand).required()
}
field("Model") {
textfield(car.model).required()
}
button("Save").action {
val index = person.cars.indexOf(car.item)
car.commit {
person.cars[index] = car.item
close()
}
}
}
}
}
该功能在 TornadoFX 1.7.17-SNAPSHOT 中可用。