Introduction
You might already hear of Reactive Programming – which is based on the Observer pattern. An Observable
object can have more than one Observer
. Once an observable object changes, all of its observers will be notified. A simple example is re-evaluating formulas (e.g. =B1+C1) and updating cells dynamically once the data was changed in a spreadsheet application. Reactive Programming generally raises the level of code abstraction and allows developers to focus more on the interdependence of events in their business logic.
RxJava is a Java VM implementation of ReactiveX, or in short RX. ReactiveX, according to its website, is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming.
In this blog post, we’ll show you a ZK addon “RxZK” that let you do things better in ZK with the help of RxJava.
Installation
Here we use Maven as an example.
1. Add zk repository in <repositories>
in your pom.xml if you haven’t done so.
<repositorties>
[...]
<repository>
<name>zk repository</name>
<url>http://mavensync.zkoss.org/maven2</url>
</repository>
[...]
<repositorties>
2. Add rxzk dependency. It will depend on rxjava and reactive-streams automatically.
<dependency>
<groupId>org.zkoss.addons</groupId>
<artifactId>rxzk</artifactId>
<version>0.8.0</version>
</dependency>
3. Profit!
Tutorial
Now you can start to use ZkObservable
to enjoy Reactive Programming in ZK!
ZkObservable
provides Observable
from events of component or event queue.
|
fromEvent |
fromEventQueue |
ZkObservable |
Create an Observable from events of a component |
Create an Observable from events of an event queue |
Let’s start with a simple example: assuming there are two buttons, add and subtract. The value increases by one while clicking the “+” button and decreases by one while clicking the “-” button. To do this we create two Observable
streams from onClick
events of the two buttons, and merge two streams into one stream.

<?xml version="1.0" encoding="UTF-8"?>
<zk>
<div apply="org.zkoss.addons.rxzk.AddSubtractComposer">
<label id="number" value="0" />
<button id="btnAdd" label="+"/>
<button id="btnSubtract" label="-"/>
</div>
</zk>
public class AddSubtractComposer extends SelectorComposer<Div> {
@Wire
private Label number;
@Wire
private Button btnAdd;
@Wire
private Button btnSubtract;
@Override
public void doAfterCompose(Div comp) throws Exception {
super.doAfterCompose(comp);
Observable.merge(
ZkObservable.fromEvent(btnAdd, Events.ON_CLICK)
.map(e -> 1),
ZkObservable.fromEvent(btnSubtract, Events.ON_CLICK)
.map(e -> -1)
)
.scan(0, (a, b) -> a + b)
.subscribe(v -> number.setValue(String.valueOf(v)));
}
}
- Line 16: Create an
Observable
stream from clicking add button.
- Line 17: Transform every add button
onclick
event into 1.
- Line 21: Use scan() method RxJava provided to accumulative every successive value.
- Line 22: Create a
Observer
that consumes the final value, and update to the component.
Asynchronous Updating The UI (Server Push)
RxJava might run tasks asynchronously in various threads using the different schedulers. Operators like debounce
, delay
or takeLast
will use computation or trampoline scheduler by default. If you want to update ZK UI after then, you need to enable the server-push feature.
The ZkServerPush
class implements some util methods that are useful. To enable server-push, call ZkServerPush.enable(Desktop)
. With the aid of ZkServerPush.updateUi
method, you can update the UI asynchronously in any other thread without activating and deactivating the Desktop
manually.
public class AsyncUpdateUiComposer extends SelectorComposer<Component> {
@WireVariable
private Desktop desktop;
@Wire
private Textbox term;
@Wire
private Label result;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
ZkServerPush.enable(desktop);
ZkObservable.fromEvent(term, Events.ON_CHANGING)
.debounce(300, TimeUnit.MILLISECONDS)
.map(e -> ((InputEvent) e).getValue())
.filter(s -> s.length() >= 3)
.distinctUntilChanged()
.observeOn(Schedulers.io())
// simulate heavy load
.map(e -> { Threads.sleep(2000); return e + "!"; })
.subscribe(ZkServerPush.updateUi(
desktop,
s -> result.setValue("You want to search: " + s)
));
}
}
- Line 3: Store
Desktop
for server-push.
- Line 15: Enable the server-push feature.
- Line 24: We update the UI in another thread using
ZkServerPush.updateUi
.
Source
I hope you find this little example interesting. The source of rxzk addon is hosted on GitHub @zkoss-demo/rxzk and is open sourced under LGPLv3. If you like it, don’t forget to Star it!