Zscript, java, EL

From Documentation
Revision as of 13:25, 19 January 2022 by Hawk (talk | contribs) (correct highlight (via JWB))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


Documentationscript, java, EL
script, java, EL


Stop.png This documentation is for an older version of ZK. For the latest one, please click here.


Overview

By default, code in zscript is Java language. Therefore, any code written in zscript can move to java file by slightly modification. In retrospect, any code written in java file can move to zscript by slightly modification. After all, they are all java.

It is convenient to use zscript in ZUML. The advantage is that no compilation is required and you can modify its content dynamically (without re-deploying the Web application). And the syntax is more intuitive, EL can access variables in zscript easily.

But it comes with a price: slower performance. The degradation varies from one application from another. For large website, it is suggested not to use zscript if possible. Also, you CAN NOT use Java debug tool to debug zscript, you can't set breakpoints in zscript. Not like Java, sometimes obvious bug like typo can't be easily found in zscript. ZK has implemented GenericAutowireComposer, it eases the burden of moving code from zscript to java a lot. Please refer to the smalltalk [1] for detail.

zscript is useful for prototyping, but it's more hard to debug and maintain. In the following paragraph, we'll show examples of equivalent zscript and java code.

Use zscript to initialize

In Page Initial Phase, ZK processes the processing instructions, called init.

Use zscript to init the page,simply specify a file containing the scripting codes with the zscript attribute, as follows. Then, the file will be interpreted at the Page Initial phase.

For example:

<?init zscript="/my/init.zs"?>

Notice that the page is not yet attached to the desktop when the Page Initial phase executes.

Use java to init the page, simply specify a class with class attribute, an instance of the specified class is constructed, and then its doInit method is called.

For example:

<?init class="MyInit"?>

access UI component by ID

zscript can access UI component by ID, in the following example,

<window id="win_1">	
	<zscript><![CDATA[  
		win_1.title="given by zscript";				
	]]>
	</zscript>	
</window>

the result will be:

given by zscript

To access ui component in java code, you can use Path.getComponent() to obtain the mapping java object.

Window win = (Window)Path.getComponent("/win_1");
win.setTitle("given by java");

define Variable in zscript and access by EL

Variable defined in zscript can be easily accessed by EL.

<window>
	<zscript><![CDATA[
		var="abc";	
	]]>
	</zscript>
	1:${var}
</window>

The result shows 1:abc

To achieve the same result in java, you can rewrite it to

<window id="win_1" use="MyWindow">
	1:${win_1.var}
</window>

And the java file:

import org.zkoss.zul.Window;

public class MyWindow extends Window {
	String var = "abc";
	public String getVar(){
		return var;
	}
}

Note that ${win_1.var} will be mapped to MyWindow.getVar(). It's case sensitive. The mapping getter function will map first character of variable to upper case and append get in front of it.

Define class and methods in zscript

Thanks to the power of BeanShell, the implementation of Java classes can be done in zscript as follows.

<zscript>
    public class MyWindow extends Window {    
    }    
</zscript>
<window use="MyWindow"/>

And you can define methods in zscript

<window>
	<zscript><![CDATA[  
		public void sayHello(){
			alert("hello");
		}
	]]>
	</zscript>
	<button label="Say Hello!" onClick="sayHello()" />
</window>

access implicit object

In zscript, you can access implicit object like they are declared global variable.

<window>
	<zscript>
		self.title="given by zscript";
	</zscript>	
</window>

the result shows window's title becomes given by zscript

Not every implicit object has its own mapping java object. But there is always a workaround. If you want to access attribute in desktopScope, the following two statements are equivalent, assuming comp is a component.

comp.getAttribute("some", comp.DESKTOP_SCOPE);
comp.getDesktop().getAttribute("some");

Desktop.java has api such as getExecution, getPage, getSession, getWebApp. And AbstractComponent.java has getSpaceOwner, getDesktop. And Execution has getArg. And ForEachImpl.java has getEach

For example, you can get the implicit object execution from any component

comp.getDesktop().getExecution()

getVariable VS. getZScriptVariable

Variables defined in the namespace can be retrieved by use of the getVariable method.

On the other hand, variables defined in zscript is part of the interpret that interprets it. They are not a part of any namespace. In other words, you can not retrieve them by use of the getVariable method.

<zscript>
     var1 = 123; //var1 belongs to the interpreter, not any namespace
     page.getVariable("var1"); //returns null
</zscript>

Instead, you have to use getZScriptVariable to retrieve variables defined in zscript. Similarly, you can use getZScriptClass to retrieve classes and getZScriptFunction to retrieve methods defined in zscript. These methods will iterate through all loaded interpreters until the specified one is found.

If you want to search a particular interpreter, you can use the getInterpreter method to retrieve the interpreter first, as follows.

page.getInterpreter("JavaScript").getVariable("some"); //interpreter for JavaScript
page.getInterpreter(null).getVariable("some"); //interpreter for default language

The Scripting Codes in a Separate File

To separate codes and views, developers could put the scripting codes in a separated file, say sayHello.zs, and then use the src attribute to reference it.

<window>
    <button label="Say Hello" onClick="sayHello()"/>    
    <zscript src="sayHello.zs"/>    
</window>

which assumes the content of sayHello.zs is as follows.

int count = 0;
void sayHello() { //declare a global function
    alert("Hello World! "+ ++count);    
}

foreach

It's a common usage of zscript to define array of object for forEach

<window>
	<zscript><![CDATA[
	contacts = new String[] {"Monday", "Tuesday", "Wednesday"};
	]]>
	</zscript>
	<listbox>
		<listitem label="${each}" forEach="${contacts}"/>
	</listbox>
</window>

You can move the definition of array of object to java file as the following example. But remember to add id inside ${}, then EL knows which component has the variable.

<window id="win_1" use="MyWindow">
	<listbox>
		<listitem label="${each}" forEach="${win_1.contacts}"/>
	</listbox>
</window>
import org.zkoss.zul.Window;
 
public class MyWindow extends Window {
	String[] contacts = new String[] {"Monday", "Tuesday", "Wednesday"};
	public String[] getContacts(){
		return contacts;
	}
}

Or you can use appendChild. Manually generate all children in java, then you don't have to mix with forEach in ZUML. In the following example, the listitems will be appended to listbox after the button is clicked.

<window id="win_1" use="MyWindow" title="list">
	<listbox id="lb_1">
	</listbox>
	<button label="Hello" onClick="win_1.onTest()"/>
</window>
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Window;

public class MyWindow extends Window {
	String[] contacts = new String[] { "Monday", "Tuesday", "Wednesday" };

	public void onTest() {
		Listbox lb = (Listbox) getFellow("lb_1");
		for (int i = 0; i < contacts.length; i++) {
			Listitem li = new Listitem();
			li.setLabel(contacts[i]);
			lb.appendChild(li);
		}
	}
}

Macro Component

ZK provides two way to provide additional methods to macro component. One is through java, and the other is through zscript. Please refer to section Provide Additional Methods in chapter Macro for more information.

event handler

You can write event handler code in zscript or forward to event handler in java.

If the code is really simple, you can write it in zscript following event's name. Like following example:

<window>
	<button onClick='alert("here is a zcript")'/>
</window>

If the code is more than one line, for readability, you can use attribute.

<window>
	<button>
		<attribute name="onClick">
			alert("here is a zscript");
		</attribute>
	</button>
</window>

Or you can deliberately define a method in zscript, and call it from event's following zscript.

<window>
	<zscript><![CDATA[
		public void showAlert(){
			alert("here is a zcript too");
		}
	]]>
	</zscript>	
	<button onClick="showAlert()"/>
</window>

To handle events in java, you define static java method, or use zk attributes: use or apply.

The following example shows how to call static method in java,

<window>
	<button onClick="MyManager.showAlert()"/>
</window>

And MyManager.java

import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

public class MyManager {
	public static void showAlert(){
		try {
			Messagebox.show("handle event in java");
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

The following example uses zk attribute: use ,

<window id="win_1" use="MyWindow">
	<button onClick="win_1.showAlert()"/>
</window>
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;

public class MyWindow extends Window {
	public void showAlert(){
		try {
			Messagebox.show("handle event in java");
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

The following example uses zk attribute: apply. Note that name of event handling method must be onXXX.

<window apply="MyComposer">
	<button forward="onShowAlert()"/>
</window>
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericComposer;
import org.zkoss.zul.Messagebox;

public class MyComposer extends GenericComposer {
	public void onShowAlert(Event evt) {
		try {
			Messagebox.show("handle event in java");
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

GenericAutowireComposer

If you want to access UI object, you have to call getFellow to get its mapping java object. But why not just bind these components and data beans automatically? In the following example, you can omit the tedious getFellow calls, if your applied class extends GenericAutowireComposer.

In the following example, Full Name is updated automatically while you change First Name or Last Name.

<window apply="Autowired">
    <grid>
        <rows>
            <row>First Name: <textbox id="firstName" forward="onChange=onFirstName"/></row>
            <row>Last Name: <textbox id="lastName" forward="onChange=onLastName"/></row>
            <row>Full Name: <label id="fullName"/></row>
        </rows>
    </grid>
</window>

And the source code of Autowired.java:

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericAutowireComposer;
import org.zkoss.zul.Label;
import org.zkoss.zul.Textbox;


public class Autowired extends GenericAutowireComposer {
    private Textbox firstName; //auto-wired
    private Textbox lastName; //auto-wired
    private Label fullName; //auto-wired

    //all getFellow() codes are removed

    public void onFirstName(Event event) { 
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
    
    public void onLastName(Event event) {
        fullName.setValue(firstName.getValue()+" "+lastName.getValue());
    }
}

For detail information, please refer to ZK MVC Made Easy.



Last Update : 2022/01/19

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