添加 Ajax 并发送一个负载到服务器在 小节,我完全在客户端处理放置链接,通过呈现一个显示链接标题和 URL 的示警框(见 )。对于 Feeds 应用程序以及大多数提供拖放的重要 JSF 应用程序而言,一个放置通常伴随着一次服务器访问,其中负载合并到服务器端数据。由于这一原因,在这一小节,我将 Ajax 添加到 <h5:drop> 组件,以便在放置发生时,该组件可以自动发起 Ajax 调用,并随 Ajax 调用发送负载。
每次当用户在右边菜单上放置一个链接时,<h5:drop> 组件发起一次 Ajax 调用,将链接添加到在服务器端保存的应用程序链接列表。当 Ajax 调用返回时,JSF 更新放置目标来反映最新添加的链接。
清单 7 显示更新的放置目标:
清单 7. 放置目标,Take II1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| <ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:h5="http://java.sun.com/jsf/composite/html5">
<script>
function dragenter(event) { /* Implement as desired */ }
function dragleave(event) { /* Implement as desired */ }
function dragover(event) { /* Implement as desired */ }
function drop(event) { /* Implement as desired */ }
</script>
<h5:drop id="dropzone" payload="#{dragDrop.payload}"
render="@this"
ondragover="dragover(event)"
ondragenter="dragenter(event)"
ondragleave="dragleave(event)"
ondrop="drop(event)">
<div class="welcomeImage">
<h:graphicImage id="welcomeImage"
library="images" name="cloudy.gif"/>
</div>
<br />
<div class="savedItems">
<ui:repeat value="#{rssFeed.savedItems}" var="item">
<div class="savedLink">
<a href="#{item.link}">
#{ fn:substring(item.title, 0, 25) } ...
</a>
<br/>
</div>
</ui:repeat>
</div>
</h5:drop>
</ui:composition>
|
在放置目标的这个版本中,我将拖放负载连接到一个 bean 属性:#{dragDrop.payload}。并且我通知放置目标显示 @this — 是指放置目标本身 — 当放置初始化的 Ajax 调用返回时。
清单 8 显示更新的 <h5:drop> 组件实现:
清单 8. <h5:drop> 组件,Take II1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="ondragenter"/>
<composite:attribute name="ondragover"/>
<composite:attribute name="ondragleave"/>
<composite:attribute name="ondrop"/>
<composite:attribute name="render"/>
<composite:attribute name="payload"/>
<composite:attribute name="payloadType"/>
</composite:interface>
<composite:implementation>
<h utputScript library="javax.faces" name="jsf.js" target="head" />
<h utputScript library="html5" name="drop.js" target="head" />
<div id="#{cc.id}" ondragenter="#{cc.attrs.ondragenter}"
ondrop="#{cc.attrs.ondrop}"
ondragover="#{cc.attrs.ondragover}"
ondragleave="#{cc.attrs.ondragleave}">
<composite:insertChildren />
<h:form id="form">
<h:inputText id="payload"
value="#{cc.attrs.payload}"
style="display: none"/>
</h:form>
</div>
<script> html5.jsf.init("#{cc.id}",
"#{cc.attrs.payloadType}",
"#{cc.attrs.render}"); </script>
</composite:implementation>
</html>
|
更新的放置目标需要:
- 当放置发生时,发起一个 Ajax 调用
- 在 Ajax 调用过程中在服务器端提供负载
在 <h5:drop> 组件 JavaScript 中进行 Ajax 调用,如清单 9 所示,使用 JSF 的 jsf.ajax.request() JavaScript 函数:
清单 9. <h5:drop> 组件的 JavaScript,Take II1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| if (!html5)
var html5 = {}
if (!html5.jsf) {
html5.jsf = {
init : function(ccid, payloadType, renderIds) {
var dropzone = $(ccid);
dropzone.payloadInput = $(ccid + ":form:payload");
dropzone.addEventListener("drop", function(event) {
if (payloadType == "")
payloadType = "text";
if (renderIds == "" || renderIds == "@this")
renderIds = ccid;
dropzone.payloadInput.value = event.dataTransfer
.getData(payloadType);jsf.ajax.request(dropzone.payloadInput, event, {
render : renderIds,
onevent : function(data) {
if (data.status == "success")
html5.jsf.init(ccid, payloadType, renderIds);
}
});
}, false);
dropzone.addEventListener("dragenter", function(event) {
event.preventDefault();
}, false);
dropzone.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
}
};
}
|
重新调用 中的负载名称:
1
| <h5:drop...payload="#{dragDrop.payload}">
|
在 中,我通过 <h5:drop> 组件的不可见输入文本 使负载可供使用。为了将负载传送到客户端,我需要一个在客户端和服务端来回传送数值的方法,而 JSF 已经在 <h:inputText> 中提供了。因此,我将一个不可见输入文本添加到放置目标,正如您在 中所看到的那样,当生成一个放置时,在 Ajax 请求之前设置不可见输入的值。
因为我在发起 Ajax 调用之前设置了输入值,现在存储在输入值中的拖放负载在 Ajax 调用期间将在服务器端可用。因为输入值是连接到一个 bean 属性的,在 Ajax 调用期间 JSF 将传递负载到那个属性的 setter 方法中。
在 中,该值被 <h5:drop> 组件使用,作为不可见文本输入的值:
1
| <h:inputText id="payload" value="#{cc.attrs.payload}" style="display: none"/>
|
在 中,不可见文本输入被指定为 Ajax 请求的源:
1
| jsf.ajax.request(dropzone.payloadInput, event, {...});
|
由于不可见文本输入被指定为 Ajax 请求的源,JSF 在服务器端处理输入,这意味着它将调用与属性 setter 方法相关的文本输入 — 在本例中是 DragDrop.setPayload(),如清单 10 所示:
清单 10. payload 属性的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| package com.clarity;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.StringTokenizer;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.gnu.stealthp.rsslib.RSSItem;
@Named
@SessionScoped
public class DragDrop implements Serializable {
@Inject private RSSFeed rssFeed;
public DragDrop() {
}
public String getPayload() {
// JSF requires both getters and setters for input properties
return "";
}
public void setPayload(String payload) {
// creates a new saved item, based on the payload. Payload
// was set in the drop event listener for the h5:drop component
// in /sections/feeds/menuLeft.xhtml
StringTokenizer st = new StringTokenizer(payload);
RSSItem item = new RSSItem();
item.setTitle(st.nextToken("|"));
st.nextToken(" ");
item.setLink(st.nextToken(" "));
rssFeed.getSavedItems().add(item);
}
}
|
JSF 传递拖放负载到 DragDrop.setPayload()。基于该负载,DragDrop.setPayload() 方法创建一个新的 RSS 项,并将其添加到保存的 rssFeed 项清单中。注意,我还为负载属性包含一个 getter 方法,因为 JSF 需要 setter 和 getter 两个来处理输入值。
使用复合组件重用现有组件功能很简单 — 在本例中是 <h:inputText> 在服务器端与客户端传送数据的功能。我在 <h5:drop> 组件中使用不可见文本输入将数据从客户端传送到服务器端,只需要将文本输入添加到复合组件,然后在 Ajax 调用之前设置输入值。
现在我已经有了一个相对成熟的放置目标了(注意拖拉源从引入到现在没有改变),进行 Ajax 调用来响应放置,然后将转换的数据从客户端传递到服务器端。这也允许页面创建者在 Ajax 调用返回时指定他们想要显示的组件。但是我的拖放组件仍然缺乏一个特性:有条件拖拉。 |