top
Loading...
深入WebLogicPortalUI編程
提要 本文重點討論如何利用BEA WebLogic portal所提供的新功能實現UI(titlebar)的定制開發。

一、 引言

也許,沒有什么比發行你的第一個portal產品更讓你激動的了。在經過長時間的努力后,你(和你的團隊)所傾力開發的應用程序終于與用戶見面了!然而,本文不是教給你如何進行基本的portal開發,而是探討如何為你的現有portal加入新的功能。

本文將描述有關BEA WebLogic portal的一些非常有用的新特征-你可以借助這些特征來擴展你的portal頁面功能。

二、 定制Titlebar

如果你已經使用過所有內置的titlebar功能,那么你可以很容易地實現titlebar的定制--你僅需要把一些新圖標放到其上即可。首先,讓我們看一下BEA為portal開發在titlebar所提供的內置功能(分別對應四個圖標):

· Minimize:如果頁面上有相當多的portlet以至于用戶想最小化其中的某些以便看清其它portlet,那么在設計初期可能把它們分解成獨立的頁面更好些。

· Maximize:這在使用一種多列布局時非常有用,盡管通常情況下,許多用戶更喜歡單列布局。

· Delete:如果不進行編程,那么這個圖標不會作為用戶設置數據的一部分而存儲起來;因此這有可能惹惱你的用戶-每次必須返回。更不用提,如果他們真正想要返回的話,他們不得不開始一個新的會話。

· Help:這項功能幾乎是必須的。

因此,對大多數用戶來說,在titlebar中只放置一個圖標就夠了。然而,下面我們將詳細討論如何進一步定制titlebar-再添加兩個用戶常用圖標。

三、 加入打印圖標

我們知道,現在已經到了無紙辦公時代。盡管如此,portal用戶仍會使用打印圖標。而實際上,除了昂貴的報告和旅行計劃外,他們真正想要打印的其實只是該頁面的一部分:單個的portlet。作為開發者,只要用戶需要,我們就應該盡最大努力滿足他們的要求。因此,下面我將向你介紹怎樣打印單個的portlet。

注意,本文中的例程實現與命名慣例有一定聯系。但是,我們所使用的命名不會影響真正的實現機制。首先,讓我們來看一下window.jsp文件中的下面一行代碼:

<render:writeAttribute name="id"
value="<%= window.getPresentationId() %>"/>

在此,你僅需要把ID值改變為window.getDefinitionLabel(),因為它可由所有版本的portal從任何存在Window上下文的地方進行存取。

現在,既然你已經有一個該portlet的句柄,那么你可以創建一個能夠打開一個可打印頁面的小腳本。

function printPortlet(appPath, portletId, portletName){
var pageString = appPath+'/resources/jsp/
printPage.jsp?portletId='+portletId;
var printWindow = window.open(pageString, 'PrintPage',"location=no,scrollbars=no,resizable");
printWindow.focus();
}

上面的腳本應該駐留在一個.js文件中,這樣在每個頁面上就可以只加載單個副本。

然后,打開titlebar.jsp并且找到下面內容:

<td class="bea-portal-window-titlebar-buttons"
nowrap="nowrap">

在這個空的td(填充了BEA圖標)中,放入你的打印圖標和一個對你的腳本的調用。為了實現可移植性,你要動態地傳入參數:

<img src="<render:getSkinPath
imageName="printerIcon.gif" />"
style="cursor:pointer; position:relative; top:-2px"
onclick="printPortlet('<%=request.getContextPath()%>',
'<%=window.getDefinitionLabel()%>')">

這樣就可以使你的新圖標出現在你的用戶titlebar中:


最后,你需要創建你自己的打印頁面以使該圖標做一些有用的事情。因為該頁面不是portal上下文的一部分,所以你需要硬編碼你的樣式參考(style reference)。另外,由于在標準框架中的嵌套DIV的繼承特點,所以你需要一些正式生成的HTML,后面再另上你的腳本生成和打印該portlet的部分代碼:

<%@ page language="java"
contentType="text/html;charset=UTF-8"%>
<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>
<netui:html>
<head>
<title>
Portlet Print Page
</title>
<meta name="bea-portal-meta-skin"
content="/framework/skins/default"/>
<meta name="bea-portal-meta-skin-images"
content="/framework/skins/default/images"/>
<link href="/snelsondemo/framework/skins/default/
css/body.css"
rel="stylesheet" type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
css/button.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
alert/css/window-alert.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
css/window.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
plain/css/window-plain.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
css/portlet.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
css/book.css"
rel="stylesheet"/>
<link href="/snelsondemo/framework/skins/default/
css/layout.css"
rel="stylesheet"
type="text/css"/>
<link href="/snelsondemo/framework/skins/default/
css/form.css"
rel="stylesheet"
type="text/css"/>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
menu.js"></script>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
util.js"></script>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
delete.js"></script>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
float.js"></script>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
menufx.js"></script>
<script type="text/javascript"
src="/snelsondemo/framework/skins/default/js/
skin.js"></script>
<style type="text/css">
.bea-portal-window-titlebar-title{font-weight:bold}
</style>
</head>
<body class="bea-portal-body">
<div class="bea-portal-book-primary">
<div class="bea-portal-book-primary-content">
<div class="bea-portal-book-primary-page"
style="margin-right:10px">
<div id="portletHtml"
class="bea-portal-window">
</div>
</div>
</div>
</div>
<script language="JavaScript">
var portletId = '<%=request.getParameter("portletId")%>';
document.getElementById('portletHtml').innerHTML =
self.opener.document.getElementById(portletId).
innerHTML;
window.print();
</script>
</body>
</netui:html>

閱讀完上面的標注和代碼后,你就會看到,我們已定義的標簽現在可用于填充我們的彈出菜單-通過僅使用portlet實現,從而使該頁面的其它空間用于實現打印,如下圖所示:


當然,你還可以進一步修改這個打印頁面,例如停用鏈接或把頁面饋送到一個隱蔽的iFrame并且由用戶調用打印功能,等等。

四、 加入速算表功能

與打印密切相關的另外一項功能-也是商業用戶最經常使用的一項功能,是操作表格數據。當然,在他們登錄到你的portal的同時,你可以通過實現一些AJAX技巧以允許他們以不同方式來觀看數字,而且很可能你應該這樣做。但是,當用戶想取走你的數據并想在飛機上操作它們時,情況會如何呢?通過對整個portal加以緩沖而使他們陷于困境,或者提示他們如何把數據復制到一個速算表(除非你有一項巨大的技術支持預算)中嗎?最好不要這樣。那么,你為什么不按他們想要的方式把數據給他們呢?

我們的實現思想是,首先通過標簽定義得到我們的portlet的一個句柄,然后用它構建一個速算表。

注意,現在你想要在自己的頁面上創建一個隱藏的iFrame來接收頁面(因為其結果是一個文件,而不是一個頁面)。為此,你只需把下面內容添加到footer.jsp中:

<iframe id="scriptRender"
src=""
style="visibility:hidden;
height:0px; padding:0px;
margin:0px"></iframe>

然后,你要在你的jsp文件中建立一個類似于打印函數的新函數。不過這個新函數作了一些改變以隱藏被調用的頁面并傳遞表格數據:

function excelPortlet(appPath, tableId, portletName){
var tableData = document.getElementById(tableId).outerHTML;
var pageString =appPath+'/resources/jsp/excelPage.jsp?tableData='+tableData+'&portletName='+portletName;
document.getElementById('scriptRender').src = pageString;
}

接下來,你要使用與前面操作打印圖標類似的方式來把你的圖標添加到工具欄上-當然現在你必須隱藏該圖標(直到你想使用它并且參考你的表格而不是整個的portlet為止)。請參考如下代碼:

<img src="<render:getSkinPath
imageName="excelIcon.gif" />"
id="<%=window.getDefinitionLabel()%>.excelIcon"
style="cursor:pointer;
position:relative; top:-2px;
visibility:hidden"
onclick= "excelPortlet('<%=request.getContextPath()%>',
'<%=window.getDefinitionLabel()%>.
excelTable', '<%= title %>')">

當然,為了使用該圖標,你首先需要取消隱藏它。為此,你可以在portlet JSP本身實現-通過添加一個到Window上下文的參考,然后再調用你的標簽定義的句柄,象下面這樣:

<%@ page import="com.bea.netuix.servlets.controls.window.
WindowPresentationContext"%>
<%
WindowPresentationContext window =WindowPresentationContext.
getWindowPresentationContext(request);
%>
<script language="javascript">
document.getElementById('<%=window.getDefinitionLabel()%>.excelIcon')
.style.visibility = 'visible';
</script>

當然,你還要編寫一個簡短的JSP來生成你的速算表:

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%
String tableData = request.getParameter("tableData");
response.reset();
response.setContentType("application/xls");
response.setHeader("Content-Disposition","attachment;filename=" +request.getParameter("portletName")
+".xls");
%>
<%=tableData%>

在隱藏的iFrame和響應變化這一時間段內,用戶只能看到下載部分,而不會看到屏幕后臺的邏輯實現:

 

如果你在表格的標題行上使用了<th>標簽,那么Excel將把它設置為一個標題行以便于可能的進一步排序。


如果你在一個portlet中有多張表,你可以使用相同的參考ID來打開該圖標從而動態地設置腳本調用。

五、更新日期數據

現在,我們要實現更新日期的問題-向titlebar中添加portlet最后被更新的日期信息。當然,你可以把此信息放到portlet本身的設計中去;但是,通過這種方式將會節省屏幕空間。

因為BEA沒有事先考慮到這一點,所以你需要簡單地修改一下titelbar.jsp-把聲明移到生成上下文的外面即可。然后,找到聲明WindowPresentationContext的scriptlet部分并且把這一部分移動到<render:beginRender>標簽的上面。現在,既然你能夠從render范圍的外部存取window對象,那么,讓我們把下列語句添加到</tr>后面:

<tr><td colspan="3"
id="<%=window.getDefinitionLabel()%>.dateCell"></td></tr>

現在,讓我們看一下所有你需要添加到你的portlet上的代碼:

<% String myDate = "As of March 1, 2006"; %>
document.getElementById('<%=window.getDefinitionLabel()%>
.dateCell').innerHTML = '<%=myDate%>';

最后,你會得到如下一幅圖像:


六、增加水平和垂直導航功能

加入了上面這些特征之后,你的portal將會更為流行,而且用戶可能會要求增加更多的portlet。更多portlet將導致更多的頁面(這樣會使得頁面空間非常擁擠,更不用提速度的問題了)。如果頁面數繼續增長,你的portal將最后變得很難在超出屏幕范圍的組件(更不用提令用戶惱火的向旁邊卷動的組件了,見下圖)之間進行導航。


標準的單級導航方式可以卷動,但是頁面導航中的其它行將占居屏幕上方的空間。另外,你可以使用多級菜單方案,但是這將使用戶為找到其目標增加更多的鼠標移動(盡管在你的站點有大量的頁面時,這種情況也是難以避免的)。

要解決上面難堪的導航問題有一個非常酷的技巧:除了原有的水平導航選項卡外,你可以再增加一種垂直導航。這樣可以把你的可視化設計和頁面數目擴展許多--幾乎加倍原來單個頁面的存取空間。

實現這種雙向導航方式的關鍵是使用隱藏的頁面設置。


標準的單級菜單代碼象下面這樣跳過隱藏的頁面:

if (!pageCtx.isHidden() && pageCtx.isVisible())

你可以在單級菜單代碼的后面為你的垂直菜單添加一個DIV區,然后使用相反的檢查來運行相同的循環:

if (pageCtx.isHidden())

我去除了對isVisible的檢查--它是冗余的。

現在,通過使用一些簡單的CSS技巧,你就可以把這個菜單放到你想要放置的任何地方。在本文示例中,我選擇把它放到右邊:

<div id="verticalmenu"
style="position:absolute;
top:40px;
right:20px;
width:20px;">


至此,你用了不到10分鐘實現了該頁面的重新調用。

如果已經隱蔽了頁面,那么就可以檢查并使用你新加入的圖標了。盡管可以實現這種類型的導航,但是我更喜歡使用圖標方式。在我實現的portal中,選擇水平的還是垂直的導航鏈接是由相應的頁面類型決定的。例如,在一個主要面對商業信息的portal中,經常存在一些與主要內容不相關的頁面(例如事件日歷和業界新聞),但是對你的主要用戶來說可能仍然具有一定參考價值。這些"工具"頁面會把他們自然地導向其它不同的導航模型--它們獨立于portal的中心內容。

在BEA WebLogic Portal service pack 4之前,在API中存在一處不太合適的地方-pageCtx.isHidden()從不返回true,因為該框架能夠從頁面列表中提取隱藏的頁面(在把該隱藏頁面返回到預定的上下文之前)。不過,你仍然能夠使用隱藏的頁面,但是你必須硬編碼它們的值,而不是簡單地通過增加更多隱藏的頁面來動態地擴展你的垂直導航。因此,為了確保你的硬編碼具有可移植性,請確保對鏈接使用了render標簽,如下所示:

<a href="<render:pageUrl pageLabel=" hackinguiNews"/>">News</a>

總之,就算你的用戶對你第一次發布你的portal感到非常高興,甚至于從未要求過什么;但是,現在你可以通過僅付出一點點努力而帶給他們一些額外的驚喜了。
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗