为了获得最大的灵活性,最好不要将我们的用户界面组件直接绑定到数据上; 相反,如果我们引入一个中间的"数据模型"层(通常是描述数据代表的真实世界对象/人/过程的层,而不是在"文档"中思考,这最终只是证明这些真实的 - 世界存在的东西),我们的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;
Run Code Online (Sandbox Code Playgroud)
在继续之前,我应该指出,上面的例子包括两个非平台原生的语法糖果:recycleAll()和navigateToNext().这两个都只是实用功能,使愚蠢的recycle东西更容易处理:
recycleAll
* 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) {
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
navigateToNext
/*
* 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;
}
Run Code Online (Sandbox Code Playgroud)
好的,现在回到数据模型......具体来说,这个块:
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"
});
Run Code Online (Sandbox Code Playgroud)
因此,对于每个视图条目,我们创建一个非常简单的对象,其中包含我们希望允许用户与之交互的所有相关信息,以及为我们自己的代码提供方便的两个额外属性: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>
Run Code Online (Sandbox Code Playgroud)
重复控件中的每个复选框现在直接绑定到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";
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于我们的数据源只是这些元数据对象的数组,我们可以循环遍历每个,询问用户是否切换了selected每个元素的属性,如果是,则获取其相应文档的句柄,修改一个或多个项目,并保存.注意:因为我们在此示例中使用了数据源,所以它不会在每个事件上重新加载后端视图数据.出于性能原因,这是一个非常好的事情(tm).但它确实意味着我们必须更新内存中的元数据对象,以匹配我们改变了对文件(即eachRequest.status = "Approved")...但它也意味着我们可以更新只能说,而不必放弃我们的整个数据源,并有它从视图中读回所有内容.
作为奖励,添加诸如"全选"按钮之类的东西甚至更简单:
for (var i = 0; i < pendingRequests.length; i++) {
pendingRequests[i].selected = "1";
}
Run Code Online (Sandbox Code Playgroud)
因此,总而言之,我们有一个内存数据模型,在许多情况下,等效操作将更快地执行,但也允许我们编写更少的代码 - 更可读的代码 - 来做更好的事情.
如果你想现场玩这个模式(和/或在上下文中下载所有上述源代码),我在这里永生.
| 归档时间: |
|
| 查看次数: |
1978 次 |
| 最近记录: |