This problem is driving me crazy.
I have a view of documents with categories. The docs are in an arbitrary order now but I want to provide the users an easy way to move docs within categories up or down. So I have up/down arrows within the view.
I have SSJS code behind the arrows that changes the "order" value in the docs so they will move up or down (in my pick the task field is showing the order).
My problem is that it works intermittently. I think the reason is because the order of the docs is being changed while I am in the loop. I tried setting the auto-update setting on the view to manual, but it still seems to not work.
Maybe I am making this way to hard. I really only need to change 2 docs, the one the user chose and the one that they are moving it to. Would maybe be simpler just to grab those?
function mveItm(mveDir,mveCat,curOrd) {
var curOrdStr:String = String(curOrd);
var tarOrd:Integer;
var tarOrdStr:String;
var doc:NotesDocument;
var lstOrd:Integer;
var fstOrd:Integer;
//Grab Collection In Order
var tskView = database.getView("(dbAllPCTasksAutoUpdateFalse)");
tskView.refresh();
tskView.setAutoUpdate(false);
var veCol:NotesViewEntryCollection = tskView.getAllEntriesByKey(mveCat);
//Set First and Last Order Number
var fstOrd = 10
var lstOrd = veCol.getCount() * 10;
//If the user has clicked move Higher on the last item
//or move down on the first, then ignore
if (mveDir == "Lower" && fstOrd == curOrd)
{return}
if (mveDir == "Higher" && lstOrd == curOrd)
{return}
//Move Higher
if (mveDir == "Higher") {
//Set target order
tarOrd = curOrd + 10;
tarOrdStr = String(tarOrd);
//Loop through viewEntryCollection and process
var entry:NotesViewEntry = veCol.getFirstEntry();
while (entry != null) {
doc = entry.getDocument()
//Where are we in the loop
var n:String = String(entry.getDocument().getItemValueInteger("order"));
//If the number we are on matches the number that was selected to be changed
//Change the current number to the target number
if (n == curOrdStr)
{
print ("We are in order");
print ("This is the one we are on..." + n);
print ("This is the one we passed in..." + String(curOrdStr));
print ("This is the target..." + String(tarOrd));
doc.replaceItemValue("order",tarOrd);
doc.save;
}
//If the number we are on matches the number that was the target
//Change the number we are on to the selected order number
if (n == tarOrdStr)
{
doc.replaceItemValue("order",curOrd);
doc.save;
}
var tmpentry:NotesViewEntry = veCol.getNextEntry(entry);
entry.recycle();
entry = tmpentry;
}
}
//Move down
if (mveDir == "Lower") {
//Set target order
tarOrd = curOrd - 10;
tarOrdStr = String(tarOrd);
//Loop through viewEntryCollection and add to DocumentCollection
var entry:NotesViewEntry = veCol.getFirstEntry();
while (entry != null) {
doc = entry.getDocument()
//Where are we in the loop
var n:String = String(entry.getDocument().getItemValueInteger("order"));
//If the number we are on matches the number that was selected to be changed
//Change the current number to the target number
if (n == curOrdStr)
{
//doc = entry.getDocument()
doc.replaceItemValue("order",tarOrd);
doc.save;
}
//If the number we are on matches the number that was the target
//Change the number we are on to the selected order number
if (n == tarOrdStr)
//Change the current number to the target number and change everyone after that to -10
//if (movDwn == true)
{
doc.replaceItemValue("order",curOrd);
doc.save;
}
var tmpentry:NotesViewEntry = veCol.getNextEntry(entry);
entry.recycle();
entry = tmpentry;
}
}
tskView.setAutoUpdate(true);
tskView.refresh();
//resetCategories(mveCat);
return true;
}
I am posting my full solution here. The code is at the end, I will explain what I did to solve this. Kudos to help from David and from Knut Hermann for giving me assistance - see Knut's post here.
Xpages get a handle on next rowData or Doc in a repeat
My problem was as follows: The user can enter docs with categories(my examples were "OS" and "Software". I want to them to be easily ordered by the user with arrows in the repeat control. They must also be able to select multiple docs to delete, and to add docs, and the order will always be correct. Just to make it harder I put in a filter for the category, so the user might see all categories or one (and this solution would work for more than one category as well).
The actual change of order is not too difficult. I realized that I do not have to go through all the docs in a category, just get the doc the user selected, and either the next higher or next lower, depending on what they want to do, and swap positions. That part is not too difficult (see method mveTsk in the library.
The difficult part is to determine whether or not to display up or down move arrows. Can't display a down arrow if the the item is the first one, can't display up if it was the last. Also, if a user deletes an item or two or three, then I must reorder the items.
I think this solution is a good one, and should work in many different situations.
============================
FULL CODE:
[Cannot enter all the code] Here is what I think is most importatnt
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
xmlns:xc="http://www.ibm.com/xsp/custom"
style="font-size:8pt">
<xp:this.data>
<xp:dominoView
var="view1"
viewName="(xpAllPCBuilds)" />
</xp:this.data>
<xp:this.resources>
<xp:styleSheet
href="/custom.css" />
</xp:this.resources>
<xp:this.beforePageLoad><![CDATA[#{javascript:viewScope.put("rows","5")}]]></xp:this.beforePageLoad>
<xe:widgetContainer id="widgetContainerView" style="width:99.00%">
<xp:panel>
<xp:repeat
id="repeat1"
var="rowData"
indexVar="repeatIndex"
value="#{view1}"
>
<xp:this.facets>
<xp:text
disableTheme="true"
xp:key="header"
escape="false">
<xp:this.value><![CDATA[<table class='lotusTable repeatRowColors' border='0' cellspacing='0' cellpadding='0'>
<tr class ='lotusFirst lotusSort scope='col'>
<th></th>
<th class ='lotusBold'>Employee Name</th>
<th class ='lotusBold'>Computer</th>
<th class ='lotusBold'>Create Date</th>
<th class ='lotusBold'>Create User</th>
<th class ='lotusBold'>ID</th>
</tr>
</thead>]]>
</xp:this.value>
<xp:panel id="pagerPnlTop">
<xp:table
styleClass="lotusPaging lotusPagingTop"
style="width:100%">
<xp:tr>
<xp:td styleClass="lotusLeft">
<xp:panel
themeId="Panel.left"
styleClass="xspRowCount">
 
 
 
 
 
 
 
 
 
</xp:panel>
</xp:td>
<xp:td styleClass="lotusRight">
</xp:td>
</xp:tr>
</xp:table>
</xp:panel>
</xp:text>
<xp:text
disableTheme="true"
xp:key="footer"
escape="false"
>
<xp:this.value>
<![CDATA[</table>]]></xp:this.value>
<xp:panel id="pagerPnlBottom">
<xp:table
styleClass="lotusPaging lotusPagingTop"
style="width:100%"
>
<xp:tr>
<xp:td styleClass="lotusLeft">
<xp:panel
themeId="Panel.left"
styleClass="xspRowCount"
>
<xp:label
value="Show: "
id="label1"
/>
<xp:link
text="5"
id="link2"
>
<xp:this.style>
<![CDATA[#{javascript:if (viewScope.get("rows") == 5)
{return "color:#808080;font-weight:normal;"}
else
{return "font-weight:bold"}}]]>
</xp:this.style>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<![CDATA[#{javascript:var numEntries = 5;
viewScope.rows = numEntries;
var dt = getComponent("repeat1");
if(dt != null && dt.getRowCount() > 0) {
dt.setFirst(0);
}}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
 
<xp:label
value="|"
id="label2"
themeId="Text.smallSeparator"
/>
 
<xp:link
text="10"
id="link3"
>
<xp:this.style>
<![CDATA[#{javascript:if (viewScope.get("rows") == 10)
{return "color:#808080;font-weight:normal;"}
else
{return "font-weight:bold"}}]]>
</xp:this.style>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<![CDATA[#{javascript:var numEntries = 10;
viewScope.rows = numEntries;
var dt = getComponent("repeat1");
if(dt != null && dt.getRowCount() > 0) {
dt.setFirst(0);
}}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
 
<xp:label
value="|"
id="label3"
themeId="Text.smallSeparator"
/>
 
<xp:link
text="25"
id="link4"
>
<xp:this.style>
<![CDATA[#{javascript:if (viewScope.get("rows") == 25)
{return "color:#808080;font-weight:normal;"}
else
{return "font-weight:bold"}}]]>
</xp:this.style>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<![CDATA[#{javascript:var numEntries = 25;
viewScope.rows = numEntries;
var dt = getComponent("repeat1");
if(dt != null && dt.getRowCount() > 0) {
dt.setFirst(0);
}}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
 
<xp:label
value="|"
id="label4"
themeId="Text.smallSeparator"
/>
 
<xp:link
text="50"
id="link6"
>
<xp:this.style>
<![CDATA[#{javascript:if (viewScope.get("rows") == 50)
{return "color:#808080;font-weight:normal;"}
else
{return "font-weight:bold"}}]]>
</xp:this.style>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<![CDATA[#{javascript:var numEntries = 50;
viewScope.rows = numEntries;
var dt = getComponent("repeat1");
if(dt != null && dt.getRowCount() > 0) {
dt.setFirst(0);
}}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
 
<xp:label
value="|"
id="label5"
themeId="Text.smallSeparator"
/>
 
<xp:link
text="100"
id="link7"
>
<xp:this.style>
<![CDATA[#{javascript:if (viewScope.get("rows") == 100)
{return "color:#808080;font-weight:normal;"}
else
{return "font-weight:bold"}}]]>
</xp:this.style>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<![CDATA[#{javascript:var numEntries = 100;
viewScope.rows = numEntries;
var dt = getComponent("repeat1");
if(dt != null && dt.getRowCount() > 0) {
dt.setFirst(0);
}}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
 
<xp:label
value=" entries"
id="label6"
/>
</xp:panel>
</xp:td>
<xp:td styleClass="lotusRight">
<xp:pager
xp:key="headerPager"
layout="Previous Group Next"
for="repeat1"
id="pager5"
style="font-weight:inherit"
styleClass="pagerTopRight"
/>
</xp:td>
</xp:tr>
</xp:table>
</xp:panel>
</xp:text>
</xp:this.facets>
<xp:this.rows><![CDATA[#{javascript:var rows:Integere = viewScope.get("rows");
if (rows == null)
{return 5}
else
{return rows}}]]></xp:this.rows>
<xp:tr id="rowDataContainer">
<xp:td
style="width:20px;min-width:20px;max-width: 20px;"
>
<xc:ccRowSelectCheckBox
docID="#{javascript:rowData.getNoteID()}"
selectedClass="selectedRow"
containerID="rowDataContainer"
/>
</xp:td>
<xp:td
style="width:200px;min-width:200px;max-width: 200px;"
>
<xp:link
escape="true"
id="link1"
value=""
>
<xp:this.text><![CDATA[#{javascript:rowData.getColumnValue("employeeName")}]]></xp:this.text>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
>
<xp:this.action>
<xp:openPage
name="xpFormPCBuild.xsp"
target="openDocument"
>
<xp:this.documentId><![CDATA[#{javascript:rowData.getDocument().getUniversalID()}]]></xp:this.documentId>
</xp:openPage>
</xp:this.action>
</xp:eventHandler>
</xp:link>
</xp:td>
<xp:td
style="width:75px;min-width:75px;max-width:75px;">
<xp:text
escape="true"
id="computedField2">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("computerName");}]]>
</xp:this.value>
</xp:text>
</xp:td>
<xp:td
style="width:100px;min-width:100px;max-width:100px;">
<xp:text
escape="true"
id="computedField3">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("CrtDte");}]]>
</xp:this.value>
</xp:text>
</xp:td>
<xp:td
style="width:150px;min-width:150px;max-width: 150px;">
<xp:text
escape="true"
id="computedField4">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("crtUsr");}]]>
</xp:this.value>
</xp:text>
</xp:td>
<xp:td>
<xp:text
escape="true"
id="computedField5">
<xp:this.value><![CDATA[#{javascript:rowData.getColumnValue("ID")}]]></xp:this.value>
</xp:text>
</xp:td>
</xp:tr>
</xp:repeat>
</xp:panel>
</xe:widgetContainer>
</xp:view>
SSJS Library
var validateForm = function()
{
var valid = true;
var control;
var control2;
var val;
var val2;
REMOVED business code
function mveTsk(mveDir,pckCat,pckOrd) {
//OK I am assuming we are only moving something that we can move
//Set variables
var pckDoc:NotesDocument;
var trgDoc:NotesDocument;
var trgOrd:Integer;
var tskView = database.getView("(dbAllPCTasksLookup)");
var key:String = createKey(pckCat,pckOrd);
//Get the chosen doc
var query = key;
var pckDoc = tskView.getDocumentByKey(query);
//Get the current order
var curOrd:Integer = pckDoc.getItemValueInteger("order");
//Get doc depending on whether we are moving higher or lower
if (mveDir == "Lower")
{query = createKey(pckCat,pckOrd-1);
var trgDoc = tskView.getDocumentByKey(query);
pckDoc.replaceItemValue("order",curOrd-1)
pckDoc.replaceItemValue("key",createKey(pckCat,curOrd-1))
trgOrd = trgDoc.getItemValueInteger("order");
trgOrd += 1;
trgDoc.replaceItemValue("order",trgOrd)
trgDoc.replaceItemValue("key",createKey(pckCat,trgOrd))}
else
{query = createKey(pckCat,pckOrd+1);
print (query);
var trgDoc = tskView.getDocumentByKey(query);
pckDoc.replaceItemValue("order",curOrd+1)
pckDoc.replaceItemValue("key",createKey(pckCat,curOrd+1))
trgOrd = trgDoc.getItemValueInteger("order");
trgOrd -= 1;
trgDoc.replaceItemValue("order",trgOrd)
trgDoc.replaceItemValue("key",createKey(pckCat,trgOrd))}
pckDoc.save();
trgDoc.save();
resetCategories(pckCat);
return
}
function resetCategories (rstCat)
{
var tskView = database.getView("(dbAllPCTasks)");
var veCol:NotesViewEntryCollection = tskView.getAllEntriesByKey(rstCat);
var veCnt = veCol.getCount();
var curCnt:Integer;
var tmpDoc:NotesDocument;
var curOrd:Integer;
var chgOrd:Integer = 0;
var doc:NotesDocument
print (String(veCnt));
//We have no records so get out
if (veCnt == 0) {
return;
}
//We have one record, so set this record and get out
if (veCnt == 1) {
var entry:NotesViewEntry = veCol.getFirstEntry();
doc = entry.getDocument()
doc.replaceItemValue("order",1);
doc.replaceItemValue("key",createKey(rstCat,1));
doc.save();
return;
}
//We have one first and one last record, so set these two records and get out
if (veCnt == 2) {
//First Entry
var entry:NotesViewEntry = veCol.getFirstEntry();
doc = entry.getDocument()
doc.replaceItemValue("order",1);
doc.replaceItemValue("key",createKey(rstCat,1));
doc.save();
//Second Entry
var entry:NotesViewEntry = veCol.getNextEntry(entry);
doc = entry.getDocument()
doc.replaceItemValue("order",2);
doc.replaceItemValue("key",createKey(rstCat,2));
doc.save();
return;
}
//Else we have 3 or more so go through the loop
print ("we have more");
curCnt = 1
var entry:NotesViewEntry = veCol.getFirstEntry();
while (entry != null) {
doc = entry.getDocument()
print (String(curCnt));
//We are the first record
if (curCnt == 1) {
doc.replaceItemValue("order",curCnt);
doc.replaceItemValue("key",createKey(rstCat,curCnt));
doc.save();
}
//We are the last record
if (curCnt == veCnt) {
doc.replaceItemValue("order",curCnt);
doc.replaceItemValue("key",createKey(rstCat,curCnt));
doc.save();
}
//We are neither first nor last; inbetween record
if ( (curCnt != 1) && (curCnt != veCnt)) {
doc.replaceItemValue("order",curCnt);
doc.replaceItemValue("key",createKey(rstCat,curCnt));
doc.save();
}
var tmpentry:NotesViewEntry = veCol.getNextEntry(entry);
entry.recycle();
entry = tmpentry;
curCnt = curCnt + 1
}
return
}
function orderToString (tmpOrd)
{
keyString:String
tmpOrdStr:String = tmpOrd
if (len(tmpOrdStr == 1))
{keyString = "00" + tmpOrdStr;}
if (len(tmpOrdStr == 2))
{keyString = "0" + tmpOrdStr;}
if (len(tmpOrdStr == 3))
{keyString = tmpOrdStr;}
return keyString
}