在线体验
官方手册

引用属性

属性引用是模板中的重要一部分,beetl支持属性同javascript的支持方式一样,如下:

  1. Beetl支持通过”.”号来访问对象的的属性,如果javascript一样。如果User对象有个getName()方法,那么在模板中,可以通过${xxx.name}来访问
  2. 如果模板变量是数组或者List类,这可以通过[] 来访问,如${userList[0]}
  3. 如果模板变量是Map类,这可以通过[]来访问,如${map[“name”]},如果key值是字符串类型,也可以使用${map.name}.但不建议这么使用,因为会让模板阅读者误以为是一个Pojo对象
  4. Beetl也支持Generic Get方式,即如果对象有一个public Object get(String key)方法,可以通过”.”号或者[]来访问,譬如 ${activityRecord.name}或者${activityRecord[“name”] }都将调用activityRecord的 get(String key)方法。如果对象既有具体属性,又有Generic get(这种模型设计方式是不值得鼓励),则以具体属性优先级高.
  5. Beetl也可以通过[]来引用属性,如${user[“name”]} 相当于${user.name}.这跟javascript保持一致。但建议不这么做,因为容易让阅读模板的人误认为这是一个Map类型
  6. Beetl 还可以定义额外的对象属性,而无需更改java对象,这叫着虚拟属性,如,对于所有集合,数组,都有共同的虚拟属性size.虚拟属性是“.~”+虚拟属性名
1
2
3
4
5
6
7
8
9
10
11
12
template.binding("list",service.getUserList());
template.binding("pageMap",service.getPage());

//在模板里
总共 ${list.~size}
<%
for(user in list){
%>
hello,${user.name};
<% } %>

当前页${pageMap['page']},总共${pageMap["total"]}

逻辑表达式

Beetl支持类似Javascript,java的条件表达式 如>,<,==,!=,>= , <= 以及 !, 还有&&和 || ,还有三元表达式等,如下例子

1
2
3
4
5
6
7
8
9
<%
var a = 1;
var b="good";
var c = null;

if(a!=1&&b=="good"&&c==null){
......
}
%>

三元表达式如果只考虑true条件对应的值的话,可以做简化,如下俩行效果是一样的。

1
2
3
4
5
<%
var a = 1 ;
%>
${a==1?"ok":''}
${a==1?"ok"}

循环语句

Beetl支持丰富的循环方式,如for-in,for(exp;exp;exp),以及while循环,以及循环控制语句break;continue; 另外,如果没有进入for循环体,还可以执行elsefor指定的语句。

for-in

for-in循环支持遍历集合对象,对于List和数组来说以及Iterator,对象就是集合对象,对于Map来说,对象就是Map.entry,如下俩个例子

1
2
3
4
5
6
<%
for(user in userList){
print(userLP.index);
print(user.name);
}
%>

第三行代码userLP是Beetl隐含定义的变量,是一个ILoopStatus实例,能在循环体内使用。其命名规范是item名称后加上LP,他提供了当前循环的信息,如

  1. *userLP.index *当前的索引,从1开始
  2. userLP.dataIndex 索引,从0开始
  3. *userLP.size *集合的长度
  4. userLP.first 是否是第一个
  5. userLP.last 是否是最后一个
  6. userLP.even 索引是否是偶数
  7. userLP.odd 索引是否是奇数

如何记住后缀是LP,有俩个诀窍,英语棒的是Loop的缩写,拼音好的是老婆的拼音缩写,这可以让程序员每次写到这的时候都会想想老婆(不管有没有,哈哈)

如下是Map使用例子

1
2
3
4
5
6
7
<%
for(entry in map){
var key = entry.key;
var value = entry.value;
print(value.name);
}
%>

for(exp;exp;exp)

对于渲染逻辑更为常见的是经典的for循环语句,如下例子

1
2
3
4
5
6
<%
var a = [1,2,3];
for(var i=0;i<a.~size;i++){
print(a[i]);
}
%>

while

对于渲染逻辑同样常见的有的while循环语句,如下例子

1
2
3
4
5
6
7
<%
var i = 0;
while(i<5){
print(i);
i++;
}
%>

elsefor

不同于通常程序语言,如果没有进入循环体,则不需额外的处理,模板渲染逻辑更常见情况是如果没有进入循环体,还需要做点什么,因此,对于for循环来说,还有elsefor 用来表达如果循环体没有进入,则执行elsefor 后的语句

1
2
3
4
5
6
7
8
<%
var list = [];
for(item in list){

}elsefor{
print("未有记录");
}
%>

条件语句

if else

同js一样,支持if else,如下例子

1
2
3
4
5
6
7
8
9
10
11
<%
var a =true;
var b = 1;
if(a&&b==1){

}else if(a){

}else{

}
%>

switch-case

同js一样,支持switch-case,如下例子

1
2
3
4
5
6
7
8
9
10
11
12
13
<%
var b = 1;
switch(b){
case 0:
print("it's 0");
break;
case 1:
print("it's 1");
break;
default:
print("error");
}
%>

switch变量可以支持任何类型,而不像js那样只能是整形

select-case

select-case 是switch case的增强版。他允许case 里有逻辑表达式,同时,也不需要每个case都break一下,默认遇到合乎条件的case执行后就退出。

1
2
3
4
5
6
7
8
9
10
11
<%
var b = 1;
select(b){
case 0,1:
print("it's small int");
case 2,3:
print("it's big int");
default:
print("error");
}
%>

select 后也不需要一个变量,这样case 后的逻辑表达式将决定执行哪个case.其格式是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%
select {
case boolExp,orBoolExp2:
doSomething();
}
%>


<%
var b = 1;
select{
case b<1,b>10:
print("it's out of range");
break;
case b==1:
print("it's 1");
break;
default:
print("error");
}
%>

try-catch

通常模板渲染逻辑很少用到try-catch 但考虑到渲染逻辑复杂性,以及模板也有不可控的地方,所以提供try catch,在渲染失败的时候仍然能保证输出正常

1
2
3
4
5
6
7
<%
try{
callOtherSystemView()
}catch(error){
print("暂时无数据");
}
%>

error代表了一个异常,你可以通过error.message 来获取可能的错误信息
也可以省略catch部分,这样出现异常,不做任何操作

函数调用

Beetl内置了少量实用函数,可以在Beetl任何地方调用。如下例子是调用date 函数,不传参数情况下,返回当前日期

1
2
3
4
5
<%
var date = date();
var len = strutil.length("cbd");
println("len="+len);
%>

定义beetl的方法非常容易,有三种方法

  1. 实现Function类的call方法,并添加到配置文件里,或者显示的通过代码注册registerFunction(name,yourFunction)
  2. 可以直接调用registerFunctionPackage(namespace,yourJavaObject),这时候yourJavaObject里的所有public方法都将注册为Beetl方法,方法名是namespace+”.”+方法名
  3. 可以直接写模板文件并且以html作为后缀,放到root/functions目录下,这样此模板文件自动注册为一个函数,其函数名是该模板文件名。

常用的函数

Beetl内置函数请参考附录,以下列出了常用的函数

  1. date 返回一个java.util.Date类型的变量,如 date() 返回一个当前时间(对应java的java.util.Date); ${date( “2011-1-1” , “yyyy-MM-dd” )} 返回指定日期,date(ms),指定一个毫秒数。相当于调用java.util.Date(ms)
  2. print 打印一个对象 print(user.name);
  3. println 打印一个对象以及回车换行符号,回车换号符号使用的是模板本身的,而不是本地系统的.如果仅仅打印一个换行符,则直接调用println() 即可
  4. nvl 函数nvl,如果对象为null,则返回第二个参数,否则,返回自己 nvl(user,”不存在”)
  5. isEmpty 判断变量或者表达式是否为空,变量不存在,变量为null,变量是空字符串,变量是空集合,变量是空数组,此函数都将返回true
  6. isNotEmpty 同上,判断对象是否不为空
  7. has 变量名为参数,判断是否存在此”全局变量”,如 has(userList),类似于1.x版本的exist(“userList”),但不需要输入引号了.注意,has和isEmpety 判断的是从java传到模板的全局变量,而不是临时变量
  8. hasAttrbiute 测试目标对象是否有此属性,hasAttribute(user,”name”)
  9. assert 如果表达式为false,则抛出异常
  10. trim 截取数字或者日期,返回字符,如trim(12.456,2)返回”12.45”,trim(date,’yyyy’)返回”2017”
  11. trunc 截取数字,保留指定的小数位,如trunc(12.456,2) 输出是12.45.不推荐使用,因为处理float有问题,兼容原因保留了
  12. decode 一个简化的if else 结构,如 decode(a,1,”a=1”,2,”a=2”,”不知道了”),如果a是1,这decode输出”a=1”,如果a是2,则输出”a==2”, 如果是其他值,则输出”不知道了”
  13. debug 在控制台输出debug指定的对象以及所在模板文件以及模板中的行数,如debug(1),则输出1 [在3行@/org/beetl/core/lab/hello.txt],也可以输出多个,如debug(“hi”,a),则输出hi,a=123,[在3行@/org/beetl/core/lab/hello.txt]
  14. parseInt 将数字或者字符解析为整形 如 parseInt(“123”);
  15. parseLong 将数字或者字符解析为长整形,parseInt(123.12);
  16. parseDouble 将数字或者字符解析为浮点类型 如parseDouble(“1.23”)
  17. range 接收三个参数,初始值,结束值,还有步增(可以不需要,则默认为1),返回一个Iterator,常用于循环中,如for(var i in range(1,5)) {print(i)},将依次打印1234.
  18. flush 强制io输出。
  19. json,将对象转成json字符串,如 var data = json(userList) 可以跟一个序列化规则 如,var data = json(userList,”[*].id:i”),具体参考 https://git.oschina.net/xiandafu/beetl-json
  20. pageCtx ,仅仅在web开发中,设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx(“title”,”用户添加页面”),在其后任何地方,可以pageCtx(“title”) 获取该变量
  21. type.new 创建一个对象实例,如 var user = type.new(“com.xx.User”); 如果配置了IMPORT_PACKAGE,则可以省略包名,type.new(“User”)
  22. type.name 返回一个实例的名字,var userClassName = type.name(user),返回”User”
  23. global 返回一个全局变量值,参数是一个字符串,如 var user = global(“user_”+i);
  24. cookie 返回指定的cookie对象 ,如var userCook = cookie(“user”),allCookies = cookie();

安全输出

安全输出是任何一个模板引擎必须重视的问题,否则,将极大困扰模板开发者。Beetl中,如果要输出的模板变量为null,则beetl将不做输出,这点不同于JSP,JSP输出null,也不同于Freemarker,如果没有用!,它会报错.

模板中还有俩种情况会导致模板输出异常

  • 有时候模板变量并不存在(譬如子模板里)
  • 模板变量为null,但输出的是此变量的一个属性,如${user.wife.name}

针对前俩种情况,可以在变量引用后加上!以提醒beetl这是一个安全输出的变量。

${user.wife.name! },即使user不存在,或者user为null,或者user.wife为null,或者user.wife.name为null beetl都不将输出

可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,譬如:

${user.wife.name!”单身”},如果user为null,或者user.wife为null,或者user.wife.name为null,输出”单身”

譬如

${user.birthday!@System.constants.DefaultBir}, 表示如果user为null,或者user. birthday为null,输出System.constants.DefaultBir

还有一种情况很少发生,但也有可能,输出模板变量发生的任何异常,如变量内部抛出的一个异常

这需要使用格式${!(变量)},这样,在变量引用发生任何异常情况下,都不作输出,譬如

${!(user.name)},beetl将会调用user.getName()方法,如果发生异常,beetl将会忽略此异常,继续渲染

值得注意的是,在变量后加上!不仅仅可以应用于占位符输出(但主要是应用于占位符输出),也可以用于表达式中,如:

1
2
3
4
5
6
<%
var k = user.name!'N/A'+user.age!;
%>
<%
${k}
%>

如果user为null,则k值将为N/A
在有些模板里,可能整个模板都需要安全输出,也可能模板的部分需要安全输出,使用者不必为每一个表达式使用!,可以使用beetl的安全指示符号来完成安全输出 如:

1
2
3
4
5
6
7
8
9
<%
DIRECTIVE SAFE_OUTPUT_OPEN;
%>
${user.wife.name}
模板其他内容,均能安全输出……
<%
//关闭安全输出。
DIRECTIVE SAFE_OUTPUT_CLOSE;
%>

Beetl不建议每一个页面都使用DIRECTIVE SAFE_OUTPUT_OPEN,这样,如果真有不期望的错误,不容易及时发现,其次,安全输出意味着beetl会有额外的代码检测值是否存在或者是否为null,性能会略差点。所以建议及时关闭安全输出(这不是必须的,但页面所有地方是安全输出,可能不容易发现错误)

在for-in 循环中 ,也可以为集合变量增加安全输出指示符号,这样,如果集合变量为null,也可以不进入循环体,如:

1
2
3
4
5
6
7
8
<%
var list = null;
for(item in list!){

}elsefor{
print("no data");
}
%>

变量是否存在

1
2
3
4
5
<%
if(has(flag)){
print("flag变量存在,可以访问")
}
%>

如果需要判断变量是否存在,如果存在,还有其他判断条件,通常都这么写

1
2
3
4
5
<%
if(has(flag)&&flag==0){
//code
}
%>

如果flag存在,而且值是0,都将执行if语句
但是,有更为简便的方法是直接用安全输出,如

1
2
3
4
5
<%
if(flag!0==0){
//code
}
%>

flag!0 取值是这样的,如果flag不存在,则为0,如果存在,则取值flag的值,类似三元表达式 if((has(flag)?flag:0)==0)

安全输出表达式

安全输出表达式可以包括

  • 字符串常量,如 ${user.count!”无结果”}
  • boolean常量 ${user.count!false}
  • 数字常量,仅限于正数,因为如果是负数,则类似减号,容易误用,因此,如果需要表示负数,请用括号,如${user.count!(-1)}
  • class直接调用,如${user.count!@User.DEFAULT_NUM}
  • 方法调用,如 ${user.count!getDefault() }
  • 属性引用,如 ${user.count!user.maxCount }
  • 任何表达式,需要用括号

格式化

几乎所有的模板语言都支持格式化,Beetl也不列外,如下例子Beetl提供的内置日期格式

1
2
3
4
<% var date = date(); %>
Today is ${date,dateFormat="yyyy-MM-dd"}.
Today is ${date,dateFormat}
salary is ${salary,numberFormat="##.##"}

格式化函数只需要一个字符串作为参数放在=号后面,如果没有为格式化函数输入参数,则使用默认值,dateFormat格式化函数默认值是local
Beetl也允许为指定的java class设定格式化函数,譬如已经内置了对java.util.Date,java.sql.Date 设置了了格式化函数,因此上面的例子可以简化为

1
${date,“yyyy-MM-dd”}

直接调用java方法和属性

可以通过符号@来表明后面表达式调用是java风格,可以调用对象的方法,属性

1
2
3
4
5
6
7
8
9
${@user.getMaxFriend(“lucy”)}
${@user.maxFriend[0].getName()}
${@com.xxxx.constants.Order.getMaxNum()}
${@com.xxxx.User$Gender.MAN}
<%
var max = @com.xxxx.constants.Order.MAX_NUM;
var c =1;
var d = @user.getAge(c);
%>

可以调用instance的public方法和属性,也可以调用静态类的属性和方法 ,需要加一个 @指示此调用是直接调用class,其后的表达式是java风格的。

  • GroupTemplate可以配置为不允许直接调用Class,具体请参考配置文件.
  • 也可以通过安全管理器配置到底哪些类Beetl不允许调用,具体请参考高级用法。默认情况,java.lang.Runtime,和 java.lang.Process不允许在模板里调用。你自己的安全管理器也可以配置为不能直接访问DAO类(避免了以前jsp可以访问任意代码带来的危害)
  • 重要:请按照java规范写类名和方法名,属性名。这样便于beetl识别到底调用的是哪个类,哪个方法。否则会抛出错误
  • 可以省略包名,只用类名。beetl将搜索包路径找到合适的类(需要设置配置“IMPORT_PACKAGE=包名.;包名.”,包名后需要跟一个“.”, 或者调用Configuration.addPkg)方法具体请参考附件配置文件说明
  • 内部类(包括枚举)访问同java一样,如User类有个内部枚举类Gender,访问是User$Gender
  • 表达式是java风格,但参数仍然是beetl表达式,比如 @user.sayHello(user.name).这里user.sayHello是java调用,user.name 仍然是beetl表达式