2019-05-10 17:34

这个功能实现起来很容易,在 enjoy 之下扩做一个 #action 指令即可,然后这么来用:
#action("/blog/list")

具体实现有两种方式:
1:同步方案
让 #action 指令先得到目标 action 内容,再将内容直接输出,最简单粗暴的代码为:
public class ActionDriective extends Directive {
public void exec(Env env, Scope scope, Writer writer);
String action = exprList.eval(scope).toString();
String actionResult = HttpKit.get("localhost" + action);
write(writer, actionResult);
}
}

2:异步方案
让 #action 指令只生成一断 ajax 异步请求后端 action 的 js 代码,让其异步加载 html 片段,纯粹生成一段 js 代码而已,以下是一段示意性代码
$.ajax(action, {
type: "GET"
, cache: false
, dataType: "html"
, success: function(ret) {
// 得到当前位置的父标签,然后将 ajax 返回值插入进来
$(this).parent().append(ret);
}
});

在 action 指令内部生成以上 js 代码是极其容意的,js 代码的具体细节还需要你自行调整,以上仅为示意

最后配置一下:
engine.addDirective("action", ActionDirective.class);

从用户体验的角度来说,使用异步方案为好

2019-05-10 17:16

freemarker 的这种用法是在用 freemarker 的语法来写 java 代码该写的逻辑,建议使用 jfinal enjoy 模板的 shared method 或者 shared object 扩展来解决,代码最简洁,性能也最好,试用一次便知

2019-05-10 16:55

@opal #define 可以通过 #set(key, value) 将值返回

2019-05-10 16:54

我猜你可以在模板中使用一下 #render(...) 以及 #include(...)

2019-05-10 16:54

功能是一定可以实现的,就是不知道 struts 的 s:action 的具体功能是什么

2019-05-10 16:49

enjoy 拥有多种扩展方式,早就覆盖掉 freemarker 的模板方法功能了

试用一下 #define 定义模板函数,以及 shared object、shared method 扩展,第一种扩展只需要 5 分钟学会,第二、第三种只需要一分钟学会

2019-05-10 16:25

@李明明 细节问题,单步调试,不要猜谜

2019-05-10 16:24

@guanxb 通过上面的配置,本身就是隐藏具体路径的,只会暴露你的 web 项目路径

此外,这些配置全放在外部配置文件中,通过 PropKit 来加载,方便生产环境与开发环境协同

2019-05-10 16:10

在 configConstant(Constants me) 中配置 setBaseUploadPath(...) 即可,规则如下:

设置文件上传保存基础路径,当路径以 "/" 打头或是以 windows 磁盘盘符打头, 则将路径设置为绝对路径,否则路径将是以应用根路径为基础的相对路径
例如:
1:参数 "/var/www/upload" 为绝对路径,上传文件将保存到此路径之下
2:参数 "upload" 为相对路径,上传文件将保存到 PathKit.getWebRoot() + "/upload" 路径之下

此外,如果你又希望可以通过 web 能直接访问到这些上传的资源就更简单了,配置一下 undertow:
undertow.resourcePath=src/main/webapp, /var/www/upload

以上配置的 src/main/webapp 用于开发,当然你开发的时候 web 资源不在这里可以不需要,例如有些是将 web 资源放在 src/main/resources/static 下面的,可以这么配置:
undertow.resourcePath=classpath:static, /var/www/upload

以上两个 undertow.resourcePath 配置的第二个参数就是你希望在项目之外可以访问的 web 资源: /var/www/upload,可以配置多个,可以配置成与 setBaseUploadPath(...) 一样的值,极致方便

2019-05-10 15:37

windows 处在生产环境下,就是不该关闭那个 cmd 窗口,命令窗口虽然难看点,但却是最省事的用法

当然,你可以自己用一个包装性的工具,将启动脚本包装起来,对外显示一个 windows 应用程序样的小窗口,就跟安装 tomcat 启动程序一样

2019-05-10 15:34

js 处在前端,而模板引擎的动作全在后端,本质上来说 js 无法与后端模板引擎交互

js 只能是在等待后端模板引擎的数据吐出来以后,对吐出来的数据进行处理

一般有两种用法:
1:js 写在模板文件中,也就是 html 中,例如:
var value = #(value);
alert(value);

以上的 #(value) 在后端模板引擎渲染的时候会替换成一个具体值,例如 123,然后渲染完成的 html 到浏览器以后, js 可以通过 alert(value) 进行输出

2:js 发送 ajax 请求,后端 render 吐出来数据,然后 js 拿到数据后使用数据,例如:
下面的 js 代码是即将要上将的新版本 jfinal.com,ajax 动态获取 document 的 content
function clickDoc(event) {
// console.info(event);
event.preventDefault(); // 取代 return false 防止页面跳转

var menu = event.target.id;
var url = "/doc/ajaxContent/" + menu;
$.ajax(url, {
type: "GET"
, cache: false
, dataType: "html"
, success: function(ret) {
// 替换掉右侧文档内容
$("#ajaxContainer").replaceWith(ret);
}
});
}

下面的代码是后端模板引擎吐出 html 数据:
public void ajaxContent() {
Integer mainMenu = getInt(0);
Integer subMenu = getInt(1);
Document doc = srv.findById(mainMenu, subMenu);
if (doc != null) {
setAttr("doc", doc);
render("_content.html");
} else {
renderError(404);
}
}

而上面 ajaxContent() 中使用的 render("_content.html") 是文档页面除去左侧的 menu 后的所有 html 数据

新版本会在下周一或者周二发布,到时候可以参考一下

这里有必要强调一点,js 与模板引擎的运作有一个先后,模板引擎吐数据总是在前,js 使用数据总是在后,它们永远无法同时交互,只能一前一后

2019-05-09 19:51

@zz210891470 数据库字段名是什么,能否完全对得上,如果对不上的话,可以用 jfinal 生成器生成 setter 方法,并且使用 getBean 代替 getModel

2019-05-09 17:48

form 表单的 input 域的 name 没有给出来,没人猜到是什么

2019-05-09 17:46

如果一定要让 #date 指令很方便智能化处理 Date 与 String ,可以用一个 Shared method 扩展,大致如下:
public class MyDateSharedMethod {
public Date toDate(String dateStr) {
return 这里将 dateStr 自行转化成 Date;
}

public Date toDate(Date date) {
// 这里原值返回
return date
}
}

然后配置一下:
engine.addSharedMethod(MyDateSharedMethod.class);

用的时候这样:
#date(toDate(startDate));

这样用的时候,无论你的 starDate 是一个 Date 还是一个纯 String ,都会被正确处理

2019-05-09 17:44

是原样输出,右键点击浏览器,查看生成的 html ,可以看到是 2019-05-09

而不是 2015

输出指令会原样输出字符串 #(...)

注意,不要粗心搞错成 #date(...) 指令,这里专指 #(...)

#date 指令不支持输出 String,这样是错误的 :
#date("2019-5-1")