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.

A simple example

<?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!

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

Leave a Reply