现在来看看63. XPages自定义控件(三)高级搜索之一中提到的RecordView自定义控件的代码。从控件的设计视图自上而下可以看到用于条件查询的控件、一个向用户反馈操作消息的Message自定义控件、常见的视图操作的几个按钮和一个XPages自带的视图控件。
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core" style="display:inline; "> <xp:inputText id="Users" style="float:none;width:auto"> <xp:typeAhead mode="full" minChars="1" ignoreCase="true" id="typeAhead1"> <xp:this.valueList><![CDATA[${app.usersOptions}]]></xp:this.valueList> </xp:typeAhead> </xp:inputText> </xp:view>
辅助输入的值列表用表达式语言(expression language)设置,来自一个类名为starrow.xsp.App的managed bean的getUsersOptions()方法:
public List getUsersOptions() throws NotesException{ Vector result=new Vector(); try { //获得HR数据库的路径 String path = getHRDBPath(); //XSPUtil是笔者编写的一个包含很多常用方法的工具类。 Database db=XSPUtil.getDatabase(path); result=db.getView("bySCRO").getColumnValues(1); } catch (NotesException e) { if (e.id==NotesError.NOTES_ERR_DBNOACCESS){ XSPUtil.feedback(e.text); }else{ throw e; } } return result; }起始日期使用了两个XPages日期控件。其后的三个按钮,一个是用于测试,读取和显示从InputUser控件获得的用户名,另两个分别用于搜索和清除结果,XPage的代码片断如下:
<xp:button value="Search" id="btnSearch"> <xp:eventHandler event="onclick" action="#{app.search}" submit="true" refreshMode="complete"> </xp:eventHandler> </xp:button> <xp:button value="Clear" id="button1"> <xp:eventHandler event="onclick" action="#{app.clearSearch}" submit="true" refreshMode="complete"> </xp:eventHandler> </xp:button>可以看到两个按钮的onclick事件的响应程序也都是用表达式语言绑定到同一个managed bean里,不同的是这里采用的是表示动态计算的#符号而不是上面的InputUser控件用到的设定页面载入时记算的$符号,这并不是笔者的有意选择,而是因为如果选择更高效的页面载入时计算,XPages会报错。
public void search(){ //获得XPages视图控件 XspViewPanel viewPanel1 = (XspViewPanel) JSFUtil.findComponent("viewPanel1"); //获得视图控件绑定的的数据源 DominoViewData vd=(DominoViewData) viewPanel1.getData(); //为方便构造查询条件,创建一个字符串的Collection Collection<String> filters=new Vector<String>(); String filter=""; //获得RecordView自定义控件 UIIncludeComposite cc=(UIIncludeComposite) JSFUtil.findComponent("recordView1"); //cc.getPropertyMap().getString("filter") //com.ibm.xsp.FacesExceptionEx: Null value for composite component property filter //如果设置了该控件的filter属性,则将属性值添加到查询条件里。 if (cc.getPropertyMap().get("filter")!=null){ filters.add("("+cc.getPropertyMap().get("filter")+")"); } //获得输入起始时间的编辑框 XspInputText inpFrom=(XspInputText) JSFUtil.findComponent("inpFrom"); //获得起始时间值,格式化,并用它构造出用于全文搜索的语句 if (inpFrom.getValue()!=null && !inpFrom.getValue().equals("")){ SimpleDateFormat df=new SimpleDateFormat("MM/dd/yy"); Date from=(Date) inpFrom.getValue(); filters.add("([AccessTime]>="+df.format(from)+")"); } //类似地处理结束时间 XspInputText inpTo=(XspInputText) JSFUtil.findComponent("inpTo"); if (inpTo.getValue()!=null && !inpTo.getValue().equals("")){ SimpleDateFormat df=new SimpleDateFormat("MM/dd/yy"); Date to=(Date) inpTo.getValue(); filters.add("([AccessTime]<="+df.format(to)+")"); } //获得输入用户的编辑框,构造出查询语句 XspInputText inpUser=(XspInputText) JSFUtil.findComponent("Users"); if (!inpUser.getValue().equals("")){ filters.add("([StaffName]="+inpUser.getValueAsString()+")"); } //合并出最终的查询语句,传递给Message自定义控件 filter=starrow.Commons.join(filters, " & "); XSPUtil.getRequestMap().put("message", filter); //设置数据源的搜索条件,即搜索。 vd.setSearch(filter); } public void clearSearch(){ //清空各个输入查询条件的编辑框 ((XspInputText) JSFUtil.findComponent("inpFrom")).setValue(""); ((XspInputText) JSFUtil.findComponent("inpTo")).setValue(""); ((XspInputText) JSFUtil.findComponent("Users")).setValue(""); //调用搜索方法 this.search(); }
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core"> <xp:text escape="true" id="message" value="#{requestScope.message}" style="background-color:rgb(255,255,0)"> </xp:text> </xp:view>反馈信息的格式,如字体、颜色和背景等都可以在控件里预先设置,整个应用程序里各处的反馈信息自动具有了统一的风格。
<xp:button value="Refresh" id="button5"> <xp:eventHandler event="onclick" submit="true" refreshMode="complete"> <xp:this.action><![CDATA[#{javascript:database.updateFTIndex(false);}]]></xp:this.action> </xp:eventHandler> </xp:button>新建按钮使用浏览器端JavaScript打开一个新页面。
<xp:button value="New" id="button3" rendered="true"> <xp:eventHandler event="onclick" submit="false"> <xp:this.script><![CDATA[window.open("record.xsp", "_blank");]]></xp:this.script> </xp:eventHandler> </xp:button>删除按钮为了简单直接调用了XPages提供的删除文档的简单操作(simple action)。
<xp:button value="Delete" id="button4"> <xp:eventHandler event="onclick" submit="true" refreshMode="complete"> <xp:this.action> <xp:deleteSelectedDocuments view="viewPanel1" message="Are you sure to delete the selected records?"> </xp:deleteSelectedDocuments> </xp:this.action> </xp:eventHandler> </xp:button>
<xp:viewPanel rows="20" id="viewPanel1" viewStyle="width:99%" pageName="/record.xsp"> <xp:this.facets> <xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="headerPager" id="pager1"> </xp:pager> <xp:pager partialRefresh="true" layout="Previous Group Next" xp:key="footerPager" id="pager2"> </xp:pager> </xp:this.facets> <xp:this.data> <xp:dominoView var="view1" viewName="${javascript:compositeData.notesView;}" search="#{javascript:compositeData.filter;}" sortOrder="descending" sortColumn="$8"> </xp:dominoView> </xp:this.data> <xp:viewColumn id="viewColumn8" columnName="$8" showCheckbox="${javascript:compositeData.showEdit}"> <xp:this.displayAs><![CDATA[${javascript:if (compositeData.showEdit) return "link";}]]></xp:this.displayAs> <xp:this.facets> <xp:viewColumnHeader xp:key="header" id="viewColumnHeader8" value="Date"> </xp:viewColumnHeader> </xp:this.facets> <xp:this.converter> <xp:convertDateTime type="date"></xp:convertDateTime> </xp:this.converter> </xp:viewColumn> <xp:viewColumn columnName="StaffName" id="viewColumn4"> <xp:viewColumnHeader value="Staff Name" id="viewColumnHeader4"> </xp:viewColumnHeader> </xp:viewColumn> <xp:viewColumn columnName="AccessTime" id="viewColumn1"> <xp:this.converter> <xp:convertDateTime type="time"></xp:convertDateTime> </xp:this.converter> <xp:viewColumnHeader value="Access Time" id="viewColumnHeader1"> </xp:viewColumnHeader> </xp:viewColumn> <xp:viewColumn id="viewColumn9" columnName="Type"> <xp:this.facets> <xp:viewColumnHeader xp:key="header" id="viewColumnHeader9" value="Type"> </xp:viewColumnHeader> </xp:this.facets> </xp:viewColumn> <xp:viewColumn columnName="CardNo" id="viewColumn2" rendered="false"> <xp:viewColumnHeader value="Card Number" id="viewColumnHeader2"> </xp:viewColumnHeader> </xp:viewColumn> <xp:viewColumn columnName="StaffNo" id="viewColumn3" rendered="false"> <xp:viewColumnHeader value="Staff Number" id="viewColumnHeader3"> </xp:viewColumnHeader> </xp:viewColumn> <xp:viewColumn columnName="Department" id="viewColumn5"> <xp:viewColumnHeader value="Department" id="viewColumnHeader5"> </xp:viewColumnHeader> </xp:viewColumn> <xp:viewColumn columnName="Office" id="viewColumn7"> <xp:viewColumnHeader value="Office" id="viewColumnHeader7"> </xp:viewColumnHeader> </xp:viewColumn> </xp:viewPanel>
RecordView控件的自定义属性的设计视图
比如在显示考勤记录的页面里,关于RecordView控件的代码如下:
<xc:RecordView id="recordView1" notesView="vwRecord" showSearch="${javascript:app.isAdmin()}"> <xc:this.filter><![CDATA[${javascript:var filter="[Type] is present"; if (!app.isAdmin()){ filter+=" & [StaffName]=" + context.getUser().getCommonName(); } return filter;}]]></xc:this.filter> </xc:RecordView>
在显示补卡记录的页面里,传递给RecordView控件的属性值就有了变化:
<xc:RecordView id="recordView1" notesView="vwRecord" showSearch="${javascript:false;//app.isAdmin()}" showLink="true" showCheckbox="true" showEdit="${javascript:app.isAdmin()}"> <xc:this.filter><![CDATA[${javascript:var filter="[MakeUp]=1"; if (!app.isAdmin()){ filter+=" & [StaffName]=" + context.getUser().getCommonName(); } return filter;}]]></xc:this.filter> </xc:RecordView>app.isAdmin()是managedbean里一个判断当前用户是否管理员的方法,如果是则显示所有员工的信息,否则只显示当前用户自己的信息。
RecordView控件的各个组成部分都已介绍完毕,下一篇文章会给出它们的完整代码,并介绍使用这个自定义控件遇上的问题和反映出来的XPages的缺陷。