我有一个显示视图的重复控件。我现在想包含一个复选框,以便我可以对选定的文档执行操作。我添加复选框没有问题,但我如何检查复选框是否被选中然后获取该行的关联文档?
我的想法是:
让复选框更改事件从数组范围变量中添加或删除文档的 UNID。然后只需对该数组中的文档执行操作。
忘记复选框,只需弹出一个列表框,以允许用户从中进行选择。
但是有没有更简单的方法?
我有一个显示视图的重复控件。我现在想包含一个复选框,以便我可以对选定的文档执行操作。我添加复选框没有问题,但我如何检查复选框是否被选中然后获取该行的关联文档?
我的想法是:
让复选框更改事件从数组范围变量中添加或删除文档的 UNID。然后只需对该数组中的文档执行操作。
忘记复选框,只需弹出一个列表框,以允许用户从中进行选择。
但是有没有更简单的方法?
为了获得最大的灵活性,最好不要将我们的用户界面组件直接绑定到数据;相反,如果我们引入一个中间的“数据模型”层(通常是描述数据所代表的真实世界对象/人/过程的层,而不是在“文档”中思考,这最终只是证明这些真实-世界万物存在),我们的 UI 代码变得非常干净、易于理解、易于维护。当我们继续在文档中思考时,它还可以更容易地引入那些难以实现的功能。
例如,假设我们使用扩展库中的对象数据源来创建一个任意对象(例如,让我们调用它pendingRequests
),我们稍后可以将我们的重复控件绑定到(而不是将其直接绑定到视图):
// Create an empty array to return at the end:
var results = [];
// Create a view navigator instance for iterating the view contents:
var pendingView = database.getView("pendingRequests");
var entryNavigator = pendingView.createViewNav();
var eachEntry = entryNavigator.getFirst();
while (eachEntry != null) {
// Add metadata about each entry to result array:
var metaData = eachEntry.getColumnValues();
results.push({
startDate: metaData.get(0).getDateOnly(),
endDate: metaData.get(1).getDateOnly(),
employeeName: metaData.get(2),
status: metaData.get(3),
unid: eachEntry.getUniversalID(),
selected: "0"
});
// In case any column values were Domino objects:
recycleAll(metaData);
// Cruise on to the next:
eachEntry = navigateToNext(entryNavigator, eachEntry);
}
// Final Domino handle cleanup:
recycleAll(entryNavigator, pendingView);
// Return our now populated array:
return results;
在继续之前,我应该指出,上面的示例包括两个不是平台原生的语法糖果:recycleAll()
和navigateToNext()
. 这两个只是使愚蠢的recycle
东西更容易处理的实用功能:
回收所有
* More convenient recycling
*/
function recycleAll() {
for(var i = 0; i < arguments.length; i++) {
var eachObject = arguments[i];
// assume this is a collection
try {
var iterator = eachObject.iterator();
while (iterator.hasNext()) {
recycleAll(iterator.next());
}
} catch (collectionException) {
try {
eachObject.recycle();
} catch (recycleException) {
}
}
}
}
导航到下一个
/*
* Safe way to navigate view entries
*/
function navigateToNext(navigator, currentEntry) {
var nextEntry = null;
try {
nextEntry = navigator.getNext(currentEntry);
} catch (e) {
} finally {
recycleAll(currentEntry);
}
return nextEntry;
}
好的,现在回到数据模型......具体来说,这个块:
var metaData = eachEntry.getColumnValues();
results.push({
startDate: metaData.get(0).getDateOnly(),
endDate: metaData.get(1).getDateOnly(),
employeeName: metaData.get(2),
status: metaData.get(3),
unid: eachEntry.getUniversalID(),
selected: "0"
});
因此,对于每个视图条目,我们创建一个非常简单的对象,其中包含我们希望允许用户与之交互的所有相关信息,以及两个额外的属性,以便我们自己的代码方便unid
:如果我们需要,文档和selected
,它为我们提供了一种将复选框绑定到此元数据对象的属性的方法......这意味着用户可以通过复选框切换其值。
下面是一个基本示例,说明我们如何将这些数据呈现给用户:
<ul style="list-style-type: none;">
<xp:repeat var="vacationRequest" value="#{pendingRequests}">
<li style="margin-bottom:10px;">
<strong>
<xp:checkBox value="#{vacationRequest.selected}" text="#{vacationRequest.startDate} - #{vacationRequest.endDate}"
checkedValue="1" uncheckedValue="0" />
</strong>
<xp:text value="#{vacationRequest.employeeName} (#{vacationRequest.status})" tagName="div" />
</li>
</xp:repeat>
</ul>
重复控件中的每个复选框现在都直接绑定到selected
每个“行”表示的元数据对象的属性......它也有一个unid
属性,因此对与此数据模型对应的实际文档进行操作很简单:
for (var i = 0; i < pendingRequests.length; i++) {
var eachRequest = pendingRequests[i];
if (eachRequest.selected == "1") {
var requestDataSource = database.getDocumentByUNID(eachRequest.unid);
requestDataSource.replaceItemValue("status", "Approved");
if (requestDataSource.save()) {
// update in-memory metadata:
eachRequest.status = "Approved";
}
}
}
由于我们的数据源只是这些元数据对象的数组,我们可以遍历每个对象,询问用户是否切换了selected
每个对象的属性,如果是,则获取其相应文档的句柄,修改一个或多个项目,并保存它。注意:因为我们在这个例子中使用了一个数据源,所以它不会在每个事件上重新加载后端视图数据。出于性能原因,这是一件非常好的事情(tm)。但这确实意味着我们必须更新内存中的元数据对象以匹配我们在文档上所做的更改(即eachRequest.status = "Approved"
)......但这也意味着我们只能更新它,而不必废弃我们的整个数据源并拥有它从视图中读取所有内容。
作为奖励,添加类似“全选”按钮的内容更加简单:
for (var i = 0; i < pendingRequests.length; i++) {
pendingRequests[i].selected = "1";
}
所以,总而言之,我们有一个内存数据模型,在许多情况下,等效操作将执行得更快,但也允许我们编写更少的代码——更易读的代码——来做更有趣的事情。
如果您想实时使用此模式(和/或下载所有上述源代码),我已将其保存在这里。
布鲁斯,我做了一个关于从重复控件中选择文档的视频。我没有使用复选框,而是使用了添加和删除按钮,然后做了一些 CSS 来突出显示选定的文档。我确信复选框可以与添加/删除按钮中的代码基本相同。
基本上,我在内存中创建了一个 java.util.ArrayList 来保存unid,然后在单击重复行时填充该数组。我计算了每一行的 CSS,如果该 UNID 存在于数组中,我将更改背景颜色以显示它是“选定的”。我实际上并没有显示对所选 unid 的任何处理,但由于该数组位于范围内存中,因此您几乎可以用它做任何您想做的事情。无论如何,视频演示在这里:
http://notesin9.com/index.php/2011/04/01/notesin9-025-selecting-documents-from-a-repeat-control/
与往常一样,Tim 的答案是您长期健康、理智和代码可维护性的最佳选择。
在我开始将 Java 用于所有后端逻辑之前,我过去还采取了另一条路线。您可以创建一个包含 HashMap 的页面加载绑定 dataContext,然后将每个复选框绑定到该复选框 - 它将为每个键填写 true 或 false,因此您可以循环遍历映射条目并找到为 true 的条目,这是检查的值。
我整理了一个快速示例,从名称数据库中提取列表来说明我的意思:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoView var="names" databaseName="names.nsf" viewName="$NamesFieldLookup"/>
</xp:this.data>
<xp:this.dataContexts>
<xp:dataContext var="checkedNames" value="${javascript: new java.util.HashMap() }"/>
</xp:this.dataContexts>
<xp:div id="refresher">
<xp:repeat value="#{names}" var="name" rows="3">
<xp:this.facets>
<xp:pager xp:key="header" id="pager1" layout="Previous Group Next" />
</xp:this.facets>
<div>
<xp:checkBox value="#{checkedNames[name.$9]}">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="refresher"/>
</xp:checkBox>
<xp:text value="#{name.$9}"/>
</div>
</xp:repeat>
<p><xp:text value="#{checkedNames}"/></p>
</xp:div>
</xp:view>