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.
- TornadoFX reemplaza el layoutChildren en la región
- Cómo instalar un controller de clics en una vista de list dinámica (en tornadofx)
- En TornadoFX, ¿cómo puedo vincular las properties de BigDecimal a otra propiedad de BigDecimal usando objectBinding?
- En TornadoFX, ¿cómo puedo separar layouts en diferentes classs y luego usarlos en el generador?
- TornadoFx: que realiza mejor FXML o type-CSS
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?
- Usar bibliotecas de UI de terceros con TornadoFX
- tornadoFX togglebutton no tiene propiedad de text
- El text de la label no se actualiza aunque el hilo de la interfaz de usuario parece responder
- ¿Cómo cancelar la suscripción a events en TornadoFX?
- Cómo implementar TornadoFX WebEngine Callback en Kotlin
- Tornadofx Javafx - Cómo volver a cargar una vista / componente
- Tornadofx tableview sincroniza dos tablas
- No se puede hacer que el nodo se centre en un StackPane con TornadoFX
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:
- Use la inyección dentro del scope
- 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
- Efectos secundarios condicionales y types opcionales en Kotlin
- Gradle Script Kotlin y dependencyManagement