ZK8 Series: Interact with Client Side Libaries using ZK8's New Client Side Binding"

From Documentation
m (correct highlight (via JWB))
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
#REDIRECT [[Small_Talks/2015/April/ZK8_Series:_Interact_with_Client_Side_Libaries_using_ZK's_New_Client_Side_Binding]]
 +
 
{{Template:Smalltalk_Author|
 
{{Template:Smalltalk_Author|
 
|author=Han Hsu, Potix Corporation
 
|author=Han Hsu, Potix Corporation
|date=April 1, 2015
+
|date=April 2, 2015
 
|version='''ZK''' 8.0.0.FL.20150331
 
|version='''ZK''' 8.0.0.FL.20150331
 
}}
 
}}
{{Template:UnderConstruction}}
+
 
 
= Introduction =
 
= Introduction =
 
In a [http://blog.zkoss.org/index.php/2015/03/11/zk8-work-with-native-web-components-using-the-new-zk-client-side-data-binding-api/ recent blog], we have introduced ZK 8's new client side binding and showed how we can use it to make a Polymer component work with ZK. In this smalltalk, we will have a more complete discussion of what will be included in the new client side binding up to date and how we can use these new methods to interact with a more complex client side library. In the second part of this post, we will present a simple demo along with its implementation to show you how to actually work with the new client side binding in real projects.
 
In a [http://blog.zkoss.org/index.php/2015/03/11/zk8-work-with-native-web-components-using-the-new-zk-client-side-data-binding-api/ recent blog], we have introduced ZK 8's new client side binding and showed how we can use it to make a Polymer component work with ZK. In this smalltalk, we will have a more complete discussion of what will be included in the new client side binding up to date and how we can use these new methods to interact with a more complex client side library. In the second part of this post, we will present a simple demo along with its implementation to show you how to actually work with the new client side binding in real projects.
Line 37: Line 39:
  
 
= Demo =
 
= Demo =
In this demo, we will use full calendar, which is a javascript calendar library under MIT license. We are going to wire up the full calendar with some ZK components back to the server with a view model and a data model. The goal here is to bind the events coming out from full calendar to the commands we have in our view model so that we can sync whatever changes in the calendar directly to our data model.
+
In this demo, we will use full calendar, which is a JavaScript calendar library under MIT license. We are going to wire up the full calendar with some ZK components back to the server with a View Model and a Data Model. The goal here is to bind the events coming out from full calendar to the commands we have in our View Model so that we can sync whatever changes in the calendar directly to our data model.
 +
 
 
<gflash width="900" height="730">Fullcalendar_demo.swf</gflash>
 
<gflash width="900" height="730">Fullcalendar_demo.swf</gflash>
  
Line 44: Line 47:
 
* zul page, which is the view of our demo
 
* zul page, which is the view of our demo
 
* js file, which will contain our event handlers for full calendar and our client side bindings.  
 
* js file, which will contain our event handlers for full calendar and our client side bindings.  
* view model, where we handle all the commands that come from client.
+
* view model, where we handle all the commands that come from the client.
 
* data model, which act as a data accessing object to our data source.
 
* data model, which act as a data accessing object to our data source.
  
Line 51: Line 54:
 
The container will be just a native div element with an ID:
 
The container will be just a native div element with an ID:
 
<source lang="xml"><n:div id="cal" /></source>
 
<source lang="xml"><n:div id="cal" /></source>
 +
 
The popups will be ZK popups and they will be used to create and modify events. The modify event popup looks like:
 
The popups will be ZK popups and they will be used to create and modify events. The modify event popup looks like:
 +
 
<source lang="xml">
 
<source lang="xml">
 
<popup id="modifyEventPop">
 
<popup id="modifyEventPop">
Line 81: Line 86:
 
</popup>
 
</popup>
 
</source>
 
</source>
The popup shows when a ''eventClicked'' event is triggered. The data of the event will be pre-loaded before the popup shows. When the modify button gets clicked, a ''modEvent'' command will be fired back to our view model, and we will update the modified event to our data model from there.
+
 
 +
The popup shows when an ''eventClicked'' event is triggered. The data of the event will be pre-loaded before the popup shows. When the modify button gets clicked, a ''modEvent'' command will be fired back to our View Model, and we will update the modified event to our data model from there.
 
The popup for creating event is very similar with the one above, we will omit its implementation here just to keep things simple.
 
The popup for creating event is very similar with the one above, we will omit its implementation here just to keep things simple.
  
 
=== js file ===
 
=== js file ===
 
Our script is where the new client side binding begins to play a role at. The complete js file looks like:
 
Our script is where the new client side binding begins to play a role at. The complete js file looks like:
<source lang="js" high="3,10,11,37,40">
+
<source lang="js" highlight="3,10,11,37,40">
 
zk.afterMount(function() {
 
zk.afterMount(function() {
 
 
Line 134: Line 140:
 
});
 
});
 
</source>
 
</source>
The first thing we do here at line 3 is to get our client binder. Then in our day clicked event handler, we use ''binder.command'' to trigger the ''doDayClicked'' command and pass the clicked date back to our view model at line 10. At line 11, we use ''binder.after'' to open our popup. Notice that when cascading ''binder.command'' and ''binder.after'', the first argument in ''binder.after'' can be omitted. The ''eventClick'', ''eventDrop'', and ''eventResize'' handler follow the similar concept as well. Line 37 is where we initialize our full calendar and finally, begins at line 40 is where our ''doEventsChange'' callback. We use this callback to ensure that every time when events change at view model, they will be updated in our view.
+
The first thing we do here at line 3 is to get our client binder. Then in our day clicked event handler, we use ''binder.command'' to trigger the ''doDayClicked'' command and pass the clicked date back to our view model at line 10. At line 11, we use ''binder.after'' to open our popup. Notice that when cascading ''binder.command'' and ''binder.after'', the first argument in ''binder.after'' can be omitted. ''eventClick'', ''eventDrop'', and ''eventResize'' handlers follow the similar concept as well. Line 37 is where we initialize our full calendar and finally, begins at line 40 is where our ''doEventsChange'' callback. We use this callback to ensure that every time when events change at the View Model, they will be updated in our view.
  
=== view model ===
+
=== View Model ===
 
Our view model is where we put all of our commands at. The structure of our view model looks like:
 
Our view model is where we put all of our commands at. The structure of our view model looks like:
  
<source lang="java" high="1,2">
+
<source lang="java" highlight="1,2">
 
@NotifyCommand(value="doEventsChange", onChange="_vm_.events")
 
@NotifyCommand(value="doEventsChange", onChange="_vm_.events")
 
@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})
 
@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})
Line 184: Line 190:
 
@NotifyCommand(value="doEventsChange", onChange="_vm_.events")
 
@NotifyCommand(value="doEventsChange", onChange="_vm_.events")
 
</source>
 
</source>
We specify that our view model will trigger the ''doEventsChange'' command whenever events is changed. The ''_vm_'' here stands for the current view model.
+
We specify that our view model will trigger the ''doEventsChange'' command whenever events are changed. The ''_vm_'' here stands for the current view model.
  
 
At line 2,
 
At line 2,
Line 190: Line 196:
 
@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})
 
@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})
 
</source>
 
</source>
We specify that everytime these commands execute, ZK will notify our client, and if there is a ''binder.after'' callback at client, it will be invoked. Notice that we do not have a ''doEventsChange'' command in our view model, we put ''@NotifyCommand'' here just because we want to trigger the callback function at the client.
+
We specify that every time these commands execute, ZK will notify our client, and if there is a ''binder.after'' callback at client, it will be invoked. Notice that we do not have a ''doEventsChange'' command in our view model, we put ''@NotifyCommand'' here just because we want to trigger the callback function at the client.
  
 
=== data model ===
 
=== data model ===
Data model is used to assess our data source. Here the class diagram of our data model:
+
Data model is used to assess our data source. Here's the class diagram of our data model:
  
  
 
[[File: Fullcalendar_datamodel.png]]
 
[[File: Fullcalendar_datamodel.png]]
  
Since we can have different data sources, our data model will have different implementations depending on the data source. In the demo showed above, we use a mock data source, which is just a map object in memory. If you go to the [https://github.com/jumperchen/zk8-fullcalendar-demo source code] of this demo, we also have a implementation with Google Calendar's API.
+
Since we can have different data sources, our data model will have different implementations depending on the data source. As shown in the demo above, we use a mock data source, which is just a map object in memory. If you check out the [https://github.com/jumperchen/zk8-fullcalendar-demo source code] of this demo, we also have a implementation with Google Calendar's API.
  
 
== Download ==
 
== Download ==

Latest revision as of 04:21, 20 January 2022

DocumentationSmall Talks2015AprilZK8 Series: Interact with Client Side Libaries using ZK8's New Client Side Binding
ZK8 Series: Interact with Client Side Libaries using ZK8's New Client Side Binding

Author
Han Hsu, Potix Corporation
Date
April 2, 2015
Version
ZK 8.0.0.FL.20150331

Introduction

In a recent blog, we have introduced ZK 8's new client side binding and showed how we can use it to make a Polymer component work with ZK. In this smalltalk, we will have a more complete discussion of what will be included in the new client side binding up to date and how we can use these new methods to interact with a more complex client side library. In the second part of this post, we will present a simple demo along with its implementation to show you how to actually work with the new client side binding in real projects.

ZK 8's client side binding

The new client side binding provides 4 methods - 2 at the client side, and 2 at the server side. Their relationships can be illustrated by the following diagram:


ZK8 Client Binding Chart01.jpg

At client

We have to get the client binder first in order to use the client side methods. To get the binder, simply use

var binder = zkbind.$('$id');

in the scripts. After we have our client binder, we could use the command and after method to interact with the view model back to our server.

binder.command(commandName, data)

This method is used to trigger a command we have at server by giving the command name as the first parameter. The second parameter is a JavaScript object, which is used to pass any information you want with the command.

Note: You could also pass ZK widgets in the data object and use @BindingParam to get the corresponding ZK component at the server.

binder.after(commandName, callback)

This method is used to place a callback at the client after a command gets executed at the server.

At server

Here, we are going to introduce two new annotations at the server side for the new client side binding. They should be placed at the beginning of the class declaration of our View Model.

@NotifyCommand(value="commandName", onChange="_vm_.expression")

The notify command annotation allows us to trigger a command whenever the given expression changes at the server. Notice the command which gets triggered is a command in our view model. The _vm_ here means the current view model.

@ClientCommand(commandNames)

The client command annotation allows us to put which commands we want to notify the client after execution has been done. Notice only the commands we put inside this annotation will trigger the callback we put in binder.after at client.

Demo

In this demo, we will use full calendar, which is a JavaScript calendar library under MIT license. We are going to wire up the full calendar with some ZK components back to the server with a View Model and a Data Model. The goal here is to bind the events coming out from full calendar to the commands we have in our View Model so that we can sync whatever changes in the calendar directly to our data model.

Implementation

We will have four main parts in this demo.

  • zul page, which is the view of our demo
  • js file, which will contain our event handlers for full calendar and our client side bindings.
  • view model, where we handle all the commands that come from the client.
  • data model, which act as a data accessing object to our data source.

zul page

Our zul page is relatively simple. It has three parts, a container for full calendar and two popups. The container will be just a native div element with an ID:

<n:div id="cal" />

The popups will be ZK popups and they will be used to create and modify events. The modify event popup looks like:

<popup id="modifyEventPop">
	<window title="Modify Event: " width="500px">
		<grid form="@id('fx') @load(vm.tempEvent) @save(vm.tempEvent, before='modEvent')">
			<rows>
				<row>
					<label value="Event ID: " />
					<label id="modId" value="@load(fx.id)" />
				</row>
				<row>
					<label value="Title:" />
					<textbox id="modTitle" value="@bind(fx.title)" />
				</row>
				<row>
					<label value="Start Date:" />
					<datebox id="modStart" value="@bind(fx.start)" format="long+medium" />
				</row>
				<row>
					<label value="End Date:" />
					<datebox id="modEnd" value="@bind(fx.end)" format="long+medium" />
				</row>
			</rows>
		</grid>
		<button id="modify" label="Modify" 
			onClick="@command('modEvent', pop=modifyEventPop)" />
		<button label="Cancel" onClick="modifyEventPop.close()" />
	</window>
</popup>

The popup shows when an eventClicked event is triggered. The data of the event will be pre-loaded before the popup shows. When the modify button gets clicked, a modEvent command will be fired back to our View Model, and we will update the modified event to our data model from there. The popup for creating event is very similar with the one above, we will omit its implementation here just to keep things simple.

js file

Our script is where the new client side binding begins to play a role at. The complete js file looks like:

zk.afterMount(function() {
	
	var binder = zkbind.$('$cal'), 
		calConfig = {};
	
	// day click handler
	calConfig.dayClick = function(data, jsEvent, view) {
		var popOffset = [jsEvent.clientX, jsEvent.clientY];
		
		binder.command('doDayClicked', {dateClicked: data.toDate().getTime()})
			.after(function() {
				var newPop = zk.$('$newEventPop');
				newPop.open(newPop, popOffset);
		});
	};
	
	// event click handler
	calConfig.eventClick = function(event, jsEvent, view) {
		var popOffset = [jsEvent.clientX, jsEvent.clientY];
		binder.command('doEventClicked', {evtId: event.id})
			.after(function() {
				var modPop = zk.$('$modifyEventPop');
				modPop.open(modPop, popOffset);
		});
	}
	
	// event drop handler and event resize handler
	calConfig.eventResize = calConfig.eventDrop = 
		function(event, delta, revertFunc, jsEvent, ui, view) {
		var startTime = event.start ? 
				event.start.toDate().getTime() : 0,
			endTime = event.end ? event.end.toDate().getTime() : 0;
		
		binder.command('doEventChanged', {evtId: event.id, startTime: startTime, endTime: endTime});
	}
	
	$('#cal').fullCalendar(calConfig);
	
	// the event handler of after 'doCommandChange' from server
	binder.after('doEventsChange', function(events) {
		$('#cal').fullCalendar('removeEvents');
		$('#cal').fullCalendar('addEventSource', events);
		$('#cal').fullCalendar('rerenderEvents');
	});

});

The first thing we do here at line 3 is to get our client binder. Then in our day clicked event handler, we use binder.command to trigger the doDayClicked command and pass the clicked date back to our view model at line 10. At line 11, we use binder.after to open our popup. Notice that when cascading binder.command and binder.after, the first argument in binder.after can be omitted. eventClick, eventDrop, and eventResize handlers follow the similar concept as well. Line 37 is where we initialize our full calendar and finally, begins at line 40 is where our doEventsChange callback. We use this callback to ensure that every time when events change at the View Model, they will be updated in our view.

View Model

Our view model is where we put all of our commands at. The structure of our view model looks like:

@NotifyCommand(value="doEventsChange", onChange="_vm_.events")
@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})
public class DemoViewModel {
	
	private EventsDataModel dataModel;
	private Collection<EventObject> events;
	private EventObject tempEvent;
	
	
	@Init
	public void init() throws GeneralSecurityException, IOException {
		// init event data model
		dataModel = new DemoDataModel();
		events = dataModel.getEvents();
	}
	
	@Command
	@NotifyChange("tempEvent")
	public void doDayClicked(@BindingParam("dateClicked") long dateClicked);
	
	@Command
	@NotifyChange("tempEvent")
	public void doEventClicked(@BindingParam("evtId") String evtId);
	
	@Command
	@NotifyChange("events")
	public void doEventChanged(@BindingParam("evtId") String evtId, 
			@BindingParam("startTime") long startTime, @BindingParam("endTime") long endTime);
		
	@Command
	@NotifyChange("events")
	public void modEvent(@BindingParam("pop") Popup pop);
	
	@Command
	@NotifyChange("events")
	public void createEvent(@BindingParam("pop") Popup pop);
}

Notice we omit command implementations here just to focus on the new client side binding. First we put our @NotifyCommand and @ClientCommand on top of our class declaration.

At line 1,

@NotifyCommand(value="doEventsChange", onChange="_vm_.events")

We specify that our view model will trigger the doEventsChange command whenever events are changed. The _vm_ here stands for the current view model.

At line 2,

@ClientCommand({"doEventClicked", "doDayClicked", "doEventsChange"})

We specify that every time these commands execute, ZK will notify our client, and if there is a binder.after callback at client, it will be invoked. Notice that we do not have a doEventsChange command in our view model, we put @NotifyCommand here just because we want to trigger the callback function at the client.

data model

Data model is used to assess our data source. Here's the class diagram of our data model:


Fullcalendar datamodel.png

Since we can have different data sources, our data model will have different implementations depending on the data source. As shown in the demo above, we use a mock data source, which is just a map object in memory. If you check out the source code of this demo, we also have a implementation with Google Calendar's API.

Download

You can download the war file and all of the source code for this demo in Github


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.