JSF页面中的JS取得受管bean的数据(受管bean发送数据到页面)
JSF中引入jsf.js文件之后,可以拦截jsf.ajax.request请求。一直希望有一种方法可以像jquery的ajax一样,能在js中异步取得服务器端发送的数据。无奈标准JSF并没有提供这样的方法。在一些JSF框架里面提供了这样的方法,比如primefaces的onComplete方法就是返回数据到一个js方法中的。JSF能在bean里面更新视图(非ajax更新),其中的PartialViewContext类就可以做到局部更新UI,在bean里获取到这个UI就可以了。于是在网上翻看了很多开源的JSF框架,无意中发现omnifaces这个开源框架。官网:http://omnifaces.org/。
当然,一个框架的东西会有很多,以个人之力要全部参透会有一些难度。开源框架更是集思广益,不过我所需要的只是其中的一部分而已,即在js中取得bean返回的数据。
根据omnifaces的showcase http://showcase.omnifaces.org/ 看到其中的一个Ajax工具类根据PartialViewContext类做到了js取得返回数据,不论字符串或者对象数据都可以发送,非常方便。于是根据提供的源码,顺藤摸瓜,copy了必须支持的类,整理出来了一个仅Ajax发送数据的jar。
omnifaces做的步骤大概是先重写了PartialViewContextFactory这个类,然后在配置文件中配置该重写的类。
/**
* This partial view context factory takes care that the {@link OmniPartialViewContext} is properly initialized.
*
*/
public class CustomPartialViewContextFactory extends PartialViewContextFactory { // Variables ------------------------------------------------------------------------------------------------------ private PartialViewContextFactory wrapped; // Constructors --------------------------------------------------------------------------------------------------- /**
* Construct a new OmniFaces partial view context factory around the given wrapped factory.
* @param wrapped The wrapped factory.
*/
public CustomPartialViewContextFactory(PartialViewContextFactory wrapped) {
this.wrapped = wrapped;
} // Actions -------------------------------------------------------------------------------------------------------- /**
* Returns a new instance of {@link OmniPartialViewContext} which wraps the original partial view context.
*/
@Override
public PartialViewContext getPartialViewContext(FacesContext context) {
return new CustomPartialViewContext(wrapped.getPartialViewContext(context));
} /**
* Returns the wrapped factory.
*/
@Override
public PartialViewContextFactory getWrapped() {
return wrapped;
} }
该类重写还需要一个PartialViewContext ,此处没有直接继承PartialViewContext ,而是继承了PartialViewContext 的子类PartialViewContextWrapper,并重写了里面的方法:
/**
* <p>
* This OmniFaces partial view context extends and improves the standard partial view context as follows:
* <ul>
* <li>Support for executing callback scripts by {@link PartialResponseWriter#startEval()}.</li>
* <li>Support for adding arguments to an ajax response.</li>
* <li>Any XML tags which Mojarra and MyFaces has left open after an exception in rendering of an already committed
* ajax response, will now be properly closed. This prevents errors about malformed XML.</li>
* <li>Fixes the no-feedback problem when a {@link ViewExpiredException} occurs during an ajax request on a page which
* is restricted by <code>web.xml</code> <code><security-constraint></code>. The enduser will now properly be
* redirected to the login page instead of retrieving an ajax response with only a changed view state (and effectively
* thus no visual feedback at all).</li>
* </ul>
* You can use the {@link Ajax} utility class to easily add callback scripts and arguments.
* <p>
* This partial view context is already registered by OmniFaces' own <code>faces-config.xml</code> and thus gets
* auto-initialized when the OmniFaces JAR is bundled in a web application, so end-users do not need to register this
* partial view context explicitly themselves.
*
* @author Bauke Scholtz
* @since 1.2
* @see OmniPartialViewContextFactory
*/
public class CustomPartialViewContext extends PartialViewContextWrapper { // Constants ------------------------------------------------------------------------------------------------------ private static final String AJAX_DATA = "var Faces=Faces||{};Faces.Ajax={data:%s};";
private static final String ERROR_NO_OMNI_PVC = "There is no current CustomPartialViewContext instance."; // Variables ------------------------------------------------------------------------------------------------------ private PartialViewContext wrapped;
private Map<String, Object> arguments;
private List<String> callbackScripts;
private CustomPartialResponseWriter writer; // Constructors --------------------------------------------------------------------------------------------------- /**
* Construct a new OmniFaces partial view context around the given wrapped partial view context.
* @param wrapped The wrapped partial view context.
*/
public CustomPartialViewContext(PartialViewContext wrapped) {
this.wrapped = wrapped;
setCurrentInstance(this);
} // Actions -------------------------------------------------------------------------------------------------------- @Override
public PartialResponseWriter getPartialResponseWriter() {
if (writer == null) {
writer = new CustomPartialResponseWriter(this, super.getPartialResponseWriter());
} return writer;
} @Override // Necessary because this is missing in PartialViewContextWrapper (will be fixed in JSF 2.2).
public void setPartialRequest(boolean partialRequest) {
getWrapped().setPartialRequest(partialRequest);
} @Override
public PartialViewContext getWrapped() {
return wrapped;
} /**
* Add an argument to the partial response. This is as JSON object available by <code>OmniFaces.Ajax.data</code>.
* For supported argument value types, read {@link Json#encode(Object)}. If a given argument type is not supported,
* then an {@link IllegalArgumentException} will be thrown during end of render response.
* @param name The argument name.
* @param value The argument value.
*/
public void addArgument(String name, Object value) {
if (arguments == null) {
arguments = new HashMap<>();
} arguments.put(name, value);
} /**
* Add a callback script to the partial response. This script will be executed once the partial response is
* successfully retrieved at the client side.
* @param callbackScript The callback script to be added to the partial response.
*/
public void addCallbackScript(String callbackScript) {
if (callbackScripts == null) {
callbackScripts = new ArrayList<>();
} callbackScripts.add(callbackScript);
} /**
* Reset the partial response. This clears any JavaScript arguments and callbacks set any data written to the
* {@link PartialResponseWriter}.
* @see FullAjaxExceptionHandler
*/
public void resetPartialResponse() {
if (writer != null) {
writer.reset();
} arguments = null;
callbackScripts = null;
} /**
* Close the partial response. If the writer is still in update phase, then end the update and the document. This
* fixes the Mojarra problem of incomplete ajax responses caused by exceptions during ajax render response.
* @see FullAjaxExceptionHandler
*/
public void closePartialResponse() {
if (writer != null && writer.updating) {
try {
writer.endUpdate();
writer.endDocument();
}
catch (IOException e) {
throw new FacesException(e);
}
}
} // Static --------------------------------------------------------------------------------------------------------- /**
* Returns the current instance of the OmniFaces partial view context.
* @return The current instance of the OmniFaces partial view context.
* @throws IllegalStateException When there is no current instance of the OmniFaces partial view context. That can
* happen when the {@link OmniPartialViewContextFactory} is not properly registered, or when there's another
* {@link PartialViewContext} implementation which doesn't properly delegate through the wrapped instance.
*/
public static CustomPartialViewContext getCurrentInstance() {
return getCurrentInstance(getContext());
} /**
* Returns the current instance of the OmniFaces partial view context from the given faces context.
* @param context The faces context to obtain the current instance of the OmniFaces partial view context from.
* @return The current instance of the OmniFaces partial view context from the given faces context.
* @throws IllegalStateException When there is no current instance of the OmniFaces partial view context. That can
* happen when the {@link OmniPartialViewContextFactory} is not properly registered, or when there's another
* {@link PartialViewContext} implementation which doesn't properly delegate through the wrapped instance.
*/
public static CustomPartialViewContext getCurrentInstance(FacesContext context) {
CustomPartialViewContext instance = getContextAttribute(context, CustomPartialViewContext.class.getName()); if (instance != null) {
return instance;
} // Not found. Well, maybe the context attribute map was cleared for some reason. Get it once again.
instance = unwrap(context.getPartialViewContext()); if (instance != null) {
setCurrentInstance(instance);
return instance;
} // Still not found. Well, maybe RichFaces is installed which doesn't use PartialViewContextWrapper.
if (Hacks.isRichFacesInstalled()) {
PartialViewContext pvc = Hacks.getRichFacesWrappedPartialViewContext(); if (pvc != null) {
instance = unwrap(pvc); if (instance != null) {
setCurrentInstance(instance);
return instance;
}
}
} // Still not found. Well, it's end of story.
throw new IllegalStateException(ERROR_NO_OMNI_PVC);
} private static void setCurrentInstance(CustomPartialViewContext instance) {
setContextAttribute(CustomPartialViewContext.class.getName(), instance);
} private static CustomPartialViewContext unwrap(PartialViewContext context) {
PartialViewContext unwrappedContext = context; while (!(unwrappedContext instanceof CustomPartialViewContext) && unwrappedContext instanceof PartialViewContextWrapper) {
unwrappedContext = ((PartialViewContextWrapper) unwrappedContext).getWrapped();
} if (unwrappedContext instanceof CustomPartialViewContext) {
return (CustomPartialViewContext) unwrappedContext;
}
else {
return null;
}
} // Nested classes ------------------------------------------------------------------------------------------------- /**
* This OmniFaces partial response writer adds support for passing arguments to JavaScript context, executing
* oncomplete callback scripts, resetting the ajax response (specifically for {@link FullAjaxExceptionHandler}) and
* fixing incomlete XML response in case of exceptions.
* @author Bauke Scholtz
*/
private static class CustomPartialResponseWriter extends PartialResponseWriter { // Variables -------------------------------------------------------------------------------------------------- private CustomPartialViewContext context;
private PartialResponseWriter wrapped;
private boolean updating; // Constructors ----------------------------------------------------------------------------------------------- public CustomPartialResponseWriter(CustomPartialViewContext context, PartialResponseWriter wrapped) {
super(wrapped);
this.wrapped = wrapped; // We can't rely on getWrapped() due to MyFaces broken PartialResponseWriter.
this.context = context;
} // Overridden actions ----------------------------------------------------------------------------------------- /**
* An override which checks if the web.xml security constraint has been triggered during this ajax request
* (which can happen when the session has been timed out) and if so, then perform a redirect to the originally
* requested page. Otherwise the enduser ends up with an ajax response containing only the new view state
* without any form of visual feedback.
*/
@Override
public void startDocument() throws IOException {
wrapped.startDocument();
String loginURL = WebXml.INSTANCE.getFormLoginPage(); if (loginURL != null) {
FacesContext facesContext = FacesContext.getCurrentInstance();
String loginViewId = normalizeViewId(facesContext, loginURL); if (loginViewId.equals(getViewId(facesContext))) {
String originalURL = getRequestAttribute(facesContext, "javax.servlet.forward.request_uri"); if (originalURL != null) {
redirect(originalURL);
}
}
}
} /**
* An override which remembers if we're updating or not.
* @see #endDocument()
* @see #reset()
*/
@Override
public void startUpdate(String targetId) throws IOException {
updating = true;
wrapped.startUpdate(targetId);
} /**
* An override which remembers if we're updating or not.
* @see #endDocument()
* @see #reset()
*/
@Override
public void endUpdate() throws IOException {
updating = false;
wrapped.endUpdate();
} /**
* An override which writes all {@link OmniPartialViewContext#arguments} as JSON to the extension and all
* {@link OmniPartialViewContext#callbackScripts} to the eval. It also checks if we're still updating, which
* may occur when MyFaces is used and an exception was thrown during rendering the partial response, and then
* gently closes the partial response which MyFaces has left open.
*/
@Override
public void endDocument() throws IOException {
if (updating) {
// If endDocument() method is entered with updating=true, then it means that MyFaces is used and that
// an exception was been thrown during ajax render response. The following calls will gently close the
// partial response which MyFaces has left open.
// Mojarra never enters endDocument() method with updating=true, this is handled in reset() method.
endCDATA();
endUpdate();
}
else {
if (context.arguments != null) {
startEval();
write(String.format(AJAX_DATA, Json.encode(context.arguments)));
endEval();
} if (context.callbackScripts != null) {
for (String callbackScript : context.callbackScripts) {
startEval();
write(callbackScript);
endEval();
}
}
} wrapped.endDocument();
} // Custom actions --------------------------------------------------------------------------------------------- /**
* Reset the partial response writer. It checks if we're still updating, which may occur when Mojarra is used
* and an exception was thrown during rendering the partial response, and then gently closes the partial
* response which Mojarra has left open. This would clear the internal state of the wrapped partial response
* writer and thus make it ready for reuse without risking malformed XML.
*/
public void reset() {
try {
if (updating) {
// If reset() method is entered with updating=true, then it means that Mojarra is used and that
// an exception was been thrown during ajax render response. The following calls will gently close
// the partial response which Mojarra has left open.
// MyFaces never enters reset() method with updating=true, this is handled in endDocument() method.
endCDATA();
endUpdate();
wrapped.endDocument();
}
}
catch (IOException e) {
throw new FacesException(e);
}
finally {
responseReset();
}
} // Delegate actions -------------------------------------------------------------------------------------------
// Due to MyFaces broken PartialResponseWriter, which doesn't delegate to getWrapped() method, but instead to
// the local variable wrapped, we can't use getWrapped() in our own PartialResponseWriter implementations. @Override
public void startError(String errorName) throws IOException {
wrapped.startError(errorName);
} @Override
public void startEval() throws IOException {
wrapped.startEval();
} @Override
public void startExtension(Map<String, String> attributes) throws IOException {
wrapped.startExtension(attributes);
} @Override
public void startInsertAfter(String targetId) throws IOException {
wrapped.startInsertAfter(targetId);
} @Override
public void startInsertBefore(String targetId) throws IOException {
wrapped.startInsertBefore(targetId);
} @Override
public void endError() throws IOException {
wrapped.endError();
} @Override
public void endEval() throws IOException {
wrapped.endEval();
} @Override
public void endExtension() throws IOException {
wrapped.endExtension();
} @Override
public void endInsert() throws IOException {
wrapped.endInsert();
} @Override
public void delete(String targetId) throws IOException {
wrapped.delete(targetId);
} @Override
public void redirect(String url) throws IOException {
wrapped.redirect(url);
} @Override
public void updateAttributes(String targetId, Map<String, String> attributes) throws IOException {
wrapped.updateAttributes(targetId, attributes);
} }
}
在face-config.xml文件添加配置如下:
<factory>
<partial-view-context-factory>com.context.CustomPartialViewContextFactory</partial-view-context-factory>
</factory>
可以看到其中定义了两个常量,一个警告说明,一个名为AJAX_DATA,里面写的是一段js的字符串。这个常量用在了 被重写的endDocument()方法里。这个方法主要做了两件事,第一是写入要传递的数据,并且以json格式打包,第二件事是包含了一个js方法。那么可以这样认为:该方法的作用是用一个js方法取得发送的数据。
首先我们必须清楚,JSF使用f:ajax更新客户端的底层操作是怎样的。JSF更新数据是向页面发送了xml数据的,可以在firefox的 firebug下面的网络-XML面板下看到发送的xml数据,类似于以下的数据:
<partial-response id="j_id1">
<changes>
<update id="j_id1:javax.faces.ViewState:0">-4426271603414575392:-5845678956333562288</update>
</changes>
</partial-response>
里面包含了需要更新的组件和组件状态。
我把omnifaces的Ajax代码整理出来以后,根据官方例子的用法,写了一个小demo测试,其中页面form表单代码如下:
<h:form prependId="false">
<h:outputLabel value="#{test2.radom}" id="outLabel"/>
<h:commandButton action="#{test2.callback}" value="请求服务端js">
<f:ajax/>
</h:commandButton>
<h:commandButton value="获取服务端数据到js">
<f:ajax listener="#{test2.argument}"/>
</h:commandButton> <h:commandButton value="showUser">
<f:ajax listener="#{test2.parseUser}"/>
</h:commandButton>
</h:form>
测试bean代码如下:
@ManagedBean(name="test2")
@ViewScoped
public class Test2 implements Serializable { private static final long serialVersionUID = 8686669721840131192L; public void callback() {
Ajax.oncomplete("alert('Hi, I am the oncomplete callback script!')");
} public void argument() {
Ajax.data("foo", "bar");
Ajax.data("first", "one", "second", "two");
Map<String, Object> data = new HashMap<>();
data.put("bool", true);
data.put("number", 1.2F);
data.put("date", new Date());
data.put("array", new Integer[] { 1, 2, 3, 4, 5 });
data.put("list", Arrays.asList("one", "two", "three"));
Ajax.data(data);
Ajax.oncomplete("showData()");
} public void parseUser(){
Ajax.data("user", new User(1, "bigbang"));
Ajax.oncomplete("showUser()");
} }
其中提供了三个页面响应的方法。当点击第一个button时,页面会弹出一个alert提示框。此时查看firefox下面的数据如下:
<partial-response id="j_id1">
<changes>
<update id="j_id1:javax.faces.ViewState:0">-4426271603414575392:-5845678956333562288</update>
<eval>alert('Hi, I am the oncomplete callback script!')</eval>
</changes>
</partial-response>
点击“获取数据到js”按钮,查看xml数据:
<partial-response id="j_id1">
<changes>
<update id="j_id1:javax.faces.ViewState:0">-3364647386979820288:-1391656100755852530</update>
<eval>var Faces=Faces||{};Faces.Ajax={data:{"second":"two","number":1.2,"list":["one","two","three"],"foo":"bar","bool":true,"date":"Fri, 17 Jul 2015 09:17:50 GMT","first":"one","array":[1,2,3,4,5]}};</eval>
<eval>showData()</eval>
</changes>
</partial-response>
点击“showuser”按钮,查看xml数据:
<partial-response id="j_id1">
<changes>
<update id="j_id1:javax.faces.ViewState:0">-3364647386979820288:-1391656100755852530</update>
<eval>var Faces=Faces||{};Faces.Ajax={data:{"user":{"id":1,"name":"bigbang"}}};</eval>
<eval>showUser()</eval>
</changes>
</partial-response>
可以看出刚才的那个endDocumnt方法和AJAX_DATA常量的用意了,实际是构造了一个js对象,然后传入一个js方法到客户端,客户端会自动调用这个js方法,根据对象取得json数据。那么客户端的js只需要这样写就可以了:
<script>
function showData() {
var data = Faces.Ajax.data;
$.each(data, function(key, value) {
console.log(key+"---"+JSON.stringify(value));
});
} function showUser(){
var data = Faces.Ajax.data;
$.each(data, function(key,value){
var user = JSON.stringify(value);
var u = $.parseJSON(user);
console.log("userName----"+u.name);
}
</script>
控制台显示数据如下:

OK,大功告成了!其中的细节不用细究,无非就是掺杂了各种数据转换为json格式、数据的包装和写入。最后说明一点,omnifaces需要CDI的支持,必须导入CDI的jar包。

JSF页面中的JS取得受管bean的数据(受管bean发送数据到页面)的更多相关文章
- 【问题】Asp.net MVC 的cshtml页面中调用JS方法传递字符串变量参数
[问题]Asp.net MVC 的cshtml页面中调用JS方法传递字符串变量参数. [解决]直接对变量加引号,如: <button onclick="deleteProduct('@ ...
- MVC学习随笔----如何在页面中添加JS和CSS文件
http://blog.csdn.net/xxjoy_777/article/details/39050011 1.如何在页面中添加Js和CSS文件. 我们只需要在模板页中添加JS和CSS文件,然后子 ...
- JSF页面中使用js函数回调后台bean方法并获取返回值的方法
由于primefaces在国内使用的并不是太多,因此,国内对jsf做系统.详细的介绍的资料很少,即使有一些资料,也仅仅是对国外资料的简单翻译或者是仅仅讲表面现象(皮毛而已),它们的语句甚至还是错误的, ...
- JSF页面中使用js函数回调后台action方法
最近遇到了一个问题就是在JSF页面中嵌入html页面,这个html页面中很多功能是使用js动态生成的,现在需要在js函数里想去调用JSF中action类method()方法并动态传送数据给后台进行处理 ...
- 巧用开发者工具的控制台来调试页面中的js语句
因为要弄某网页的一个自动登陆工具,所以需要对此网页中的元素利用js进行选取和操作,复杂的js选取如果直接在头脑中想很容易出错,而且一旦出错也不好判断错误原因. 而浏览器带的开发者工具的控制台功能,就给 ...
- 在页面中使用js
JavaScript:用来在页面编写特效的,和HTML\CSS一样当都是由浏览器解析 JavaScript语言 一.JS如何运行(JavaScript,jscript,VbScript,applet ...
- 页面中引入js的几种方法
通常大家最为熟悉的是一下两种方法: 在页面中直接写入<script type="text/javascript">js代码</script>. 在页面中引入 ...
- 对于HTML页面中CSS, JS, HTML的加载与执行过程的简单分析
来自 https://blog.csdn.net/u011088260/article/details/79563315 最近在研究HTML页面中JavaScript的执行顺序问题.在Java ...
- js中全局变量修改后的值不生效【jsp页面中各个js中内容的加载顺序】
一个老项目中,一个jsp文件中有很多个js文件, 现在要在页面上的一个地方判断一个状态,因为一直找不到原来是在哪里修改的那个状态,所以决定不找了,而是在比较靠前引入的一个js中定义一个全局变量,然后在 ...
随机推荐
- R读取大数据data.table包之fread
>library(data.table)>data=fread("10000000.txt")>Read 9999999 rows and 71 (of 71) ...
- JSP JavaBeans
Javabean的设计原则 公有类 无参公有构造方法 私有属性 getter和setter方法 在Jsp页面中如何使用Javabeans? 像使用普通Java类一样,创建JavaBeans实例. 在J ...
- HDU 3466 Proud Merchants 排序 背包
题意:物品有三个属性,价格p,解锁钱数下线q(手中余额>=q才有机会购买该商品),价值v.钱数为m,问购买到物品价值和最大. 思路:首先是个01背包问题,但购买物品受限所以应先排序.考虑相邻两个 ...
- maven 简介 —— maven权威指南学习笔记(一)
maven是什么?有什么用? Maven是一个项目管理工具,它包含了 一个项目对象模型 (Project Object Model), 一组标准集合, 一个项目生命周期(ProjectLifecycl ...
- redis memcached MongoDB
我们现在使用的模式是,对于直接的key value对需缓存的直接用memcached.对于collection类型就使用Redis.对于大数据量的内容性的东西,我们打算尝试用mongoDB.也正在学习 ...
- Spark及其生态系统简介总结
Spark拥有DAG执行引擎,支持在内存中对数据进行迭代计算 Spark不仅支持Scala编写应用程序,而且支持Java和Python等语言进行编写,特别是Scala是一种高效.可拓展的语言,能够用简 ...
- pf_ring DNA接收流程代码分析
经过一个月的学习,对pf_ring DNA的内核部分有了一些认识,本文侧重pf_ring对ixgbe的改动分析. 先说一说接收流程吧,流程如下: 其中,硬中断处理函数是ixgbe_msix_clean ...
- Spring的DI初步
DI:依赖注入 第一个DEMO:域属性注入 java类:(Car类和Stu类,学生有一辆小汽车) package cn.dawn.day02di; /** * Created by Dawn on 2 ...
- php提前输出响应及注意问题
1.浏览器和服务器之间是通过HTTP进行通信的,浏览器发送请求给服务器,服务器处理完请求后,发送响应结果给浏览器,浏览器展示给用户.如果服务器处理请求时间比较长,那么浏览器就需要等待服务器的处理结果. ...
- sql中的group by 和 having 用法
sql中的group by 用法:-- Group By语句从英文的字面意义上理解就是“根据(by)一定的规则进行分组(Group)”.--它的作用是通过一定的规则将一个数据集划分成若干个小的区域,然 ...