0
votes

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.

enter image description here

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">


                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                            &#160;

                                        </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>
                                            &#160;
                                            <xp:label
                                                value="|"
                                                id="label2"
                                                themeId="Text.smallSeparator"
                                            />
                                            &#160;
                                            <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>
                                            &#160;
                                            <xp:label
                                                value="|"
                                                id="label3"
                                                themeId="Text.smallSeparator"
                                            />
                                            &#160;
                                            <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>
                                            &#160;
                                            <xp:label
                                                value="|"
                                                id="label4"
                                                themeId="Text.smallSeparator"
                                            />
                                            &#160;
                                            <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>
                                            &#160;
                                            <xp:label
                                                value="|"
                                                id="label5"
                                                themeId="Text.smallSeparator"
                                            />
                                            &#160;
                                            <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>
                                            &#160;
                                            <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
    }
1
Must you have a SSJS solution or could it be Java based? Not saying I have anything YET but I'm just wondering.David Leedy
Also - I don't see why you call tskView.refresh() right after you get the view. I don't see any point in that performance hit...David Leedy
Java would be fine. As for the refresh, it was just to make sure it is refreshed for my testing. I would turn it off in production. I am wondering now if it would be much much simpler just to not do a loop at all. The user is moving the current document (the one they clicked the arrow on) either up or down. So grab the current document, and then grab the previous one or the next one, depending on whether they picked the down or up arrow. Just swap the order of the two and save. Maybe that is the way to go?Bryan Schmiedeler

1 Answers

0
votes

Well I think there are a couple ways to do this. I don't have an exact answer though I'm interested in exploring it more if time permits. You don't say how many documents there are or what the sort order is. But if the documents contained a field for the sort order then you could manipulate that easily enough I think and just do a view refresh. I'll assume that's not an option though. I believe java.util.ArrayList holds the order of which you insert objects. If not then maybe LinkedList. Anyway - if you created a List and then looped through your view adding the documents unid to the List. You then in theory have a list of Unids in view order. NotesViewNavigator and NotesViewEntry should be very fast to do that. Now you feed that to a repeat control to get the document from the UNID and then any other values you want. Personally I would convert the whole document to a java Object but that's neither here nor there. So how might you effect the order? Well if the List of the unit's is in Scope or wherever you just need a Java method to "Move Up" or "Move Down". I saw this question Moving items around in an ArrayList and there's a Collections.swap method it looks like. Even if that's not the answer there's gotta be some cool Java solution somewhere. Anyway not a clear answer but maybe some ideas for you to pursue.

(Please make sure to post your final solution when you get it. )