data class Customer(val id : Int, val firstName : String, val lastName : String) { override fun toString(): String { return "$firstName $lastName"; } } object CustomerModelUpdated : FXEvent() // thrown Model -> ViewModel class MainView : View("Customer App") { val customerViewModel : CustomerViewModel by inject() override val root = vbox { splitpane { this += find(CustomerListFragment::class) this += find(CustomerDetailsFragment::class) padding = Insets(4.0) } hbox { progressbar { progressProperty().bind( customerViewModel.taskProgress ) } label { textProperty().bind( customerViewModel.taskMessage ) } visibleProperty().bind( customerViewModel.taskRunning ) padding = Insets(4.0) spacing = 4.0 } } } class CustomerListFragment : Fragment() { val customerViewModel : CustomerViewModel by inject() override val root = vbox { button("Refresh") { setOnAction { refresh() } } listview { itemsProperty().bind( customerViewModel.customers ) selectionModel.selectedItemProperty().addListener { obs, ov, nv -> updateSelected(nv) } } padding = Insets(10.0) spacing = 4.0 } fun refresh() { customerViewModel.refresh() } fun updateSelected(newSelection : Customer?) { customerViewModel.updateSelected(newSelection) } } class CustomerDetailsFragment : Fragment() { val customerViewModel : CustomerViewModel by inject() override val root = vbox { label("First Name") textfield { textProperty().bind( customerViewModel.selectedFirstName ) } label("Last Name") textfield { textProperty().bind( customerViewModel.selectedLastName ) } padding = Insets(10.0) } } class CustomerViewModel : ViewModel() { val customerModel : CustomerModel by inject() val customers = SimpleObjectProperty>() val selectedFirstName = SimpleStringProperty() val selectedLastName = SimpleStringProperty() val taskRunning = SimpleBooleanProperty() val taskMessage = SimpleStringProperty() val taskProgress = SimpleDoubleProperty() init { subscribe { updateFromRefresh() } } fun refresh() { val t = object : Task() { override fun call() { updateMessage("Loading customers") updateProgress( 0.4, 1.0 ) customerModel.loadCustomers() } override fun succeeded() { fire( CustomerModelUpdated ); } } taskRunning.bind( t.runningProperty() ) taskProgress.bind( t.progressProperty() ) taskMessage.bind( t.messageProperty() ) Thread(t).start() } fun updateSelected(newSelection : Customer?) { if( newSelection == null ) { selectedFirstName.set("") selectedLastName.set("") } else { selectedFirstName.set( newSelection!!.firstName ) selectedLastName.set( newSelection!!.lastName ) } } fun updateFromRefresh() { customers.set( customerModel.customers.observable() ) } } class CustomerModel : Controller() { val api : Rest by inject() val customers = mutableListOf() fun loadCustomers() { customers.clear() api.get("customers.json") .list() .forEach{ val obj = it as JsonObject customers.add( Customer( obj.getInt("id"), obj.getString("firstName"), obj.getString("lastName") ) ) } fire( CustomerModelUpdated ) } } class CustomerApp : App(MainView::class) { val api : Rest by inject() override fun createPrimaryScene(view: UIComponent) = Scene(view.root, 568.0, 320.0 ) init { api.baseURI = "https://www.bekwam.net/data" } }