博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JSF页面中的JS取得受管bean的数据(受管bean发送数据到页面)
阅读量:6691 次
发布时间:2019-06-25

本文共 23104 字,大约阅读时间需要 77 分钟。

  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这个类,然后在配置文件中配置该重写的类。

  

1 /** 2  * This partial view context factory takes care that the {
@link OmniPartialViewContext} is properly initialized. 3 * 4 */ 5 public class CustomPartialViewContextFactory extends PartialViewContextFactory { 6 7 // Variables ------------------------------------------------------------------------------------------------------ 8 9 private PartialViewContextFactory wrapped;10 11 // Constructors ---------------------------------------------------------------------------------------------------12 13 /**14 * Construct a new OmniFaces partial view context factory around the given wrapped factory.15 * @param wrapped The wrapped factory.16 */17 public CustomPartialViewContextFactory(PartialViewContextFactory wrapped) {18 this.wrapped = wrapped;19 }20 21 // Actions --------------------------------------------------------------------------------------------------------22 23 /**24 * Returns a new instance of {
@link OmniPartialViewContext} which wraps the original partial view context.25 */26 @Override27 public PartialViewContext getPartialViewContext(FacesContext context) {28 return new CustomPartialViewContext(wrapped.getPartialViewContext(context));29 }30 31 /**32 * Returns the wrapped factory.33 */34 @Override35 public PartialViewContextFactory getWrapped() {36 return wrapped;37 }38 39 }

 该类重写还需要一个PartialViewContext ,此处没有直接继承PartialViewContext ,而是继承了PartialViewContext 的子类PartialViewContextWrapper,并重写了里面的方法:

1 /**  2  * 

3 * This OmniFaces partial view context extends and improves the standard partial view context as follows: 4 *

    5 *
  • Support for executing callback scripts by {
    @link PartialResponseWriter#startEval()}.
  • 6 *
  • Support for adding arguments to an ajax response.
  • 7 *
  • Any XML tags which Mojarra and MyFaces has left open after an exception in rendering of an already committed 8 * ajax response, will now be properly closed. This prevents errors about malformed XML.
  • 9 *
  • Fixes the no-feedback problem when a {
    @link ViewExpiredException} occurs during an ajax request on a page which 10 * is restricted by web.xml <security-constraint>. The enduser will now properly be 11 * redirected to the login page instead of retrieving an ajax response with only a changed view state (and effectively 12 * thus no visual feedback at all).
  • 13 *
14 * You can use the {
@link Ajax} utility class to easily add callback scripts and arguments. 15 *

16 * This partial view context is already registered by OmniFaces' own faces-config.xml and thus gets 17 * auto-initialized when the OmniFaces JAR is bundled in a web application, so end-users do not need to register this 18 * partial view context explicitly themselves. 19 * 20 * @author Bauke Scholtz 21 * @since 1.2 22 * @see OmniPartialViewContextFactory 23 */ 24 public class CustomPartialViewContext extends PartialViewContextWrapper { 25 26 // Constants ------------------------------------------------------------------------------------------------------ 27 28 private static final String AJAX_DATA = "var Faces=Faces||{};Faces.Ajax={data:%s};"; 29 private static final String ERROR_NO_OMNI_PVC = "There is no current CustomPartialViewContext instance."; 30 31 // Variables ------------------------------------------------------------------------------------------------------ 32 33 private PartialViewContext wrapped; 34 private Map

arguments; 35 private List
callbackScripts; 36 private CustomPartialResponseWriter writer; 37 38 // Constructors --------------------------------------------------------------------------------------------------- 39 40 /** 41 * Construct a new OmniFaces partial view context around the given wrapped partial view context. 42 * @param wrapped The wrapped partial view context. 43 */ 44 public CustomPartialViewContext(PartialViewContext wrapped) { 45 this.wrapped = wrapped; 46 setCurrentInstance(this); 47 } 48 49 // Actions -------------------------------------------------------------------------------------------------------- 50 51 @Override 52 public PartialResponseWriter getPartialResponseWriter() { 53 if (writer == null) { 54 writer = new CustomPartialResponseWriter(this, super.getPartialResponseWriter()); 55 } 56 57 return writer; 58 } 59 60 @Override // Necessary because this is missing in PartialViewContextWrapper (will be fixed in JSF 2.2). 61 public void setPartialRequest(boolean partialRequest) { 62 getWrapped().setPartialRequest(partialRequest); 63 } 64 65 @Override 66 public PartialViewContext getWrapped() { 67 return wrapped; 68 } 69 70 /** 71 * Add an argument to the partial response. This is as JSON object available by
OmniFaces.Ajax.data. 72 * For supported argument value types, read {
@link Json#encode(Object)}. If a given argument type is not supported, 73 * then an {
@link IllegalArgumentException} will be thrown during end of render response. 74 * @param name The argument name. 75 * @param value The argument value. 76 */ 77 public void addArgument(String name, Object value) { 78 if (arguments == null) { 79 arguments = new HashMap<>(); 80 } 81 82 arguments.put(name, value); 83 } 84 85 /** 86 * Add a callback script to the partial response. This script will be executed once the partial response is 87 * successfully retrieved at the client side. 88 * @param callbackScript The callback script to be added to the partial response. 89 */ 90 public void addCallbackScript(String callbackScript) { 91 if (callbackScripts == null) { 92 callbackScripts = new ArrayList<>(); 93 } 94 95 callbackScripts.add(callbackScript); 96 } 97 98 /** 99 * Reset the partial response. This clears any JavaScript arguments and callbacks set any data written to the100 * {
@link PartialResponseWriter}.101 * @see FullAjaxExceptionHandler102 */103 public void resetPartialResponse() {104 if (writer != null) {105 writer.reset();106 }107 108 arguments = null;109 callbackScripts = null;110 }111 112 /**113 * Close the partial response. If the writer is still in update phase, then end the update and the document. This114 * fixes the Mojarra problem of incomplete ajax responses caused by exceptions during ajax render response.115 * @see FullAjaxExceptionHandler116 */117 public void closePartialResponse() {118 if (writer != null && writer.updating) {119 try {120 writer.endUpdate();121 writer.endDocument();122 }123 catch (IOException e) {124 throw new FacesException(e);125 }126 }127 }128 129 // Static ---------------------------------------------------------------------------------------------------------130 131 /**132 * Returns the current instance of the OmniFaces partial view context.133 * @return The current instance of the OmniFaces partial view context.134 * @throws IllegalStateException When there is no current instance of the OmniFaces partial view context. That can135 * happen when the {
@link OmniPartialViewContextFactory} is not properly registered, or when there's another136 * {
@link PartialViewContext} implementation which doesn't properly delegate through the wrapped instance.137 */138 public static CustomPartialViewContext getCurrentInstance() {139 return getCurrentInstance(getContext());140 }141 142 /**143 * Returns the current instance of the OmniFaces partial view context from the given faces context.144 * @param context The faces context to obtain the current instance of the OmniFaces partial view context from.145 * @return The current instance of the OmniFaces partial view context from the given faces context.146 * @throws IllegalStateException When there is no current instance of the OmniFaces partial view context. That can147 * happen when the {
@link OmniPartialViewContextFactory} is not properly registered, or when there's another148 * {
@link PartialViewContext} implementation which doesn't properly delegate through the wrapped instance.149 */150 public static CustomPartialViewContext getCurrentInstance(FacesContext context) {151 CustomPartialViewContext instance = getContextAttribute(context, CustomPartialViewContext.class.getName());152 153 if (instance != null) {154 return instance;155 }156 157 // Not found. Well, maybe the context attribute map was cleared for some reason. Get it once again.158 instance = unwrap(context.getPartialViewContext());159 160 if (instance != null) {161 setCurrentInstance(instance);162 return instance;163 }164 165 // Still not found. Well, maybe RichFaces is installed which doesn't use PartialViewContextWrapper.166 if (Hacks.isRichFacesInstalled()) {167 PartialViewContext pvc = Hacks.getRichFacesWrappedPartialViewContext();168 169 if (pvc != null) {170 instance = unwrap(pvc);171 172 if (instance != null) {173 setCurrentInstance(instance);174 return instance;175 }176 }177 }178 179 // Still not found. Well, it's end of story.180 throw new IllegalStateException(ERROR_NO_OMNI_PVC);181 }182 183 private static void setCurrentInstance(CustomPartialViewContext instance) {184 setContextAttribute(CustomPartialViewContext.class.getName(), instance);185 }186 187 private static CustomPartialViewContext unwrap(PartialViewContext context) {188 PartialViewContext unwrappedContext = context;189 190 while (!(unwrappedContext instanceof CustomPartialViewContext) && unwrappedContext instanceof PartialViewContextWrapper) {191 unwrappedContext = ((PartialViewContextWrapper) unwrappedContext).getWrapped();192 }193 194 if (unwrappedContext instanceof CustomPartialViewContext) {195 return (CustomPartialViewContext) unwrappedContext;196 }197 else {198 return null;199 }200 }201 202 // Nested classes -------------------------------------------------------------------------------------------------203 204 /**205 * This OmniFaces partial response writer adds support for passing arguments to JavaScript context, executing206 * oncomplete callback scripts, resetting the ajax response (specifically for {
@link FullAjaxExceptionHandler}) and207 * fixing incomlete XML response in case of exceptions.208 * @author Bauke Scholtz209 */210 private static class CustomPartialResponseWriter extends PartialResponseWriter {211 212 // Variables --------------------------------------------------------------------------------------------------213 214 private CustomPartialViewContext context;215 private PartialResponseWriter wrapped;216 private boolean updating;217 218 // Constructors -----------------------------------------------------------------------------------------------219 220 public CustomPartialResponseWriter(CustomPartialViewContext context, PartialResponseWriter wrapped) {221 super(wrapped);222 this.wrapped = wrapped; // We can't rely on getWrapped() due to MyFaces broken PartialResponseWriter.223 this.context = context;224 }225 226 // Overridden actions -----------------------------------------------------------------------------------------227 228 /**229 * An override which checks if the web.xml security constraint has been triggered during this ajax request230 * (which can happen when the session has been timed out) and if so, then perform a redirect to the originally231 * requested page. Otherwise the enduser ends up with an ajax response containing only the new view state232 * without any form of visual feedback.233 */234 @Override235 public void startDocument() throws IOException {236 wrapped.startDocument();237 String loginURL = WebXml.INSTANCE.getFormLoginPage();238 239 if (loginURL != null) {240 FacesContext facesContext = FacesContext.getCurrentInstance();241 String loginViewId = normalizeViewId(facesContext, loginURL);242 243 if (loginViewId.equals(getViewId(facesContext))) {244 String originalURL = getRequestAttribute(facesContext, "javax.servlet.forward.request_uri");245 246 if (originalURL != null) {247 redirect(originalURL);248 }249 }250 }251 }252 253 /**254 * An override which remembers if we're updating or not.255 * @see #endDocument()256 * @see #reset()257 */258 @Override259 public void startUpdate(String targetId) throws IOException {260 updating = true;261 wrapped.startUpdate(targetId);262 }263 264 /**265 * An override which remembers if we're updating or not.266 * @see #endDocument()267 * @see #reset()268 */269 @Override270 public void endUpdate() throws IOException {271 updating = false;272 wrapped.endUpdate();273 }274 275 /**276 * An override which writes all {
@link OmniPartialViewContext#arguments} as JSON to the extension and all277 * {
@link OmniPartialViewContext#callbackScripts} to the eval. It also checks if we're still updating, which278 * may occur when MyFaces is used and an exception was thrown during rendering the partial response, and then279 * gently closes the partial response which MyFaces has left open.280 */281 @Override282 public void endDocument() throws IOException {283 if (updating) {284 // If endDocument() method is entered with updating=true, then it means that MyFaces is used and that285 // an exception was been thrown during ajax render response. The following calls will gently close the286 // partial response which MyFaces has left open.287 // Mojarra never enters endDocument() method with updating=true, this is handled in reset() method.288 endCDATA();289 endUpdate();290 }291 else {292 if (context.arguments != null) {293 startEval();294 write(String.format(AJAX_DATA, Json.encode(context.arguments)));295 endEval();296 }297 298 if (context.callbackScripts != null) {299 for (String callbackScript : context.callbackScripts) {300 startEval();301 write(callbackScript);302 endEval();303 }304 }305 }306 307 wrapped.endDocument();308 }309 310 // Custom actions ---------------------------------------------------------------------------------------------311 312 /**313 * Reset the partial response writer. It checks if we're still updating, which may occur when Mojarra is used314 * and an exception was thrown during rendering the partial response, and then gently closes the partial315 * response which Mojarra has left open. This would clear the internal state of the wrapped partial response316 * writer and thus make it ready for reuse without risking malformed XML.317 */318 public void reset() {319 try {320 if (updating) {321 // If reset() method is entered with updating=true, then it means that Mojarra is used and that322 // an exception was been thrown during ajax render response. The following calls will gently close323 // the partial response which Mojarra has left open.324 // MyFaces never enters reset() method with updating=true, this is handled in endDocument() method.325 endCDATA();326 endUpdate();327 wrapped.endDocument();328 }329 }330 catch (IOException e) {331 throw new FacesException(e);332 }333 finally {334 responseReset();335 }336 }337 338 // Delegate actions -------------------------------------------------------------------------------------------339 // Due to MyFaces broken PartialResponseWriter, which doesn't delegate to getWrapped() method, but instead to340 // the local variable wrapped, we can't use getWrapped() in our own PartialResponseWriter implementations.341 342 @Override343 public void startError(String errorName) throws IOException {344 wrapped.startError(errorName);345 }346 347 @Override348 public void startEval() throws IOException {349 wrapped.startEval();350 }351 352 @Override353 public void startExtension(Map
attributes) throws IOException {354 wrapped.startExtension(attributes);355 }356 357 @Override358 public void startInsertAfter(String targetId) throws IOException {359 wrapped.startInsertAfter(targetId);360 }361 362 @Override363 public void startInsertBefore(String targetId) throws IOException {364 wrapped.startInsertBefore(targetId);365 }366 367 @Override368 public void endError() throws IOException {369 wrapped.endError();370 }371 372 @Override373 public void endEval() throws IOException {374 wrapped.endEval();375 }376 377 @Override378 public void endExtension() throws IOException {379 wrapped.endExtension();380 }381 382 @Override383 public void endInsert() throws IOException {384 wrapped.endInsert();385 }386 387 @Override388 public void delete(String targetId) throws IOException {389 wrapped.delete(targetId);390 }391 392 @Override393 public void redirect(String url) throws IOException {394 wrapped.redirect(url);395 }396 397 @Override398 public void updateAttributes(String targetId, Map
attributes) throws IOException {399 wrapped.updateAttributes(targetId, attributes);400 }401 402 }403 }

View Code

在face-config.xml文件添加配置如下:

1  
2
com.context.CustomPartialViewContextFactory
3

 

可以看到其中定义了两个常量,一个警告说明,一个名为AJAX_DATA,里面写的是一段js的字符串。这个常量用在了 被重写的endDocument()方法里。这个方法主要做了两件事,第一是写入要传递的数据,并且以json格式打包,第二件事是包含了一个js方法。那么可以这样认为:该方法的作用是用一个js方法取得发送的数据。

  首先我们必须清楚,JSF使用f:ajax更新客户端的底层操作是怎样的。JSF更新数据是向页面发送了xml数据的,可以在firefox的 firebug下面的网络-XML面板下看到发送的xml数据,类似于以下的数据:

1 
2   
3     
-4426271603414575392:-5845678956333562288
4   
5

里面包含了需要更新的组件和组件状态。

我把omnifaces的Ajax代码整理出来以后,根据官方例子的用法,写了一个小demo测试,其中页面form表单代码如下:

1 
2
3
4
5
6
7
8
9 10
11
12
13

测试bean代码如下:

1 @ManagedBean(name="test2") 2 @ViewScoped 3 public class Test2 implements Serializable { 4  5     private static final long serialVersionUID = 8686669721840131192L; 6  7     public void callback() { 8         Ajax.oncomplete("alert('Hi, I am the oncomplete callback script!')"); 9     }10 11     public void argument() {12         Ajax.data("foo", "bar");13         Ajax.data("first", "one", "second", "two");14         Map
data = new HashMap<>();15 data.put("bool", true);16 data.put("number", 1.2F);17 data.put("date", new Date());18 data.put("array", new Integer[] { 1, 2, 3, 4, 5 });19 data.put("list", Arrays.asList("one", "two", "three"));20 Ajax.data(data);21 Ajax.oncomplete("showData()");22 }23 24 public void parseUser(){25 Ajax.data("user", new User(1, "bigbang"));26 Ajax.oncomplete("showUser()");27 }28 29 }

其中提供了三个页面响应的方法。当点击第一个button时,页面会弹出一个alert提示框。此时查看firefox下面的数据如下:

1 
2
3
-4426271603414575392:-5845678956333562288
4
alert('Hi, I am the oncomplete callback script!')
5
6

点击“获取数据到js”按钮,查看xml数据:

1 
2
3
-3364647386979820288:-1391656100755852530
4
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]}};
5
showData()
6
7

点击“showuser”按钮,查看xml数据:

1 
2
3
-3364647386979820288:-1391656100755852530
4
var Faces=Faces||{};Faces.Ajax={data:{"user":{"id":1,"name":"bigbang"}}};
5
showUser()
6
7

可以看出刚才的那个endDocumnt方法和AJAX_DATA常量的用意了,实际是构造了一个js对象,然后传入一个js方法到客户端,客户端会自动调用这个js方法,根据对象取得json数据。那么客户端的js只需要这样写就可以了:

1   

控制台显示数据如下:

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

转载于:https://www.cnblogs.com/bigbang92/p/jsf-javascript-bean-data.html

你可能感兴趣的文章
LeetCode 222. Count Complete Tree Nodes
查看>>
对Fiddler设置【Decrypt HTTPS traffic】后火狐浏览器打开https【您的连接并不安全】的解决方法...
查看>>
0059-乘积问题
查看>>
2019年的第一篇随笔
查看>>
关于公网ip的一些信息(摘抄)
查看>>
5分钟弄懂Docker!
查看>>
BZOJ1076:[SCOI2008]奖励关(状压DP,期望)
查看>>
BZOJ2223/3524:[POI2014] Couriers(主席树)
查看>>
MyEclipse — Maven+Spring+Struts+Hibernate 整合 [学习笔记-5]
查看>>
Nodejs 连接各种数据库集合例子
查看>>
easyui的datagrid用js插入数据等编辑功能的实现
查看>>
Windows App开发之集合控件与数据绑定
查看>>
AMD、CMD/AMD与CMD的区别
查看>>
Python~第一天
查看>>
Linux管理用户账号
查看>>
redis中使用lua脚本
查看>>
颜色数组
查看>>
ELASTICSEARCH清理过期数据
查看>>
oo第三次博客作业
查看>>
设计模式六大原则(转载)
查看>>