Introduction
This is the 5th article of ZK CDT (ZK Component Development Tutorial) walkthrough, this article describe how to fire event to bring client data to server side.
Fire event to server.
The goal of this walkthrough is to create a quicknote component that help you do some quick note on web page (similar to you crop some screenshot, highlight something and add some text in photo editor), and describe each part of a component through each article in this walkthrough.
This is the 5th part: Fire event to server at client side.
Result
View demo online:
http://screencast.com/t/cDqjn5k3hU
As you can see, now you can update the selected note block by clicking on it instead of type index in intbox.
Also the selected note block will become the top most one.
Pre-request
ZK CDT: RenderableTextNote: Render Note Blocks with Server Side Data
http://ben-bai.blogspot.tw/2013/06/zk-cdt-renderabletextnote-render-note.html
Program
selectabletextnote.zul
Contains a selectabletextnote component, a control block that used to add/update/clear note blocks, a slider/colorbox that control the opacity/background-color of the selectabletextnote.
<zk>
<!-- two new things
selectedTextNoteIndex="@save(vm.indexToUpdate)":
that will save selectedTextNoteIndex of selectabletextnote to vm
chek the component definition in lang-addon.xml to see
how it can work
control of opacity and maskColor are moved into VM
-->
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('custom.zk.samples.quicknote.SelectableTextNoteVM')">
<hlayout>
<!-- The selectabletextnote component that will cover
its children by a mask
you can click on the mask to add a textarea
and type text in it
-->
<selectabletextnote width="700px" id="stn"
opacity="@load(vm.opacity)" maskColor="@load(vm.maskColor)"
model="@load(vm.model)"
selectedTextNoteIndex="@save(vm.indexToUpdate)">
<button label="ZK Website" />
<iframe width="100%"
height="1000px"
src="http://www.zkoss.org/"></iframe>
</selectabletextnote>
<vlayout>
<!-- controll block for add/update/clear note blocks -->
<vlayout>
<hlayout>
x: <intbox value="@bind(vm.textNoteData.posX)" />
</hlayout>
<hlayout>
y: <intbox value="@bind(vm.textNoteData.posY)" />
</hlayout>
<hlayout>
width: <intbox value="@bind(vm.textNoteData.width)" />
</hlayout>
<hlayout>
height: <intbox value="@bind(vm.textNoteData.height)" />
</hlayout>
<hlayout>
text: <textbox value="@bind(vm.textNoteData.text)" />
</hlayout>
<hlayout>
<button label="add" onClick="@command('addNoteBlock')" />
</hlayout>
<hlayout>
index to update: <label value="@load(vm.indexToUpdate)" />
<button label="update" onClick="@command('updateNoteBlock')" />
</hlayout>
<hlayout>
<button label="clear" onClick="@command('clearAllBlocks')" />
</hlayout>
</vlayout>
<hlayout>
<!-- slider used to control opacity of selectabletextnote -->
<slider curpos="@bind(vm.opacity)" maxpos="100"
onScroll="@command('updateOpacity')" />
<!-- colorbox used to control mask color of selectabletextnote -->
<colorbox color="@bind(vm.maskColor)"
onChange="@command('updateMaskColor')" />
</hlayout>
</vlayout>
</hlayout>
</div>
</zk>
SelectableTextNoteVM.java
VM used in selectabletextnote.zul, provide data, do command and update data.
package custom.zk.samples.quicknote;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
/** VM used in selectabletextnote.zul,
* extends RenderableTextNoteVM then simply
* define a member field and its getter/setter
*
* @author benbai123
*
*/
public class SelectableTextNoteVM extends RenderableTextNoteVM {
private int _opacity = 20;
private String _maskColor = "#00FF00";
// getters/setters
public int getOpacity () {
return _opacity;
}
public void setOpacity (int opacity) {
_opacity = opacity;
}
public String getMaskColor () {
return _maskColor;
}
public void setMaskColor (String maskColor) {
_maskColor = maskColor;
}
// commands
@Command
@NotifyChange("opacity")
public void updateOpacity () {
// do nothing, just for trigger NotifyChange
}
@Command
@NotifyChange("maskCOlor")
public void updateMaskColor () {
// do nothing, just for trigger NotifyChange
}
}
SelectableTextNote.java
Java class of SelectableTextNote component, extends RenderableTextNote and handle note block selection.
package custom.zk.components.quicknote;
import java.util.Map;
import org.zkoss.zk.ui.event.Events;
/**
* SelectableTextNote, will receive and store which note block is selected from client side action
*
* Three new things:
*
* addClientEvent: add an event that the client might send to server with specific settings,
* refer to http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/AbstractComponent.html#addClientEvent(java.lang.Class, java.lang.String, int)
*
* service: Handles an AU request. It is invoked internally.
* refer to http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/AbstractComponent.html#service(org.zkoss.zk.au.AuRequest, boolean)
*
* postEvent: post an event to self instance so the composer can be notified
* this is also required for save data with MVVM
* refer to http://books.zkoss.org/wiki/ZK_Developer's_Reference/Event_Handling/Event_Firing
*
* @author benbai123
*
*/
public class SelectableTextNote extends RenderableTextNote {
private static final long serialVersionUID = -6589891861074953359L;
private int _selectedTextNoteIndex = -1;
static {
/* CE_IMPORTANT: always fire it to server,
* without this flag, the au engine will only fire event to server
* if and only if there is an EventListener listen to this event
*
* CE_DUPLICATE_IGNORE: ignore multiple event in an au request
*
* CE_NON_DEFERRABLE: always fire it immediately,
* without this flag, au engine will queue this event at client side
* and fire it to server with other non-defferrable event
*/
addClientEvent(SelectableTextNote.class, "onTextNoteBlockSelect", CE_IMPORTANT | CE_DUPLICATE_IGNORE | CE_NON_DEFERRABLE);
}
// setter/getter
public void setSelectedTextNoteIndex (int selectedTextNoteIndex) {
_selectedTextNoteIndex = selectedTextNoteIndex;
}
public int getSelectedTextNoteIndex () {
return _selectedTextNoteIndex;
}
// process client event
public void service(org.zkoss.zk.au.AuRequest request, boolean everError) {
final String cmd = request.getCommand();
if (cmd.equals("onTextNoteBlockSelect")) {
Map data = request.getData(); // get data map
// get index by the key "index" since
// we define the data as {index: idx}
// while firing onTextNoteBlockSelect event
// in SelectableTextNote.js
Integer index = (Integer)data.get("index");
// store value
_selectedTextNoteIndex = index;
// post event to trigger listeners if any
Events.postEvent("onTextNoteBlockSelect", this, data);
} else
super.service(request, everError);
}
}
SelectableTextNote.js
Widget class of SelectableTextNote component, extends RenderableTextNote and handle click event of textarea in text note block.
/**
* Widget class of SelectableTextNote component,
* extends custom.zk.components.quicknote.RenderableTextNote
*
* One new thing:
* fire: fire event to server,
* widget.fire('EVENT_NAME', {DATA}, {OPTIONS}, TIMEOUT);
* refer to http://www.zkoss.org/javadoc/latest/jsdoc/zk/Widget.html#fire(_global_.String, zk.Object, _global_.Map, int)
*
*/
custom.zk.components.quicknote.SelectableTextNote = zk.$extends(custom.zk.components.quicknote.RenderableTextNote, {
_selectedTextNoteIndex: -1,
// called while onclick of any dom elements
// under root element is triggered
doClick_: function (evt) {
// call super at first
this.$supers('doClick_', arguments);
var target = evt.domTarget;
// clicked in textarea in text note block
if (jq(target).hasClass(this.getZclass() + '-noteblock-textarea')) {
this._doTextNoteBlockClick(evt);
}
},
/** processing onclick of textarea in note block
* pass event into this function (instead of just pass target)
* since we probably need some information (e.g., pageX/Y, etc) in
* the future
*
* @param evt
*/
_doTextNoteBlockClick: function (evt) {
// cls: css class of textarea within text note block
// target: the clicked dom element
var scls = this.getZclass() + '-noteblock-selected',
target = evt.domTarget,
idx;
// clear selected class of old selected block
jq('.' + scls).each(function () {
jq(this).removeClass(scls);
});
// add class to make it become top most note block
jq(target.parentNode).addClass(scls);
// fire event to update index to server side
if ((idx = this.getTextBlockIndex(target)) >= 0) {
this._selectedTextNoteIndex = idx;
this.fire('onTextNoteBlockSelect', {index: idx});
}
},
getTextBlockIndex: function (textarea) {
var cls = this.getZclass() + '-noteblock-textarea';
// current: a copy of text note block for while loop
// idx: index of current text note block
var current = this.$n('mask').nextSibling,
idx = 0;
// for each text note block
while (current) {
// found clicked block
if (jq(current).find('.'+cls)[0] == textarea) {
// return index
return idx;
}
current = current.nextSibling;
idx++;
}
return -1;
},
// override with new css class name
getZclass: function () {
var zcls = this._zclass;
return zcls? zcls : 'z-selectabletextnote';
}
});
selectableTextNote.css.dsp
CSS classes for SelectableTextNote component
<%--// ------------------------------------------- --%>
<%--// --%>
<%--// SelectableTextNote component --%>
<%--// --%>
<%--// ------------------------------------------- --%>
<%--// root element --%>
.z-simpletextnote {
<%--// be the anchor of absolute positioned children --%>
position: relative;
overflow: hidden;
}
<%--// the mask that cover whole element --%>
<%--// no background-color and opacity specified --%>
<%--// since we specified them in widget class --%>
.z-selectabletextnote-cover {
<%--// absoluted positioned --%>
position: absolute;
<%--// align the left-top corner of parent (root) element --%>
left: 0px;
top: 0px;
<%--// cover whole root element --%>
height: 100%;
width: 100%;
<%--// make it the top most element under root element --%>
z-index: 99999;
}
.z-selectabletextnote .z-selectabletextnote-noteblock {
<%--// absoluted positioned --%>
position: absolute;
<%--// in front of mask --%>
z-index: 999999;
}
.z-selectabletextnote .z-selectabletextnote-noteblock-textarea {
<%--// h/v resizable --%>
resize: both;
<%--// default width and height --%>
<%--// NOTE: the specified value of width/height will --%>
<%--// be the minimum value, you cannot shrink textarea --%>
<%--// smaller than these values (at least on chrome) --%>
width: 50px;
height: 30px;
}
.z-selectabletextnote .z-selectabletextnote-noteblock-selected {
<%--// in front other text note blocks --%>
z-index: 1000000;
}
zk.wpd
Define components under "custom.zk.components.quicknote"
* only the added part, not the full code
NOTE: more components will be added with other articles later
...
<widget name="SelectableTextNote" />
...
lang-addon.xml
Define all components in the project
* only the added part, not the full code
NOTE: more components will be added with other articles later
...
<!-- 5th, selectabletextnote component
extends renderabletextnote,
update zclass, handle click event of
textareas and fire event to server
to update the selected note block index
one new thing, the 'annotation' block that define
how an attribute (selectedTextNoteIndex here) works with ZKBIND
as you can see opacity and maskColor are not defined here
since they only require the 'load' direction
the 'load' direction is supported by default so
you just need to provide setter (in component) and getter (in VM) properly
refer to:
Document: http://books.zkoss.org/wiki/ZK_Developer's_Reference/MVVM/Advanced/Binding_Annotation_for_a_Custom_Component
Code: https://github.com/zkoss/zk/blob/master/zkbind/src/archive/metainfo/zk/lang-addon.xml
-->
<component>
<component-name>selectabletextnote</component-name>
<extends>renderabletextnote</extends>
<component-class>custom.zk.components.quicknote.SelectableTextNote</component-class>
<widget-class>custom.zk.components.quicknote.SelectableTextNote</widget-class>
<mold>
<mold-name>default</mold-name>
<css-uri>css/selectableTextNote.css.dsp</css-uri>
</mold>
<annotation>
<!-- ZKBIND is the zkbind system annotation -->
<annotation-name>ZKBIND</annotation-name>
<!-- property name -->
<property-name>selectedTextNoteIndex</property-name>
<attribute>
<!-- ACCESS is the access direction:
can be "both", "save", "load";
default to "load" if not found -->
<attribute-name>ACCESS</attribute-name>
<attribute-value>both</attribute-value>
</attribute>
<attribute>
<!-- SAVE_EVENT is the save trigger event;
meaningful only when ACCESS is "both" or "save" -->
<attribute-name>SAVE_EVENT</attribute-name>
<attribute-value>onTextNoteBlockSelect</attribute-value>
</attribute>
<attribute>
<!-- LOAD_TYPE is the type of attribute for loading -->
<attribute-name>LOAD_TYPE</attribute-name>
<attribute-value>java.lang.Integer</attribute-value>
</attribute>
</annotation>
</component>
...
References
addClientEvent java method
http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/AbstractComponent.html#addClientEvent(java.lang.Class, java.lang.String, int)
service java method
http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/AbstractComponent.html#service(org.zkoss.zk.au.AuRequest, boolean)
postEvent java method
http://books.zkoss.org/wiki/ZK_Developer's_Reference/Event_Handling/Event_Firing
fire client widget method
http://www.zkoss.org/javadoc/latest/jsdoc/zk/Widget.html#fire(_global_.String, zk.Object, _global_.Map, int)
ZKBIND annotation
Document:
http://books.zkoss.org/wiki/ZK_Developer's_Reference/MVVM/Advanced/Binding_Annotation_for_a_Custom_Component
Code:
https://github.com/zkoss/zk/blob/master/zkbind/src/archive/metainfo/zk/lang-addon.xml
Download
Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Components_Development__Series/001_walkthrough/ZKQuickNote
selectabletextnote_component.swf
https://github.com/benbai123/ZK_Practice/blob/master/Components/demos/component_development_series/001_walkthrough/selectabletextnote_component.swf
No comments:
Post a Comment