Introduction
This is the 4th article of ZK CDT (ZK Component Development Tutorial) walkthrough, this article describe how to render dom elements at client side with server side data.
Again attributes and extends.
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 4th part: Render client side element with server side data
Result
View demo online:
http://screencast.com/t/JrwPHqsrA
As you can see you can update note blocks by server now.
Note: the 'index to update' is the index of data object in model to update, only used to update data when you click 'update' button.
Pre-request
ZK CDT: Handle Client Side Event to Create Simple Text Note
http://ben-bai.blogspot.tw/2013/06/zk-cdt-handling-client-side-event-to.html
ZK Basic MVVM Pattern
http://ben-bai.blogspot.tw/2012/12/zk-basic-mvvm-pattern.html
Program
renderabletextnote.zul
Contains a renderabletextnote component, a control block that used to add/update/clear note blocks, a slider/colorbox that control the opacity/background-color of the renderabletextnote.
<zk>
<div apply="org.zkoss.bind.BindComposer"
viewModel="@id('vm') @init('custom.zk.samples.quicknote.RenderableTextNoteVM')">
<hlayout>
<!-- The renderabletextnote component that will cover
its children by a mask
you can click on the mask to add a textarea
and type text in it
-->
<renderabletextnote width="700px" id="stn"
opacity="20" maskColor="#00FF00"
model="@load(vm.model)">
<button label="ZK Website" />
<iframe width="100%"
height="1000px"
src="http://www.zkoss.org/"></iframe>
</renderabletextnote>
<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: <intbox value="@save(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 renderabletextnote -->
<slider curpos="20" maxpos="100">
<attribute name="onScroll"><![CDATA[
stn.setOpacity(((ScrollEvent)event).getPos());
]]></attribute>
<attribute name="onScrolling"><![CDATA[
stn.setOpacity(((ScrollEvent)event).getPos());
]]></attribute>
</slider>
<!-- colorbox used to control mask color of renderabletextnote -->
<colorbox color="#00FF00">
<attribute name="onChange"><![CDATA[
stn.setMaskColor(self.getValue());
]]></attribute>
</colorbox>
</hlayout>
</vlayout>
</hlayout>
</div>
</zk>
RenderableTextNoteVM.java
VM used in renderabletextnote.zul, provide data, do command and update data.
package custom.zk.samples.quicknote;
import java.util.ArrayList;
import java.util.List;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import custom.zk.components.quicknote.Data.TextNoteData;
import custom.zk.components.quicknote.model.TextNoteModel;
/** VM used in renderabletextnote.zul
*
* @author benbai123
*
*/
public class RenderableTextNoteVM {
private TextNoteModel _model;
private TextNoteData _textNoteDataToUpdate;
private int _indexToUpdate = -1;
// getters, setters
@SuppressWarnings({ "unchecked", "rawtypes" })
public TextNoteModel getModel () {
if (_model == null) {
List l = new ArrayList();
l.add(new TextNoteData(5, 5, 100, 35, "test"));
l.add(new TextNoteData(55, 55, 150, 50, "test 2"));
l.add(new TextNoteData(105, 105, 75, 25, "test 3"));
_model = new TextNoteModel(l);
}
return _model;
}
public TextNoteData getTextNoteData () {
if (_textNoteDataToUpdate == null) {
_textNoteDataToUpdate = new TextNoteData(0, 0, 0, 0, "");
}
return _textNoteDataToUpdate;
}
public int getIndexToUpdate () {
return _indexToUpdate;
}
public void setIndexToUpdate (int indexToUpdate) {
_indexToUpdate = indexToUpdate;
}
// add note block then update model to client
@Command
@NotifyChange ("model")
public void addNoteBlock () {
_model.add(new TextNoteData(_textNoteDataToUpdate));
}
// update note block then update model to client
@Command
@NotifyChange ("model")
public void updateNoteBlock () {
_model.update(_indexToUpdate, new TextNoteData(_textNoteDataToUpdate));
}
// clear note blocks then update model to client
@Command
@NotifyChange ("model")
public void clearAllBlocks () {
_model.clear();
}
}
RenderableTextNote.java
Java class of RenderableTextNote component, extends enhancedmask and handle model rendering.
package custom.zk.components.quicknote;
import java.util.List;
import org.zkoss.json.JSONArray;
import custom.zk.components.quicknote.Data.TextNoteData;
import custom.zk.components.quicknote.model.TextNoteModel;
/** RenderableTextNote, can render text note block with server side data
*
* @author benbai123
*
*/
public class RenderableTextNote extends EnhancedMask {
private static final long serialVersionUID = -6824067586007799573L;
/** data model that contains information of text note blocks
*
*/
private TextNoteModel _model;
/**
* setter
* @param model
*/
public void setModel (TextNoteModel model) {
_model = model;
updateNoteBlocks();
}
/**
* getter
* @return
*/
public TextNoteModel getModel () {
return _model;
}
/**
* used to update text note blocks at client side
*/
public void updateNoteBlocks () {
String blockToRender = getRenderedTextNoteData();
if (blockToRender == null) {
blockToRender = "";
}
// update client note blocks while setter is called
smartUpdate("noteBlocks", blockToRender);
}
/**
* create a json string used to update text note blocks at client side
* @return
*/
@SuppressWarnings("rawtypes")
public String getRenderedTextNoteData () {
if (_model != null
&& _model.getTextNoteData() != null
&& _model.getTextNoteData().size() > 0) {
// json arrays of
// left, top, width, height and text of note blocks
JSONArray jsArr = new JSONArray();
JSONArray jsXposArr = new JSONArray(); // left
JSONArray jsYposArr = new JSONArray(); // top
JSONArray jsWidthArr = new JSONArray(); // width
JSONArray jsHeightArr = new JSONArray(); // height
JSONArray jsTextArr = new JSONArray(); // text
// get all data
List datas = _model.getTextNoteData();
// put each data into json array accordingly
for (int i = 0; i < datas.size(); i++) {
TextNoteData data = (TextNoteData)datas.get(i);
jsXposArr.add(data.getPosX());
jsYposArr.add(data.getPosY());
jsWidthArr.add(data.getWidth());
jsHeightArr.add(data.getHeight());
jsTextArr.add(data.getText());
}
// put each data array into another json array
// i.e., will be a 2-D array at client side
jsArr.add(jsXposArr);
jsArr.add(jsYposArr);
jsArr.add(jsWidthArr);
jsArr.add(jsHeightArr);
jsArr.add(jsTextArr);
return jsArr.toJSONString();
} else {
return null;
}
}
// render noteBlocks as needed
protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer)
throws java.io.IOException {
super.renderProperties(renderer);
String blockToRender = getRenderedTextNoteData();
if (blockToRender != null) {
render(renderer, "noteBlocks", blockToRender);
}
}
}
RenderableTextNote.js
Widget class of RenderableTextNote component, extends SimpleTextNote and handle model rendering.
/**
* Widget class of RenderableTextNote component,
* extends custom.zk.components.quicknote.SimpleTextNote,
* handle update command (setNoteBlocks) to render note blocks
* with the data updated from server
*
* one new thing:
* bind_: Callback when this widget is bound (aka., attached) to the DOM tree.
*
*
*/
custom.zk.components.quicknote.RenderableTextNote = zk.$extends(custom.zk.components.quicknote.SimpleTextNote, {
_noteBlocks: null,
/** setter for noteBLocks,
* will render note blocks if dom element exists
*
* @param noteBlocks information of note blocks from server
*/
setNoteBlocks: function (noteBlocks) {
this._noteBlocks = noteBlocks;
if (this.$n()) {
// dom exists, render note blocks
this._renderNoteBlocks();
}
},
// doms are ready
bind_: function (desktop, skipper, after) {
// call super
this.$supers(custom.zk.components.quicknote.RenderableTextNote, 'bind_', arguments);
// render note blocks if any
this._renderNoteBlocks();
},
// render note blocks
_renderNoteBlocks: function () {
var noteBlocks = this._noteBlocks;
// clear all old note blocks
// so we do not need to care about the mapping (keep, rerender, add, etc)
jq(this.$n()).find('.' + this.getZclass() + '-noteblock')
.each(function () {
// 'this' is each block here
this.parentNode.removeChild(this);
});
// has note blocks
if (noteBlocks) {
var datas = jq.evalJSON(noteBlocks), // eval to get a 2-D array
x = datas[0], // left array
y = datas[1], // top array
w = datas[2], // width array
h = datas[3], // height array
text = datas[4], // text array
len = x.length, // amount of note blocks
idx = 0; // index
// render each note block
for ( ; idx < len; idx++) {
this._renderNoteBlock(x[idx], y[idx], w[idx], h[idx], text[idx]);
}
}
},
// create dom element of note block
_renderNoteBlock: function (x, y, w, h, txt) {
// note block created by _createNoteBlock defined in SimpleTextNote
var noteBlock = this._createNoteBlock(x, y),
textArea = noteBlock.firstChild;
// add width and height, insert text
jq(textArea).css({'width': w+'px',
'height': h+'px'});
textArea.innerHTML = txt;
// add note block under root element of widget
this.$n().appendChild(noteBlock);
}
});
TextNoteData.java
Java class that represent the attributes of textarea in a note block.
package custom.zk.components.quicknote.Data;
/** Java bean that represent a text note block
*
* @author benbai123
*
*/
public class TextNoteData {
private int _posX; // left
private int _posY; // top
private int _width; // width
private int _height; // height
private String _text; // text
// constructor that construct with each attributes
public TextNoteData (int posX, int posY, int width, int height, String text) {
_posX = posX;
_posY = posY;
_width = width;
_height = height;
_text = text;
}
// constructor that construct with another data bean
public TextNoteData (TextNoteData dataToCopy) {
_posX = dataToCopy.getPosX();
_posY = dataToCopy.getPosY();
_width = dataToCopy.getWidth();
_height = dataToCopy.getHeight();
_text = dataToCopy.getText();
}
// setters, getters
public void setPosX (int posX) {
_posX = posX;
}
public int getPosX () {
return _posX;
}
public void setPosY (int posY) {
_posY = posY;
}
public int getPosY () {
return _posY;
}
public void setWidth (int width) {
_width = width;
}
public int getWidth () {
return _width;
}
public void setHeight (int height) {
_height = height;
}
public int getHeight () {
return _height;
}
public void setText (String text) {
_text = text;
}
public String getText () {
return _text;
}
}
TextNoteModel.java
Java class that contains a list of TextNoteData, provide add/insert/remove/update APIs.
package custom.zk.components.quicknote.model;
import java.util.ArrayList;
import java.util.List;
import custom.zk.components.quicknote.Data.TextNoteData;
/** basic model to hold text note block datas
*
* @author benbai123
*
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class TextNoteModel {
// text note block data list
private List _textNodeDatas;
// constructor
public TextNoteModel (List textNodeDatas) {
if (textNodeDatas == null) {
textNodeDatas = new ArrayList();
}
_textNodeDatas = textNodeDatas;
}
// add
public void add (TextNoteData data) {
_textNodeDatas.add(data);
}
// add at specific position
public void add (int index, TextNoteData data) {
_textNodeDatas.add(index, data);
}
// remove
public void remove (TextNoteData data) {
_textNodeDatas.remove(data);
}
// update specific data (by position)
public void update (int index, TextNoteData data) {
_textNodeDatas.remove(index);
add(index, data);
}
// return data list
public List getTextNoteData () {
return _textNodeDatas;
}
// clear all data
public void clear () {
_textNodeDatas.clear();
}
}
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="RenderableTextNote" />
...
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
...
<!-- 4th, renderabletextnote component
extends simpletextnote and handle data rendering with model
reuse css style,
extends java and widget classes,
add functions to handle model rendering
-->
<component>
<component-name>renderabletextnote</component-name>
<extends>simpletextnote</extends>
<component-class>custom.zk.components.quicknote.RenderableTextNote</component-class>
<widget-class>custom.zk.components.quicknote.RenderableTextNote</widget-class>
</component>
...
References
eval json
http://www.zkoss.org/javadoc/latest/jsdoc/_global_/jq.html#evalJSON(_global_.String)
bind_
http://www.zkoss.org/javadoc/latest/jsdoc/zk/Widget.html#bind_(zk.Desktop, zk.Skipper, _global_.Array)
Download
Full project at github
https://github.com/benbai123/ZK_Practice/tree/master/Components/projects/Components_Development__Series/001_walkthrough/ZKQuickNote
renderabletextnote_component.swf
No comments:
Post a Comment