Coder Social home page Coder Social logo

rxkotlinfx's Introduction

UNSUPPORTED, PLEASE FORK AND SUPPORT

RxKotlinFX

Kotlin extensions to the RxJavaFX library. This framework works especially well with TornadoFX. See a fully functional demo here!

Documentation

Learning RxJava with JavaFX

Binaries

Binaries and dependency information for Maven, Ivy, Gradle and others can be found at http://search.maven.org.

Example for Maven:

<dependency>
    <groupId>com.github.thomasnield</groupId>
    <artifactId>rxkotlinfx</artifactId>
    <version>x.y.z</version>
</dependency>

Gradle:

repositories {
    mavenCentral()
}
dependencies {
    compile 'com.github.thomasnield:rxkotlinfx:x.y.z'
}

Contributing

Feel free to contribute and help streamline a pragmatic UI stack with Kotlin, RxJava, and JavaFX. Speaking of stacks, this project may be used in conjunction with TornadoFX and RxKotlin. Please make sure no extension naming conventions conflict with these two other libraries :)

Features

RxKotlinFX is the convergence of interoperability between RxJava, JavaFX, and Kotlin.

RxKotlinFX contains Kotlin extensions to RxJavaFX as well as additional Observable operators specific to JavaFX. It also is in exporatory stages to add helpful Node extension functions that return Observables. This exploration is inspired by the JavaFX/Kotlin interop project TornadoFX. Where TornadoFX handles layouts, node extensions, DI, and other JavaFX/Kotlin interoperations, this library will seek to integrate RxJava with JavaFX in the same spirit using Kotlin. The vision is to add useful extensions that put Observable streams as properties and functions on JavaFX components, especially where ObservableValue properties are not readily available.

RxJavaFX Extensions

The core API implements RxJavaFX static factories as extension functions.

Target Type Extension Function Description
Observable<T> toBinding() Subscribes the Observable<T> to a JavaFX Binding implementation. Calling dispose() will unsubscribe from the Observable<T>
Observable<T> toLazyBinding() Subscribes the Observable<T> to a lazy JavaFX Binding implementation, delaying subscription until a value is needed. Calling dispose() will unsubscribe from the Observable<T>
Property<T> bind(observable: Observable<T>) Binds a Property<T> to the emissions of an Observable<T>, and returns the Binding
Binding<T> addTo(compositeBinding: CompositeBinding) Adds the Binding to a CompositeBinding, and returns the Binding
ObservableValue<T> toObservable() Turns a JavaFX ObservableValue<T> into an RxJava Observable<T> that emits the latest value
ObservableValue<T> toObservableChanges() Turns a JavaFX ObservableValue<T> into an RxJava Observable<Change<T>> that emits the old value and new value as a pair
Dialog<T> toObservable<T> Returns an Observable<T> that emits the given result of Dialog<T>. Will be empty if no response.
Node, Window, Scene events(eventType: EventType) Creates an Observable emitting events of the given EventType
Node, MenuItem, ContextMenu actionEvents() Creates an Observable that emits an ActionEvent every time one occurs
ObservableList<T> onChangedObservable() Returns an Observable<ObservableList<T>> that emits the entire ObservableList<T> every time it is modified.
ObservableList<T> additions() Creates an Observable<T> emitting T items that were added to the ObservableList<T>
ObservableList<T> removals() Creates an Observable<T> emitting T items that were removed from the ObservableList<T>
ObservableList<T> updates() Creates an Observable<T> emitting T items whose specified properties were updated in the ObservableList<T>
ObservableList<T> changes() Creates an Observable<ListChange<T>> emitting ListChange<T> items, which pairs each item with an ADDED, REMOVED, or UPDATED flag
ObservableList<T> distinctChanges() Creates an Observable<ListChange<T>> emitting distinct ListChange<T> items. It will only emit the first ADDED item T and not emit dupes, and will only emit the REMOVED item T when no more dupes exist
ObservableList<T> distinctChanges(mapper: (T) -> R) Creates an Observable<ListChange<T>> emitting distinct ListChange<T> items based off the mapper's definition of a distinct value R. It will only emit the first ADDED item T and not emit dupes, and will only emit the REMOVED item T when no more dupes exist
ObservableList<T> distinctMappingChanges(mapper: (T) -> R) Creates an Observable<ListChange<R>> emitting distinct ListChange<R> mappings based off the mapper's definition of a distinct value R. It will only emit the first ADDED item R and not emit dupes, and will only emit the REMOVED item R when no more dupes exist
Observable of Button ActionEvents
val myButton = Button("Press Me")
val subscription = myButton.actionEvents().subscribe { println("Pressed!") } 
Creating a Reactive Binding
val myButton = Button("Press Me")

val countBinding = myButton.actionEvents().map { 1 }
    .scan(0, { x,y -> x + y })
    .map { it.toString() }
    .toBinding()
    
val myLabel = Label()
myLabel.textProperty().bind(countBinding)
Observable of ObservableList Events
val items = FXCollections.observableArrayList("Alpha", "Beta", "Gamma")

val changes = items.changes()

changes.filter { it.flag == Flag.ADDED }
        .map { it.value }
        .subscribe { println("ADDED $it") }

items.add("Delta")
items.add("Epsilon")
OUTPUT
ADDED Delta
ADDED Epsilon
Turning an ObservableList into a Hot Concatenation
val observableList = FXCollections.observableArrayList<String>()

observableList.onChangedObservable()
        .flatMap {
            it.toObservable().map { it.length }
                    .map { it.toString() }
                    .reduce { s1,s2 -> s1 + "|"  + s2  }
        }
        .subscribe { println(it) }

observableList.setAll("Alpha", "Beta", "Gamma")
observableList.add("Delta")
observableList.add("Epsilon")
observableList.remove("Alpha")
OUTPUT
5|4|5
5|4|5|5
5|4|5|5|7
4|5|5|7
Using a Dialog or Alert
val dialog = Alert(AlertType.CONFIRMATION, "Are you sure you want to continue?")

dialog.toObservable().filter { it == ButtonType.YES }
	.subscribe { println("You pressed YES") } 
Using and Disposing CompositeBinding
val binding1: Binding = ...
val binding2: Binding = ... 

//adding one at a time
val bindings = CompositeBinding()
val bindings += binding1
val bindings += binding2

//or all at once
val bindings = CompositeBinding(binding1,binding2)

//do stuff, then dispose Bindings
bindings.dispose()

Operators

RxKotlinFX has a growing list of operators placed as extension functions onto Observable that aid interoperability with JavaFX.

Operator Description
observeOnFx() Schedules the emissions to be observed on the JavaFX thread
subscribeOnFx() Schedules the source Observable to emit items on the JavaFX thread
doOnNextFx() Executes the specified action on the FX thread for each emission
doOnErrorFx() Executes the specified action on the FX thread when an error is emitted
doOnCompleteFx() Executes the specified action on the FX thread when the Observable calls onComplete()
doOnSubscribeFx() Executes the specified action on the FX thread when the Observable is first subscribed
doOnTerminateFx() Executes the specified action on the FX thread when the Observable calls onComplete() or onError()
doOnDisposeFx() Executes the specified action on the FX thread when the Observable is unsubscribed
doOnNextCount() Executes the specified action with the cumulative count of emissions for that emission
doOnErrorCount() Executes the specified action with the cumulative count of emissions when an error is emitted
doOnCompleteCount() Executes the specified action with the total emission count when onComplete() is called
doOnNextCountFx() Same as doOnNextCount() except action is executed on FX thread
doOnErrorCountFx() Same as doOnErrorCount() except action is executed on FX thread
doOnCompleteCountFx() Same as doOnCompleteCount() except action is executed on FX thread

The doOnXXXCount() operators are especially helpful for providing a status update of how many items have been "processed" by an Observable.

val source = Observable.range(1,1000)
val processedCountLabel = Label()

source.map { it * 10 }
     .doOnNextFx { processedCountLabel.text = "Processed $it items" }
     .subsribe { doSomethingWith(it) }

Control Extensions

The rest of the project will likely add convenient extension functions to emit events as Observable values, much like the TornadoFX project has done. For example, helpful Observable extension functions and properties can be added to TableView and ListView, such as selection events.

val tableView: TableView<MyItem> = ...
val selections: Observable<MyItem> = tableView.itemSelections
val rowIndexSelections: Observable<Int> = tableView.rowIndexSelections

Check releases as well the Nodes code file to see a list of available extensions. Feel free to contribute if you see any missing.

Bugs and Feedback

For bugs, questions and discussions please use the Github Issues.

LICENSE

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

rxkotlinfx's People

Contributors

thomasnield avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rxkotlinfx's Issues

Reactive Dialogs

One of the pain points I've had with Rx and JavaFX is putting a dialog somewhere in the middle of an Observable chain, usually to prevent emissions from going forward on some condition. It's messy and maybe I'm just not competent enough with maintaining modality, and I struggle to get it right.

But I found an interesting pattern that seems to work consistently: Make the dialog return an Observable of the result. Note I am using TornadoFX in this example.

class YesNoConfirmDialog(val message: String) : Fragment() {
    override val root = BorderPane()
    var result: Observable<Response> by singleAssign()
    init {
        with(root) {
            top {
                label(message)
            }
            center {
                hbox {
                    alignment = Pos.BASELINE_CENTER
                    result = Observable.concat(
                            button("YES").actionEvents().map { Response.YES },
                            button("NO").actionEvents().map { Response.NO }
                    ).take(1).doOnCompleted { this@YesNoConfirmDialog.closeModal() }
                }
            }
        }
        openModal()
    }
    enum class Response { YES, NO }
}

Then all I have to do is flatMap() to this dialog's result property which will emit as an Observable<Response>.

sourceObservable
    .flatMap { YesNoConfirmDialog("Are you sure you want item $it to be committed?").result }
    .subscribe { println("You selected $it") }

I would hate to build dialogs from scratch like I did above. It would be ideal to put extension functions on existing JavaFX dialogs that return the Observable. This may be worth exploring.

It might be even better to create simple functions that take care of creating and returning the result of dialogs.

sourceObservable
    .flatMap { yesNoConfirmDialog("Are you sure you want item $it to be committed?") }
    .subscribe { println("You selected $it") }

Question: How to route events between Views with singleton controller

While working on a TornadoFX application prototype which uses Reactive Extensions I'd like to 'route' events from
the SourceView to the TargetView. Currently I use a Controller which exposes a Property which is update via a subscription
in the SourceView.

I have some doubts about this approach and would like to learn about a idiomatic solution using TFX and RX.

class SourceView : View() {

    val signalController: SignalsController by inject()

    add(JFXToggleButton().apply {
        selectedProperty().toObservableChanges().subscribe { signalController.sourceToSignalProperty.value = selectedProperty().value; }
    })

}

class TargetView : View() {

    val signalController: SignalsController by inject()

    Observable.combineLatest(signalController.searchFieldProperty.toObservableChanges().startWith(Change<String>("", "")),
    signalController.protocolPredicateProperty.toObservableChanges().startWith(Change<List<(SignalProperty) -> Boolean>>(listOf(), listOf())),

    signalController.sourceToSignalProperty.toObservableChanges().startWith(Change(true, true)),

    Function3 { _: Change<String>, _: Change<List<(SignalProperty) -> Boolean>>, enable: Change<Boolean> -> { enable.newVal } })
    .forEach { f ->
        if (f()) doSomeFancyStuff()
    }
}

class SignalsController : Controller() {

    val sourceToSignalProperty = SimpleBooleanProperty(true)
    var sourceToSignal by sourceToSignalProperty
}

Some helpful patterns I'm using.

Didn't want to hijack edvin/tornadofx#120, so demonstrating to some of the patterns I'm using in my codebase.

I'm currently in the middle of a large refactoring, so this code might have some super obvious bugs. It worked before, honest!

RxProperty.kt:

class RxProperty<T>(initialValue: T) {
    val observable: Observable<T>
    private var propertyField: T = initialValue
    private val subject = BehaviorSubject.create<T>(propertyField)

    companion object {
        /**
         * Two way binding between two RxProperty objects.
         * It relies on Observable.distinctUntilChanged() to prevent
         * infinite loops.
         */
        fun <T> bind(property1: RxProperty<T>, property2: RxProperty<T>) {
            property1.bindTo(property2)
            property2.bindTo(property1)
        }
    }

    init {
        propertyField = initialValue
        observable = subject.distinctUntilChanged()
    }

    // TODO: followed javafx here -- should have just done "value =" get/set for better interop
    fun get(): T = propertyField
    fun set(value: T): RxProperty<T> {
        propertyField = value
        subject.onNext(value)
        return this
    }
    // TODO: Both of these were a mistake: should have just done asObservable or toObservable
    fun observeOn(scheduler: Scheduler) = observable.observeOn(scheduler)
    fun subscribe(callback: (value: T) -> Unit) = observable.subscribe(callback)

    /**
     * Create a one way binding from this object to another RxProperty
     */
    fun bindTo(receiver: RxProperty<T>): Subscription {
        return subscribe { receiver.set(it) }
    }

RxProxy, a helper delegate for RxProperty:

/**
 * Maps a field to an RxProperty, so that it can be used like a getter/setter bean.
 *
 * Example:
 *  class Demo {
 *      val textProperty = RxProperty("default text")
 *      val text by RxProxy(textProperty)
 *
 *      fun test() {
 *        text = "One, two, three!"
 *      }
 *  }
 */
class RxProxy<T>(val rxProperty: RxProperty<T>) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>) = rxProperty.get()


    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        rxProperty.set(value)
    }
}

Disposables:

interface DisposableResource {
    fun dispose()
}

/**
 * Lifecycle object that unsubscribes all its subscriptions when disposed
 */
class RxDisposable : DisposableResource {
    val subscriptions = CompositeSubscription()

    fun addSusbcription(subscription: Subscription) {
        subscriptions.add(subscription)
    }

    override fun dispose() {
        subscriptions.unsubscribe()
    }
}

disposeWith

fun Subscription.disposeWith(instance: RxDisposable) {
    instance.addSusbcription(this)
}

Misc?

fun <R, T : R> Observable<T>.bindTo(property: RxProperty<R>): Subscription {
    return subscribe { property.set(it) }
}

fun <T> Observer<T>.bindTo(observer: Observable<T>): Subscription {
    return observer.subscribe(this)
}

I haven't upgraded my copy of RxKotlinFX in a while, so some of these patterns might already exist or have a better solution. The disposeWith pattern came from RxSwift, where they have "DisposeBags" that handle disposing subscriptions automatically. I've generalized it to ResourceDisposable so I can dispose of other, non-Rx resources when necessary.

Use builder for `toBinding()` arguments

Someone told me that the syntax myObservable.toBinding { it.printStackTrace() } makes it look like the Binding is being bound to the stack trace.

It may be better to use a SubscribeWith-like builder instead.

someObservable.toBinding { 
    onError { it.printStackTrace() }
    onCompleted { println("done!") }
}

I don't know if this can be solved for Java as it has the same problem: myObservable.toBinding(e -> e.printStackTrace()).

how to handler with custom alert action

i try to create an custom alert with 3 button like this
`eventController.saveDanhMucDVKT
.map { table.items }
.flatMapSingle { lstDMDVKT ->
val dialog = Alert(Alert.AlertType.CONFIRMATION, "Lựa chọn hình thức lưu dữ liệu",
ButtonType("Save", ButtonBar.ButtonData.YES),
ButtonType("Save and delete", ButtonBar.ButtonData.OK_DONE),
ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE))

                //save but not remove old data
                dialog.toMaybe().filter { it == ButtonType.YES }
                        .map { lstDMDVKT }
                        .flatMapSingle {
                            danhMucDVKTController.createList(it)
                        }.toObservable().toSet()

                //save and remove old data
                dialog.toMaybe().filter { it == ButtonType.OK }
                        .map { lstDMDVKT }
                        .flatMapSingle {
                            danhMucDVKTController.delete()
                            danhMucDVKTController.createList(it)
                        }.toObservable().toSet()
            }.publish()`

but seem it not save data to database. which way i wrong

Wrong receiver type of the extension function in the Observables.kt

Hello! Thank you for your library! Here is a possible error which i found.
In the Observables.kt file we have the following line of code:
fun <T> ObservableSet<SetChange<T>>.changes() = JavaFxObservable.changesOf(this)
It seems there is an error in the receiver type of this function and it should be changed to:
fun <T> ObservableSet<T>.changes() = JavaFxObservable.changesOf(this) .

`eagerConnect()` extension function

There are times, especially with CompositeObservable, where I want to call replay(1) to hold onto the last value emitted (e.g. from a ComboBox) but I can lose it with refCount() when there are no more subscribers. autoConnect() is a better choice but it still has to wait for a subscriber. Using autoConnect(0) to eagerly mutlicast emissions behaves strangely and feels like it blocks in some situations.

But I've been using this extension function at work called eagerConnect() and it seems to work nicely.

fun <T> ConnectableObservable<T>.eagerConnect() = autoConnect().apply { subscribe().unsubscribe() }

I may add this to RxKotlinFX since this can be useful for JavaFX environments.

Why is the value of the val in the closure seemingly being mutated?

For the code below, why does xnow change along with x? e.g. I was expecting to see the "Observer N received click" to retain the N value that was assigned to xnow when the closure was initially created...

Observer 2 subscribing!
Observer 1 received click
Observer 2 received click
Observer 3 subscribing!
Observer 1 received click
Observer 3 received click
Observer 3 received click
Observer 4 subscribing!
Observer 1 received click
Observer 4 received click
Observer 4 received click
Observer 4 received click
import tornadofx.*
import io.reactivex.rxkotlin.*
import io.reactivex.Observable
import javafx.scene.control.*

class MyApp: App(MyView::class)

class MyView: View() {
  override val root = vbox {
    var clicks = button("Pressme").actionEvents()
    var x = 1
    clicks.subscribe { println("Observer 1 received click") }

    button("Subscribe New Observer") {
      setOnAction {
        x++
        println("Observer $x subscribing!")
        clicks.subscribe { val xnow=x; println("Observer $xnow received click")}
      }
    }
  }
}

doOnXXX Operators for FX Thread

Might be helpful to create extension function operators that create side effects on FX thread

doOnFx
doOnCompletedFx
doOnErrorFx
doOnSubscribeFx

How to run this application

Hi

I've downloaded this application to study the code but I couldn't run it even though after I have updated the gradle. I also build it which was created a build folder containing all the compiled files; therefore I could not locate the main function. Any Ideas would pretty helpful.

Thanks a lot.

Connection leak on execute (Hikari)

I'm using rxkotlinfx and a Hikari data source in a TornadoFX project and found that a connection is leaking whenever I use fun DataSource.execute to delete objects from an underlying PostgreSQL database. I came across this by enabling logging with the JVM parameter -Dorg.slf4j.simpleLogger.log.com.zaxxer.hikari=debug and setting the hikari config's leakDetectionThreshold property to 60_000 ms, which showed the following output in my console:

[HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=10, active=0, idle=10, waiting=0)
[HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=10, active=1, idle=9, waiting=0)
[HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=10, active=1, idle=9, waiting=0)
[HikariPool-1 housekeeper] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.postgresql.jdbc.PgConnection@3fd29f40 on thread tornadofx-thread-2, stack trace follows
java.lang.Exception: Apparent connection leak detected
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100)
	at org.nield.rxkotlinjdbc.DatasourceKt$execute$1.invoke(datasource.kt:78)
	at org.nield.rxkotlinjdbc.DatasourceKt$execute$1.invoke(datasource.kt)
	at org.nield.rxkotlinjdbc.PreparedStatementBuilder.toPreparedStatement(PreparedStatementBuilder.kt:80)
	at org.nield.rxkotlinjdbc.UpdateOperation$toSingle$1.call(UpdateOperation.kt:38)
	at org.nield.rxkotlinjdbc.UpdateOperation$toSingle$1.call(UpdateOperation.kt:6)
	at io.reactivex.internal.operators.single.SingleDefer.subscribeActual(SingleDefer.java:36)
	at io.reactivex.Single.subscribe(Single.java:3394)
	at io.reactivex.Single.subscribe(Single.java:3380)
	at <<my tornadofx view class>>$deleteSelected$1.invoke(<<my tornadofx view class>>:157)
	at <<my tornadofx view class>>$deleteSelected$1.invoke(<<my tornadofx view class>>:17)
	at tornadofx.FXTask.call(Async.kt:459)
	at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
o

The specific function in violation:

fun deleteRegistrations(registrations: List<Registration>, idexam: Int) = db.execute(
    "DELETE FROM registered WHERE idexam = ? AND code in (${registrations.joinToString { "?" }});").parameter(
    idexam).parameters(*registrations.map {
    it.code
}.toTypedArray()).toSingle()

jdk: 1.8.0_144
kotlin version: 1.3.10
rxkotlinfx maven version: 2.2.2
hikaricp maven version: 3.2.0

Not sure this is a bug of Hikari or rxkotlinfx, but I found that swapping the execute to insert (and toSingle() to toSingle{true}) fixes the leak. Seems like the connection is not being closed and returned to the pool correctly?

doOnXXXCount() Operators

Not long ago I created some Operators analogous to doOnNext(), doOnError(), and doOnCompleted() except it would provide the emission counts and be able to use them for side effects.

This could be helpful for providing UI status updates through a side effect showing the number of items processed.

val mySource = Observable.just("Alpha","Beta","Gamma")
mySource.doOnNextCount { println("$it items processed")}.subscribe()

OUTPUT

1 items processed
2 items processed
3 items processed

If I go forward with this, then the next question is should there be FX and non-FX version... I'm going to guess yes. I'll think about it over the next few days.

Not able to get a full RxKotlinFX RxKotlin tornadofx stack working

I am not able to get a stack of RxKotinFX RxKotlin and TornadoFX working due to dependecy issues, please see the following SO post

It would be nice to have some comments in the docs or an example project showing how to get this working since it is such a common stack. In the example right now it just says x.y.z for versions witch is not helpful.

BindingList

It would be nice to have something similar to RxJava's internal API rx.internal.SubscriptionList.

class BindingList {
    fun add(binding: binding) { ... }
    fun dispose() {
        ...
        bindings.each { it.dispose() }
        ...
    }

    fun invalidate() {
        ...
        bindings.each { it.invalidate() }
        ...
    }

    fun isValid() { ... }
}
val bindings = BindingList()
bindings.add(...)
bindings.add(...)
bindings.add(...)

// later on
bindings.dispose()

Update doc: Dialog.toMaybe()

The README shows the extension method "Dialog.toObservable()" which does not exist in 2.2.2 (at least I couldn't find it).
But I could find toMaybe().

Maybe update the doc?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.