Tornadofx – Cómo pasar el parámetro a Fragment en cada instancia

Soy un novato en javafx, kotlin y obviamente tornadofx.
Problema :
¿Cómo pasar parameters a Fragment en cada instancia?

Digamos que tengo un layout de vista de tabla como mi fragment. Ahora este fragment se usa en varios lugares pero con diferentes sets de datos.

p.ej. Agregar un fragment en:

class SomeView : View() { ... root += SomeViewFragment::class } class SomeAnotherView : View() { ... root += SomeViewFragment::class } 

Declarando Fragmento:

 class SomeViewFragment : Fragment() { ... tableview(someDataSetFromRestApiCall) { ... } } 

¿Cómo puedo pasar diferentes someDataSetFromRestApiCall desde SomeView y SomeAnotherView?

Comencemos con la forma más explícita de pasar datos a Fragmentos. Para este ejemplo de TableView, podría exponer una list observable dentro del Fragmento y vincular su TableView a esta list. Luego puede actualizar esa list desde fuera del Fragmento y hacer que sus cambios se reflejen en el fragment. Para el ejemplo, creé un object de datos simple con una propiedad observable llamada SomeItem :

 class SomeItem(name: String) { val nameProperty = SimpleStringProperty(name) var name by nameProperty } 

Ahora podemos definir SomeViewFragment con una propiedad de elemento asociada a TableView:

 class SomeViewFragment : Fragment() { val items = FXCollections.observableArrayList<SomeItem>() override val root = tableview(items) { column("Name", SomeItem::nameProperty) } } 

Si luego actualiza el contenido de los elementos, los cambios se reflejarán en la tabla:

 class SomeView : View() { override val root = stackpane { this += find<SomeViewFragment>().apply { items.setAll(SomeItem("Item A"), SomeItem("Item B")) } } } 

A continuación, puede hacer lo mismo con SomeOtherView pero con otros datos:

 class SomeOtherView : View() { override val root = stackpane { this += find<SomeViewFragment>().apply { items.setAll(SomeItem("Item B"), SomeItem("Item C")) } } } 

Si bien esto es fácil de entender y muy explícito, crea un acoplamiento muy fuerte entre sus componentes. Es posible que desee considerar el uso de ámbitos para esto en su lugar. Ahora tenemos dos opciones:

  1. Use la inyección dentro del scope
  2. Deje que el scope contenga los datos

Use la inyección dentro del scope

Iremos primero con la opción 1, inyectando el model de datos. Primero creamos un model de datos que puede contener nuestra list de elementos:

 class ItemsModel(val items: ObservableList<SomeItem>) : ViewModel() 

Ahora inyectamos este ItemsModel en nuestro Fragment y extraemos los elementos de ese model:

 class SomeViewFragment : Fragment() { val model: ItemsModel by inject() override val root = tableview(model.items) { column("Name", SomeItem::nameProperty) } } 

Por último, necesitamos definir un scope separado para los fragments en cada vista y preparar los datos para ese scope:

 class SomeView : View() { override val root = stackpane { // Create the model and fill it with data val model= ItemsModel(listOf(SomeItem("Item A"), SomeItem("Item B")).observable()) // Define a new scope and put the model into the scope val fragmentScope = Scope() setInScope(model, fragmentScope) // Add the fragment for our created scope this += find<SomeViewFragment>(fragmentScope) } } 

Tenga en count que la function setInScope utilizada anteriormente estará disponible en TornadoFX 1.5.9. Mientras tanto, puedes usar:

 FX.getComponents(fragmentScope).put(ItemsModel::class, model) 

Deje que el scope contenga los datos

Otra opción es poner datos directamente en el scope. Vamos a crear un ItemsScope en ItemsScope lugar:

 class ItemsScope(val items: ObservableList<SomeItem>) : Scope() 

Ahora nuestro fragment esperará get una instancia de SomeItemScope para que SomeItemScope y extraemos los datos:

 class SomeViewFragment : Fragment() { override val scope = super.scope as ItemsScope override val root = tableview(scope.items) { column("Name", SomeItem::nameProperty) } } 

La Vista necesita hacer less trabajo ahora ya que no necesitamos el model:

 class SomeView : View() { override val root = stackpane { // Create the scope and fill it with data val itemsScope= ItemsScope(listOf(SomeItem("Item A"), SomeItem("Item B")).observable()) // Add the fragment for our created scope this += find<SomeViewFragment>(itemsScope) } } 

Pasar parameters

EDITAR : como resultado de esta pregunta, decidimos include soporte para pasar parameters con find e inject . Desde TornadoFX 1.5.9 puede enviar la list de elementos como un parámetro como este:

 class SomeView : View() { override val root = stackpane { val params = "items" to listOf(SomeItem("Item A"), SomeItem("Item B")).observable() this += find<SomeViewFragment>(params) } } 

El SomeViewFragment ahora puede recoger estos parameters y usarlos directamente:

 class SomeViewFragment : Fragment() { val items: ObservableList<SomeItem> by param() override val root = tableview(items) { column("Name", SomeItem::nameProperty) } } 

Por favor, no que esto implique un lanzamiento no verificado dentro del Fragmento.

Otras opciones

También podría pasar parameters y datos a través de EventBus, que también estará en el TornadoFX 1.5.9 que pronto se lanzará. EventBus también es compatible con ámbitos, lo que facilita la orientación de sus events.

Otras lecturas

Puede leer más sobre Scopes, EventBus y ViewModel en la guía:

Alcances

EventBus

ViewModel y validation

  • TornadoFX filechooser
  • TornadoFX envuelve las classs de dominio en ItemViewModel
  • java.lang.ClassNotFoundException: no encontró la class "com.my.app.example.Main" en la ruta: DexPathList
  • Tornadofx - controlando el object de la fila de la vista de tabla mientras construye
  • Lanzar una aplicación TornadoFX desde (un progtwig existente) de Kotlin
  • Las properties vinculadas no funcionan
  • Cómo inyectar ItemViewModel en tornadoFx
  • CellCache se representa inesperadamente en TableView con tornadoFX
  • TornadoFX: permite copyr un elemento de una vista de list al portapapeles
  • Cómo cambiar la vista en TornadoFx
  • Animación consecutiva en TornadoFX?