前人关于FreeMarker 的总结
一宗罪:freemarker的变量必须有值,没有被赋值的变量就会抛出异常,那个黄黄的freemarker出错页面,真是让人看了太难过了。
freemarker的FAQ上面冠冕堂皇的说,未赋值的变量强制抛错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。但是实际的效果是:带来的是非常大的编程麻烦,程序里面几乎所有可能出现空值的变量统统需要加上${xxx?if_exists},有些循环条件还需要写if判断,这样不但没有杜绝应该杜绝的错误,反而极大增加了编程的麻烦。
二宗罪:freemarker的map限定key必须是string,其他数据类型竟然无法操作!这一点就不讲了,JavaEye上面已经有人抱怨过了。连Webwork的开发人员Pat Lightboy都在抱怨这一点。
三宗罪:freemarker为了编程方便把不可序列化的东西往session里面放!
freemarker支持在页面里面直接操作Session,request等,例如${Session[...]},方便确实很方便,但是一旦需要做群集,就会报错。
但是这些小错误,个人认为无伤大雅,毕竟WebWork开发小组就是用FreeMaker的,那么我们在项目的时候肯会接触到 .ftl 文件,所以花几个单位时间学习下,还是必要的
现在来看看
FreeMaker
有哪些变量
- Strings: "Foo" or 'Foo' or "It's /"quoted/"" or r"C:/raw/string" 最后一种用法要注意
- Numbers: 123.45
- Booleans: true, false
- Sequences: ["foo", "bar", 123.45], 1..100
- Hashes: {"name":"green mouse", "price":150} 这里注意,lookup name必须是String
下面来看看关于 String,Sequence,Hash的操作方法
有一种很典型的用法错误
<#if ${isBig}>Wow!</#if>
,
Interpolations work only in text sections (e.g.
<h1>Hello ${name}!</h1>
) and in string literals
(e.g.
<#include "/footer/${company}.html">
)
这里有几个关于Sequence的例子,值得看一下
|
<#assign x = ["red", 16, "blue", "cyan"]>
"blue": ${x?seq_contains("blue")?string("yes", "no")}
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}
| |
|
The output will be:
|
"blue": yes
"yellow": no
16: yes
"16": no
| |
|
seq_index_of
seq_last_index_of
还发现里面居然有排序功能
| | |
|
<#assign ls = ["whale", "Barbara", "zeppelin", "aardvark", "beetroot"]?sort>
<#list ls as i>${i} </#list>
| |
|
| | |
|
<#assign ls = [
{"name":"whale", "weight":2000},
{"name":"Barbara", "weight":53},
{"name":"zeppelin", "weight":-200},
{"name":"aardvark", "weight":30},
{"name":"beetroot", "weight":0.3}
]>
Order by name:
<#list ls?sort_by("name") as i>
- ${i.name}: ${i.weight}
</#list>
Order by weight:
<#list ls?sort_by("weight") as i>
- ${i.name}: ${i.weight}
</#list>
| |
|
最后还有一个函数非常之实用
|
<#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>
<#list seq?chunk(4) as row>
<#list row as cell>${cell} </#list>
</#list>
<#list seq?chunk(4, '-') as row>
<#list row as cell>${cell} </#list>
</#list>
| |
|
The output will be:
|
a b c d
e f g h
i j
a b c d
e f g h
i j - -
| |
|
想想以前写网上商城购物系统的时候,从数据库里出来的数据都要把它们转换成几行几列的样子,算法都是自己写的,这种时代要过去了,呵呵
·
Comparison
:
x == y
,
x != y
,
x < y
,
x > y
,
x >= y
,
x <= y
,
x < y
, ...etc.
因为FreeMarker无法区分大于号和结尾符>的区别,所以最好用括号括起来
Operator group | Operators |
---|
postfix operators | [subvarName] [subStringRange] . ? (methodParams) expr!default expr! expr?? |
unary operators | +expr -expr !expr |
multiplicative | * / % |
additive | + - |
relational | < > <= >= (and quivalents: gt, lt, etc.) |
equality | == != (and equivalents: =) |
logical AND | && |
logical OR | || |
numerical range | .. |
Escape sequence
|
Meaning
|
/"
|
Quotation mark (u0022)
|
/'
|
Apostrophe (a.k.a. apostrophe-quote) (u0027)
|
//
|
Back slash (u005C)
|
/n
|
Line feed (u000A)
|
/r
|
Carriage return (u000D)
|
/t
|
Horizontal tabulation (a.k.a. tab) (u0009)
|
/b
|
Backspace (u0008)
|
/f
|
Form feed (u000C)
|
/l
|
Less-than sign:
<
|
/g
|
Greater-than sign:
>
|
/a
|
Ampersand:
&
|
/{
|
Curly bracket:
{
|
/x
Code
|
Character given with its hexadecimal Unicode code (UCS code)
|
<#list 1..8 as x> 注意不要写成 [1..8]
${x}<br>
</#list>
/*-----------------以下会出错--------------------*/
<#list ["aa",3,true] as x>
${x}<br>
</#list>
<#list ["aa",3,[1,2]] as x>
${x}<br>
</#list>
Expecting a string, date or number here, Expression x is instead a freemarker.template.TemplateBooleanModel$2
可见,list中只能是string, date or number
先来看看最基本的innerHTML用法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Using responseText with innerHTML</title>
<script type="text/javascript">
var xmlHttp;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}
function startRequest() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET", "innerHTML.xml", true);
xmlHttp.send(null);
}
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
document.getElementById("results").innerHTML = xmlHttp.responseText;
} 这个地方通过DOM API得到元素
}
}
</script>
</head>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<body>
<form action="#">
<input type="button" value="Search for Today's Activities" onclick="startRequest();"/>
</form>
<div id="results"></div>
</body>
</html>
innerHTML.xml
<table border="1">
<tr>
<th>Activity Name</th>
<th>Location</th>
<th>Time</th>
</tr>
<tr>
<td>Waterskiing</td>
<td>Dock #1</td>
<td>9:00 AM</td>
</tr>
<tr>
<td>Volleyball</td>
<td>East Court</td>
<td>2:00 PM</td>
</tr>
</table>
下面的例子看看如何用DOM API来修改页面中的信息
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
>
<
title
>
Dynamically Editing Page Content
</
title
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
script
type
="text/javascript"
>
var xmlHttp;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}
function doSearch() {
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET", "dynamicContent.xml", true);
xmlHttp.send(null);
}
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
clearPreviousResults();
parseResults();
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function clearPreviousResults() {
var header = document.getElementById("header");
if(header.hasChildNodes()) {
header.removeChild(header.childNodes[0]);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
var tableBody = document.getElementById("resultsBody");
while(tableBody.childNodes.length > 0) {
tableBody.removeChild(tableBody.childNodes[0]);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function parseResults() {
var results = xmlHttp.responseXML;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
var property = null;
var address = "";
var price = "";
var comments = "";
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
var properties = results.getElementsByTagName("property");
for(var i = 0; i < properties.length; i++) {
property = properties[i];
address = property.getElementsByTagName("address")[0].firstChild.nodeValue;
price = property.getElementsByTagName("price")[0].firstChild.nodeValue;
comments = property.getElementsByTagName("comments")[0].firstChild.nodeValue;
addTableRow(address, price, comments);
}
var header = document.createElement("h2");
var headerText = document.createTextNode("Results:");
header.appendChild(headerText);
document.getElementById("header").appendChild(header);
document.getElementById("resultsTable").setAttribute("border", "1");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function addTableRow(address, price, comments) {
var row = document.createElement("tr");
var cell = createCellWithText(address);
row.appendChild(cell);
cell = createCellWithText(price);
row.appendChild(cell);
cell = createCellWithText(comments);
row.appendChild(cell);
document.getElementById("resultsBody").appendChild(row);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function createCellWithText(text) {
var cell = document.createElement("td");
var textNode = document.createTextNode(text);
cell.appendChild(textNode);
return cell;
}
</
script
>
</
head
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
body
>
<
h1
>
Search Real Estate Listings
</
h1
>
<
form
action
="#"
>
Show listings from
<
select
>
<
option
value
="50000"
>
$50,000
</
option
>
<
option
value
="100000"
>
$100,000
</
option
>
<
option
value
="150000"
>
$150,000
</
option
>
</
select
>
to
<
select
>
<
option
value
="100000"
>
$100,000
</
option
>
<
option
value
="150000"
>
$150,000
</
option
>
<
option
value
="200000"
>
$200,000
</
option
>
</
select
>
<
input
type
="button"
value
="Search"
onclick
="doSearch();"
/>
</
form
>
<
span
id
="header"
>
</
span
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
table
id
="resultsTable"
width
="75%"
border
="0"
>
<
tbody
id
="resultsBody"
>
</
tbody
>
</
table
>
</
body
>
</
html
>
<?
xml version="1.0" encoding="UTF-8"
?>
<
properties
>
<
property
>
<
address
>
812 Gwyn Ave
</
address
>
<
price
>
$100,000
</
price
>
<
comments
>
Quiet, serene neighborhood
</
comments
>
</
property
>
<
property
>
<
address
>
3308 James Ave S
</
address
>
<
price
>
$110,000
</
price
>
<
comments
>
Close to schools, shopping, entertainment
</
comments
>
</
property
>
<
property
>
<
address
>
98320 County Rd 113
</
address
>
<
price
>
$115,000
</
price
>
<
comments
>
Small acreage outside of town
</
comments
>
</
property
>
</
properties
>
接下来一个例子实现的是:通过JSON来做到客户端与服务端的大量数据提交,而不再是简单的 .action?id=1&name=wxy&color=red 而是可以传递类似XML的数据结构,甚至对象的传递
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
>
<
title
>
JSON Example
</
title
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
script
type
="text/javascript"
src
="json.js"
></
script
>
<
script
type
="text/javascript"
>
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
var xmlHttp;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}
function doJSON() {
var car = getCarObject();
//Use the JSON JavaScript library to stringify the Car object
var carAsJSON = JSON.stringify(car);
将对象通过JSON转化传递到服务器上去,在那里再进行解析
alert("Car object as JSON: " + carAsJSON);
var url = "JSONExample?timeStamp=" + new Date().getTime();
createXMLHttpRequest();
xmlHttp.open("POST", url, true);
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.send(carAsJSON);
} 注意这里的POST 与 GET 是不同的。
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
parseResults();
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function parseResults() {
var responseDiv = document.getElementById("serverResponse");
if(responseDiv.hasChildNodes()) {
responseDiv.removeChild(responseDiv.childNodes[0]);
}
var responseText = document.createTextNode(xmlHttp.responseText);
responseDiv.appendChild(responseText);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function getCarObject() {
return new Car("Dodge", "Coronet R/T", 1968, "yellow");
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
function Car(make, model, year, color) {
this.make = make;
this.model = model;
this.year = year;
this.color = color;
}
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
</
script
>
</
head
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
body
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
br
/><
br
/>
<
form
action
="#"
>
<
input
type
="button"
value
="Click here to send JSON data to the server"
onclick
="doJSON();"
/>
</
form
>
<
h2
>
Server Response:
</
h2
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
<
div
id
="serverResponse"
></
div
>
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
</
body
>
</
html
>
package
ajaxbook.chap3;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
import
java.io.
*
;
import
java.net.
*
;
import
java.text.ParseException;
import
javax.servlet.
*
;
import
javax.servlet.http.
*
;
import
org.json.JSONObject;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
public
class
JSONExample
extends
HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String json = readJSONStringFromRequestBody(request);
//Use the JSON-Java binding library to create a JSON object in Java
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(json);
}
catch(ParseException pe) {
System.out.println("ParseException: " + pe.toString());
}
String responseText = "You have a " + jsonObject.getInt("year") + " "
+ jsonObject.getString("make") + " " + jsonObject.getString("model")
+ " " + " that is " + jsonObject.getString("color") + " in color.";
轻松进行类型转换
response.setContentType("text/xml");
response.getWriter().print(responseText);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
private String readJSONStringFromRequestBody(HttpServletRequest request){
StringBuffer json = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while((line = reader.readLine()) != null) {
json.append(line);
}
}
catch(Exception e) {
System.out.println("Error reading JSON string: " + e.toString());
}
return json.toString();
}
}