Get list of all ids in an xPage container control - xpages

I am using this code to copy some documents by a button click source. I would like to prevent the end user from having to select columns and would prefer simply get all the documents ids from the view panel. Not exactly sure how to do that, or if a dataview might be a better choice for me.
var viewPanel=getComponent("viewPanel1"); //get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); //get the array of document ids
for(i=0;i < docIDArray.length;i++){
var docId=docIDArray[i];
var doc=database.getDocumentByID(docId);
var db=session.getCurrentDatabase();
var newDoc:NotesDocument=doc.copyToDatabase(db);
newDoc.replaceItemValue("approved","No");
var id=newDoc.getUniversalID();
newDoc.save(true);
}

Leave the view panel out of the equation: a view panel is a component, and components are for users to interact with; if the user's interaction with the view panel (i.e. "selecting" documents) doesn't alter which documents you wish to duplicate, ignore the view panel (at least, for the purposes of this specific event).
If you simply want to duplicate all documents that display in the view to which the view panel is bound, talk to the same data source the view panel is associated with. So, assuming your data source declaration looks something like the following:
<xp:panel>
<xp:this.data>
<xp:dominoView var="allDocuments" viewName="($All)" />
</xp:this.data>
<xp:viewPanel value="#{allDocuments}">
...
...then just iterate through that same view:
allDocuments.setAutoUpdate(false);
var eachDoc = allDocuments.getFirstDocument();
while(eachDoc) {
var newDoc = eachDoc.copyToDatabase(database);
newDoc.replaceItemValue("approved", "No");
newDoc.save();
newDoc.recycle();
var nextDoc = allDocuments.getNextDocument(eachDoc);
eachDoc.recycle();
eachDoc = nextDoc;
}
allDocuments.setAutoUpdate(true);
Since you're duplicating the documents within the same database, when the event finishes, the view panel will simply show twice as many documents, since you duplicated all of them. Unless, of course, the item value you're replacing disqualifies them from the view you're displaying.
NOTE 1: The reason the code above toggles the autoUpdate property is because, unless you toggle that to false prior to the iteration, when you duplicate each document, if the new document does display in the view you're iterating, the indexer will become aware of it, and you might end up in an infinite loop, because each time you try to get the next document, it's actually returning a handle on the duplicate you just created... so you would essentially be infinitely duplicating the same document until some exception is thrown (i.e. stack overflow, out of memory, etc.). Disabling autoUpdate prevents that by only allowing iteration of entries the index was aware of when your routine began.
NOTE 2: If the data source is only defined inside the view panel, move it to a parent (panel, Custom Control, or XPage) that also contains whatever component will trigger the duplication (i.e. button, link) and reference the data source within the view panel. That way both the view panel and the button can talk to the same data; otherwise, only the view panel is aware that the data source exists.

Related

xpages embedded view or view panel

I know it is a very simple question , but I'm trying to see the difference between a view panel ( that I drag from the Container Controls ) and an embedded view.
How can I add an embedded view on my xpages?
Or the 2 items are one and the same.
The reason why I'm asking this question: I have a view panel on my xpage ( I thought it is like an embedded view in clasic lotus notes programming ) where I have listing some docs. The first column is categorized based on the UNID document.
I noticed if I compose again another document, this view contains all the previous docs., and all the UNID categorized. The view panel isn't empty.
In lotus notes programming:
If I have a main form and a computed text field with #Text(#DocumentUniqueId) and some button for composing another form ( of course, when clicking the button I saved the main form to obtain the UNID ), and this form has the same text field name as the previous - I'm passing the UNID to this second form. If the main form contains an embedded view listing all documents saved from the second form I will use the first column categorized and hidden with the name of the field from the second form ( which will contain the UNID ). If I save and close the 1st main, and then I'll compose another main form, the embedded view is not listing the previous documents already saved.
Well, this view is already created.
I did drag and drop this view in myxpage. I have a button inside myxpage that shows a dialog. Here a datasource is declared, the dialog containing some fields. In the main xpage ( where is defined another datasource ) there is a computed field which takes the UNID of the document. Before I click the dialog I save the first datasource, to pass the value UNID to other field inside the dialog.
I save the datasource from the dialog & close the dialog, and then the view panel ( which is the view from the lotus notes classic presented above ) lists the doc. If I close the main xpage ( save it if is a new one ) and then open another main document, the view isn't empty, it contains the previous document lists. ( I tried also to hide it, if the xspDoc is new. But when I try adding other docs. from the dialog, the view panel lists also the previous documents from the previous doc., even if the UNIDs are distinct )
Thanks in advance.
To explain this best, it's good to consider what a view and an embedded view in Notes Client is. A view determines the look and feel, like a View Panel and by default will show all documents found by the selection criteria. So in this case the documents available correspond to a dominoView datasource bound to the underlying view with no additional criteria defined.
An embedded view still uses the view to determine the look and feel, but will not display the first column (that's effectively a property of the embedded view "control" in Notes Client). In the View Panel you choose which columns display, so you would need to code that yourself on the View Panel, where you choose which columns to display. The embedded view also has a property to define the single category. But the View Panel and other repeating containers (like Data Table and repeat control) don't restrict the data available, that's done by the datasource - a dominoView or e.g. a ViewEntryCollection for anything other than the View Panel. So in that you set the filter.
However, a new document does not have a UNID, so it cannot restrict what is displayed. Instead, I think a good approach is to set the visibility so the View Panel is only displayed if it's not a new document. You can use loaded, if you fully reload the page after save, or else rendered.
First off, this is not a stupid question.
I am assuming you are using a self implemented parent response system and have a view sorted by the parent UNID. Under the data source of the view panel, find the the "filter by category name" option. there compute the UNID value, or category value you are searching for.
Steps:
insure that the background view is categorized (ascending order is best to insure that this works, though I doubt it is needed)
drag (my favourite is the dynamicViewPanel) a view control onto the xpage.
Under Properties/Data there is a place to calculate the value of the category to show. If you want an exact match, check the exact match check box.
If you mistype the category value or the value is not there, then no documents will be shown.
if you put in an empty value all documents are shown (at least in the tests that I can remember)
if documents are returned, the categorized column is automatically hidden.
EDIT
If you are reading the value from a field, you can use the code:
xspDoc.getItemValueString("fld");
If you are searching based on the UNID, again, hide the viewPanel if the document is new. If the value is not yet set, also hide the panel.
If you have two custom controls, even if a custom control is embedded in the second, you cannot easily have one custom control access the values of the datasource in another. I am sure there are ways to trick this into working, but in this case, use a viewScope variable to access the values and possibly an onLoad, onChange or onSave event to update the viewScope variable. Make sure to verify that the result is not null or empty by either printing it out to the server log or another field.
Final Edit after question edit/expansion
These are the steps that I would take to do what you described.
You have your parent document XPage. Insert all fields for this Xpage.
Drag a dynamicViewPanel onto the XPage. If parentDoc is New, then hide.(this could be a custom control in theory, but if you are having trouble, try it without for the sake of trouble shooting.
Set dynamicViewPanel datasource to ignoreReqeustParams.
Calculate the datasource and setting the "Filter by category name" filter. When computing this, for the sake of ease and troubleshooting, print this value out to the server, or other logging mechanism. You can delete it later.
Verify that the correct UNID/Value is being inserted into the document you create in the dialog.
If you are using a scoped variable to hold the filter value, be sure you are using viewScope and not appliationScope or sessionScope.
if you are using a custom control and standard parameters (not scopedVars), verify that the compositeData variable is being updated with a log or server print.
Consider setting Dialog Properties/AllProperties/basics/refreshOnShow to true
Play with the partial refresh option for the dialogOpen action, test a full vs partial refresh.
Remember to set the ignoreRequestParams for the document you are creating in the dialog to true
Consider making the new document data source created in the Dialog to request scope.
And of course consider and test all datasources being defined on the XPage on not some here, some there.
Verify that you are taking the value of the UNID from the main document and not the new dialog document by mistake!
Those are all of the tips I can think of right now, pretty much in the order I'd try them in. If that does not help, then I suspect there is a piece of this puzzle that you are not including in the question. Remember, try to keep things as simple as you can. You can over think things, make things harder than they need be.

View data being cached/failing to refresh

I have a tabbed panel containing different sections of a form. In one section, users are given the ability to add a child document to the currently open document. In a second section, they are given a listbox, where the options are dynamically generated (via #DbLookup) by looking at a view of all the child documents, filtered by the current document's ID. This functionality all works correctly, however, there is a problem with the dynamic listbox options.
When a user adds a new child document, then switches to the next tab, the listbox is not updated with this new document. If they save/re-edit the main document or refresh the page, it makes no different, the user must load another XPage before going back to the original in order for the listbox to update. I have tried to resolve this by doing full updates of the page, using session.evaluate and "NoCache" in the #DBLookup call, or calling database.getView("My view").refresh(), but without luck.
There is also a similar problem where I have a repeat control which uses a view of child documents (again filtered by main document ID) as a datasource. When a user adds a child document using a button, it partially refreshes the repeat - but doesn't show the new child document until the page is refreshed again (or you leave the page and return).
Is there something crucial I am missing with regards to the JSF lifecycle/the way that view data is cached?
As first measure I would add another formula item to the listbox which simply returns the current time (#Now() should work). That way you can check if the listbox options are refreshed on the update in the first place.
If the options are refreshed fine it's indeed clear that the #DbLookup does some caching, although I'm not aware of any default caching logic.
At least for a test I would change the code to use a NotesView object instead of the #DbLookup, something like this:
var nview = database.getView("someview");
var nc = nview.getAllEntriesByKey(currentDocument.getDocument().getUniversalID(), true);
var a = [];
var ve = nc.getFirstEntry();
while (ve) {
a.push(ve.getColumnValues().elementAt(0)); // value of first column
ve = nc.getNextEntry(ve);
}
return a;
(I wrote the code from memory, there may be syntax errors).
Since the code only works with view entries it should be equally fast than the #DbLookup. And you could even do a nview.refresh() if needed.

Opening different xpages forms from a view panel

I have an Xpages application that pulls data from another .nsf file. I have a view panel linked to a view in that db. The view has documents with several different forms in it. I want to be able to open each document in it's own form(xpage).
How do I write a computed At Runtime, open selected document using: statement that will select the correct Xpage to present the document.
If you use the Data View component instead of a View Panel, you can compute the pageName attribute, referencing the var attribute to return a different value for each row based on the document that row represents. The flexibility of the Data View component also makes it easier to make your app look more like a modern web application and less like an Excel spreadsheet. As an additional bonus, the mobile theme invokes a renderer that makes each Data View instance look like a native mobile list, so using Data Views instead of View Panels simplifies mobile development.
You have 2 options:
use "use xpage associated with form" and edit the form's property
use a SSJS formula to compute the Form. You provide a variable name in the view control var to access a view row as XSPViewEntry. If the Form is in a view column even one you don't display you use .getColumnValue otherwise getDocument.getItemValueString
Does that work for you?
Maybe this mothed can help you: Unable to get document page name for
Hope this helps
Mark
I had a similar problem today. I use only one form but 3 different xpages for associated with this form. I have 3 different document types in the view. I used rowData the get the type of the document.
try{
var v=rowData.getColumnValue("form");
if(v.indexOf("x")> -1){var page ="x.xsp"}
else if(v.indexOf("y") > -1){var page = "y.xsp"}
else{var page = "z.xsp"}
}catch(e){
var page = "x.xsp"
}
So to your view you can create a column with the value of the form and you can use it.
I have used the extension library Dynamic View control which has an event you can code to get a handle to the NotesViewEntry which was selected. See the demo database page Domino_DynamicView.xsp and the Custom Event Handler tab for an example.
Note, in 8.5.3 (I have not upgraded yet) if you add or edit the eventHandler for onColumnClick it will be added to the XPages source as an xe:eventHandler. It needs to be an xp:eventHandler to work. The way to do it is to copy the code in the source from the exiting event and delete it. Recreate the event and update the code. Then go back into the source and change the tags within the eventHandler to xp:.

XPages remove documents on server and trigger partial refresh

I am struggling with the following.
On my XPage I have a viewpanel component, but it is not bound to a notesview datasource, but to a hashmap stored in viewScope. Reasons for this is beyond scope of my question.
Since the lines in my view are not actually linked to the documents I cannot use the standard checkboxes and the related getSelectedDocIds. I do however want a way to remove the selected documents. I have a column with checkboxes containing the unid of the corresponding row.
So long story short. I have an array of unids and want to perform an action that does the following:
Display a dijit.Dialog asking for confirmation
If OK clicked call a function that does the following:
Remove the documents based on the unids
Refresh the viewpanel
I am thinking of the following 2 solutions, but in doubt what would be best (maybe a third, even simpler solution?)
Have the OK button of the dojo dialog call a function that does an XmlHttpRequest to an XAgent or plain old LS agent
Have the OK button trigger an eventhandler that runs on the server as described by JeremyHodge here. But how would I pass the unids as parameter and refresh the view afterwards?
Thanks!
Cant you just make use of the extension library dialog with the dialog button control. In this button control you can then
A third option would be to add a column to your datatable/view which contains checkboxes. On the onchange event of these boxes you add an eventhander which adds the value to a viewScope variable.
A button on the bottom (or top.) of the page you add the code you need to remove the selected items from the hashmap, delete the documents associate with the selected id's. this button can be a ordinary button with a partial refresh on the viewpanel. When you run into the bug that you cant use buttons in a dialog please use the extension library dialog control because this fixes that issue for you.
If the current user does not have the correct access level to delete documents you could use the sessionAsSigner global (assuming the signer of the design element has the correct access levels).
This way you dont need to go call an xAgent by xmlthttprequest and can stay with the default xpage methodology.
I hope this helps in some way
I would second #jjbsomhorst in the use of the extension library for the dialog box - if you use one at all. Usually users don't read dialog boxes. So the approach would be add the column with the checkboxes, but don't bother with an event handler, but bind them ALL with their value to ONE scopeVariable. On submission that variable will then hold an array with the selected UNID.
Then render a page that lists these documents and have a confirm button. While the new page affords a server round-trip the likelihood, that users actually pay attention is way higher. What you can do:
Have the normal page that renders the dialog with editable checkboxes and when the user clicks "Delete" you set something like viewScope.confirmDeleteMode=true; and use that as condition for the checkboxes and make them read-only AND set the class of the selected rows to "morituri" which in your CSS would have something like .morituri { color: white; background-color : red; font-weight: bold } and a new button "Confirm Delete" (and hide the Delete button).
This way you only have one page to deal with.
I went for option 2, which has the possibility to provide the partial refresh id. I passed the unids as a submitvalue like:
function doRemove(unids){
XSP.executeOnServer(ISP.UI.removeEventID, ISP.UI.removeRefreshID, {
params: {
'$$xspsubmitvalue': unids
},
onComplete : function() {
//alert('test')
}
});
}
The ISP.UI.removeEventID performs the following code:
var unids = context.getSubmittedValue();
removeDocuments(unids); //SSJS function performing the actual delete
viewScope.reload = 'reload' //triggers the hashmap to be rebuild based on new documentcollection

Xpages- Create New copy of saved document and open it without saving it

I have a document that I want to create a new version/copy of, so I am trying to do server-side javascript to
Create a new document
copy all items from the current document
open the new document that I have created, without saving it
I am not able to open the newly created document, is this possible?
code I am using is:
var viewPanel=getComponent("viewPanel1");get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); get the array of document ids
for(i=0;i < docIDArray.length; i++){
var docId=docIDArray[i];
var doc=database.getDocumentByID(docId);
var newDoc = database.CreateDocument
doc.CopyAllItems (newDoc)
var docUNID = newDoc.getUniversalID ()
// need something here to open copied document
}
You need to store the IDs in the session scope and then open the page and do the copy inside in one of the data source events:
var viewPanel=getComponent("viewPanel1");get the componet of viewPanel
var docIDArray=viewPanel.getSelectedIds(); get the array of document ids
sessionScope.alltheDocs = docIDArray;
then open the page where you want to have the new document. Inside that page you need to have a repeat control that matches the element count of alltheDocs. I probably would design it using a DojoTab container (one tab per document). Inside the repeat place a panel with a data source (or a custom control). Then in the queryNewDocument event you copy the fields using the variable name of your datasource.
You could save these docs, then show them and add them to a deletion queue by marking a field on those docs. On save remove them from deletion queue as a work around possibly.
1) Do not store Notes object to scope lasting longer than request.
2) If XPage has to inherit some values, it needs to read them from some source.
3) You can not inherit data from Notes document - according to (1) in memory object must not be stored in sessionScope (simplest way to pass objects between two pages), and you can not retrieve it by UNID/key (it is not saved, as requested).
So, (possibly) the only option is:
Make copy of source document - copy every field you are interested in into Map[String,Object]. Fields must be converted into "raw" objects String, Double, Date (java, not Notes) or their vectors for multivalues. You must not copy special (Notes objects) fields - names, dates, rich text! Names can be converted to strings, Dates can be retrieved as Java dates, rich text might be treated as MIME (String) (but with possible loss of formatting). I think you don't want to pass attachments.
In target XPage, define queryNewDocument event to lookup and initialize fields from this Map object. Delete the sessionScope object to prevent duplicates.
I needed very similar thing in my application. I have XPage with a source document opened in read mode. There is a button that creates a new document and set few values (using the source document). I wanted the XPage to open this new document in Edit mode after it was created.
Note: I could not use redirect with URL parametr action as I needed to open it in a same XPage and preserve a view scope variables and beans.
Solution:
Add View Scope variables NewDocAction and documentId (on button click),
Partial refresh of XPage (on button)
Compute document data source using viewScope variable documentId
Check presence of View Scope variable NewDocAction (in onClienLoad event)
javascript code in the button:
var travelDoc = xpBean.createTravelDoc(requestDoc);
if (travelDoc != null){
viewScope.put("content","travelForm"); //to render proper CC on XPage
viewScope.put("documentId", travelDoc.getUniversalID());
viewScope.put("NewDocAction", "ToEditMode");
}
javascript code in a onClientLoad event of a travelForm custom control:
if (viewScope.containsKey("NewDocAction") && viewScope.get("NewDocAction").equals("ToEditMode")){
context.setDocumentMode("travelDoc","edit");
viewScope.remove("NewDocAction");
}

Resources