This is a question / proposal. In the past I've used a small extension of a Fragment when I needed to a get a result from a short-lived fragment.
Use case: the user opens a modal fragment, enters some data, closes the fragment, the data is now available where the user opened the fragment.
What I used to do:
abstract class ResultFragment<T>(title: String) : Fragment(title) {
// this should be private
lateinit var fn: (T) -> Unit
protected fun closeWithResult(result: T) {
fn.invoke(result)
close()
}
}
fun <R, S : ResultFragment<R>> openFragment(fragmentClass: KClass<S>, fn: (R) -> Unit) {
val fragment = find(fragmentClass)
fragment.fn = fn
fragment.openModal(block = true)
}
User class:
class AddAssignmentFragment : ResultFragment<Assignment>("Add Assignment")
Usage:
val assignments = ...
button("Add Assignment") {
action {
openFragment(AddAssignmentFragment::class, { assignments.add(it) })
}
}
I don't _quite_ remember why I needed this extension but I believe the motivation behind it was
that with the current approach in order to get back the data from the fragment we first need to instantiate the fragment and / or pass a specific parameter via injection?
Use scopes instead. Simply create a new scope, inject your viewmodel into the scope, then open the fragment in the new scope. The fragment modifies the viewmodel, and the data is available at the call site where you created the scope :)
Cheers 👍
Let me know if you can't get it to work smoothly and I'll create a sample :)
Sample will help beginners 😅
thanks
Agree with Awais, sample will help beginners.
feel very energetic to use the new technology, however kindly provide adequate documentation with samples for various scenarios.
I would suggest to take a sample application which must cover login to reporting functionality.
Sure, here is a complete example demonstrating how you can pass parameters between scopes. This is all covered in the guide, we can't give examples of everything, but all the knowledge you need to perform these operations are described in the guide. If you want to supply a demo application to the tornadofx-samples project, a PR is most welcome :)
class DemoApp : App(MainView::class)
class Person {
val nameProperty = SimpleStringProperty()
var name by nameProperty
}
class PersonModel : ItemViewModel<Person>(Person()) {
val name = bind(Person::nameProperty)
}
class MainView : View("Person list") {
val people = FXCollections.observableArrayList<Person>()
override val root = borderpane {
center {
tableview(people) {
column("Name", Person::nameProperty)
smartResize()
}
}
bottom {
hbox(spacing = 10) {
button("Add...").action {
// Create a new model and set some default values
val newPerson = PersonModel()
newPerson.name.value = "New name"
// Create a new scope and inject the PersonModel into that scope
// You can also add other objects that could work as parameters for the PersonEditor
val newScope = Scope(newPerson)
// Find the PersonEditor in the new scope and open a modal - wait for the result
find<PersonEditor>(newScope).openModal(block = true)
// Insert the edited Person into the table in this View
// You could access any object in the scope of the editor from here
people.add(newPerson.item)
}
}
}
}
}
class PersonEditor : Fragment("Person Editor") {
val person: PersonModel by inject()
override val root = form {
fieldset {
field("Name") {
textfield(person.name).required()
}
}
button("Save and close") {
isDefaultButton = true
action {
person.commit {
close()
}
}
}
}
}
Thanks Edvin,
Tornadofx is really inspiring and from last 15 days I am back of learning it. After programming for last 23 years, the declarative way of writing the program is not setting yet…
BTW, have you ever seen Powebuilder Datawindow ?, if no, kindly have a look, it was one of the best way of developing the database oriented Front End programming tool.
if we can achieve the same in Torndaofx, the development will become more of functional than the technical.
Please ignore, my silly questions as I am new to Tornado.
Able to achieve a bit of it, still the work is in progress.
Question
Can we get itemviewmodel from the table.
Basically, wanted to achieve the functionality wherein on selection of the row, we pass directly the item view model so that we don’t have to refer individual columns.
if we get the itemviewmodel in the table itself, we will not have to map individual columns like below
person.name.value = selectedItem!!.name;
person.age.value = selected!!.age...
instead of mapping individual column and then sending it to fragment, we can directly send the view model itself.
onUserSelect {
println(model!!.name)
var model = editModel; —>
var rowNumber = selectedCell!!.row
callEdit(rowNumber,model);
}
fun callEdit(rowNumber : Int,person : PersonModel)
{
person.name.value = selectedItem!!.name;
val newScope = Scope(person)
find
people.removeAt(rowNumber);
//people.add(newPerson.item);
people.add(rowNumber,person.item);
}
Regards
Niranjan G Shah
On 28-Jan-2018, at 12:49 AM, Edvin Syse notifications@github.com wrote:
Sure, here is a complete example demonstrating how you can pass parameters between scopes. This is all covered in the guide, we can't give examples of everything, but all the knowledge you need to perform these operations are described in the guide. If you want to supply a demo application to the tornadofx-samples project, a PR is most welcome :)
class DemoApp : App(MainView::class)
class Person {
val nameProperty = SimpleStringProperty()
var name by nameProperty
}class PersonModel : ItemViewModel
(Person()) {
val name = bind(Person::nameProperty)
}class MainView : View("Person list") {
val people = FXCollections.observableArrayList() override val root = borderpane { center { tableview(people) { column("Name", Person::nameProperty) smartResize() } } bottom { hbox(spacing = 10) { button("Add...").action { // Create a new model and set some default values val newPerson = PersonModel() newPerson.name.value = "New name" // Create a new scope and inject the PersonModel into that scope // You can also add other objects that could work as parameters for the PersonEditor val newScope = Scope(newPerson) // Find the PersonEditor in the new scope and open a modal - wait for the result find<PersonEditor>(newScope).openModal(block = true) // Insert the edited Person into the table in this View // You could access any object in the scope of the editor from here people.add(newPerson.item) } } } }}
class PersonEditor : Fragment("Person Editor") {
val person: PersonModel by inject()override val root = form { fieldset { field("Name") { textfield(person.name).required() } } button("Save and close") { isDefaultButton = true action { person.commit { close() } } } }}
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub https://github.com/edvin/tornadofx/issues/511#issuecomment-361008224, or mute the thread https://github.com/notifications/unsubscribe-auth/AI4lwvv5OVimePm9IWFNclrPbRRBmimyks5tO3a2gaJpZM4QEmHh.
I don't understand what you mean - the complete Person object is already passed to onUserSelect function as a parameter. You can just wrap that in a viewmodel and pass to the PersonEditor, then any changes committed to that viewmodel will be immediately visible in the table.
Most helpful comment
Sure, here is a complete example demonstrating how you can pass parameters between scopes. This is all covered in the guide, we can't give examples of everything, but all the knowledge you need to perform these operations are described in the guide. If you want to supply a demo application to the tornadofx-samples project, a PR is most welcome :)