xpages set value to field before action=newDocument - xpages

I have a simple button with the eventHandler:
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:openPage target="newDocument" name="/doc.xsp"></xp:openPage>
</xp:this.action>
</xp:eventHandler>
In lotusScript I would add a value to a field, before composing the form, using:
Call doc_new.ReplaceItemValue("txt_codformularmain","01")
....
....
Set uidoc = w.EditDocument ( True, doc_new )
I tried in the postNewDocument event of the doc.xsp
<xp:this.data>
<xp:dominoDocument var="Contr" formName="(fmFormularCIP)">
<xp:this.postNewDocument><![CDATA[#{javascript:Contr.replaceItemValue("txt_codformularmain", "01")}]]></xp:this.postNewDocument></xp:dominoDocument>
</xp:this.data>
I don't want every time the doc is created that value to be, let say, 01, BUT only when the doc. is composed by a specific button. Some other button will have the chance to add the 02 value for that field, and so on.
How can I achieve this in xpages development? Thanks for your time.

This is a little bit tricky in Web application development, because what you did in classical Notes development cannot be applied here. I had a lot of issues in this.
The classical scenario is that you have a Page X with some value (say txt_codformularmain will be 01). How do you decide this value "01"?
In some cases, this value is something you have in the page X. So you want to pass a specific value to the target page. One option could be using a query parameter (doc.xsp?myValue=01) and consume it on doc.xsp with param.myValue. Or, you might put a sessionScope variable before going to the target page and consume it by sessionScope.myValue.
It depends on what you need. Query parameter is somewhat safer, because the user might use the same button twice and sessionScope variable causes inconsistent behaviour. On the other hand, query parameter can be changed by the user, so you might have a breach in your application.
Some cases, you want to populate some values at the second page. Simple values (e.g. creation date, user names, etc.) can be populated with forms (i.e. compute value on load). Sometimes it would be more complicated (e.g. the department of the user where you have to lookup some views). For such cases, you need to use postNewDocument event at the target page.
After you have passed the value to the doc.xsp page, you will consume in the way you need. If it's not going to be edited on the page, you may again use postNewDocument event and set value by dataSourceName.replaceItemValue('itemName', param.myValue). If the value can be edited, you would use it as a default value on your field component.
One problem I have experienced: Once you set a value in postNewDocument, you may have problems to change it later, until the document data source saved. This is happening on specific cases. If you experience such a problem, you might use some editable field with readonly attribute. Just keep in mind :)

Related

dataContext binding to data

I am trying to understand the dataContext better and tried creating a dataContext referencing a document.
<xp:this.dataContexts>
<xp:dataContext var="doc1">
<xp:this.value>
<![CDATA[#{javascript:
var db:NotesDatabase = sessionAsSigner.getDatabase("","privDb.nsf");
var adoc:NotesDocument = db.createDocument();
return adoc }]]>
</xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>
I then tried using EL and javascript to bind fields on my xpage to the dataContext
<xp:inputText id="inputText2" value="#{doc1.lastname}"></xp:inputText>
<xp:inputText id="inputText1" value="${javascript:doc1.firstname}"></xp:inputText>
But when I save, it does't save anything.
<xp:button value="save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
print(doc1.getClass().getName() )
doc1.save();
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
The print command showing me the class name is showing up as lotus.domino.local.Document
The document is saved to the database, but it has no values other than $UpdatedBy. I can't seem to bind fields to the edit boxes.
The reason I am going down this path is twofold, 1. I want to use sessionAsSigner so I can keep the database security on the remote db (privDb.nsf) at No Access for anonymous and default, and 2. I want to learn a little more about dataContext, data sources and binding. I have read the "easy" way of using a Public Document, using the $PublicAccess field, etc, which is the "Old School" Notes way, and yes, I could do it that way, but want to understand how to do it using dataContexts, if possible.
A dataContext is basically a scoped variable, scoped lower than viewScope but higher than requestScope, scoped to a component. That component can be an XPage, a Custom Control or a Panel (yes, dataContexts can be added to a Panel too).
Like other scoped variables on the page, a simple save action does not save dataContexts. If you want a variable that's create-able and save-able, that's the Data Object. That has specific createObject and saveObject properties, where you define what should happen when they're called by the XPages runtime.
Similarly, like the other scoped variables, it needs to be serialized, so you cannot store a Domino object in them. So you can't store a NotesDocument in them. You need to wrap a normal Java object around a NotesDocument. With a greater understanding of XPages it becomes apparent that's what the dominoDocument datasource is doing (creating properties for all fields on the document, storing its note id, UNID, adding other properties like whether it's in edit mode or new etc).
A final point, as Jesse says, dataContexts are re-evaluated multiple times during a partial refresh. I haven't re-tested recently, but under 8.5.3 dataContexts bound to an XPage or Custom Control were re-evaluated more than dataContexts bound to a Panel, so I'd recommend the latter.
The immediate problem that you're running into is that #{}-bound dataContexts are re-evaluated constantly, several times during a page load and, I believe, every time they're referenced. Generally, the rule of thumb with dataContexts (and don't get me wrong - I like them) is that they should either be extremely low-cost, like a quick mathematical calculation, or be ${}-bound. The latter wouldn't work here, though, since the document wouldn't survive past the first load.
The tack you may want to try is to use a dataContext like this:
<xp:dataContext var="docData" value="${javascript: new java.util.HashMap() }" />
Basically, using a simple object as a holding pen. Then, in the save action, create the new document and set all of the values from "docData" there, like:
var db = sessionAsSigner.getDatabase("", "privDb.nsf");
var doc = db.createDocument();
doc.replaceItemValue("firstname", docData.get("firstname"));
doc.replaceItemValue("lastname", docData.get("lastname"));
doc.save();
There are a few caveats to this approach:
Each save will create a new document, rather than editing the existing one. You could change this by storing the UNID back into the map and fetching it from the DB, though
It should work as-is in this case, but you'll have to be mindful of data types. For example, if you have a multi-value control, then the XPages runtime will probably create an ArrayList, which you would have to convert to a Vector for storage unless you're using the OpenNTF Domino API
And as a final note, that binding you have for firstname is almost definitely not what you want. That may just be an artifact of the testing you were doing, but I'd be remiss if I didn't mention it.

How can I refresh the field in CSJS

How to refresh one field in csjs? the code is here:
<xp:repeat id="repeat1" rows="30" var="currentDetail" indexVar="detailIndex" value="#{LeaveBean.details}">
<xp:inputText id="leavefrom" value="#{currentDetail.subfromtime}">
<xp:eventHandler event="onblur" submit="false" refreshMode="partial" refreshId="repeat1">
<xp:this.script><![CDATA[
XSP.partialRefreshPost('#{id:view:_id1:repeat1:0:leavefrom}');
var detailIndex = document.getElementById("#{id:detailIndexText}").innerHTML;
myRPC.checkfromdate(detailIndex).addCallback(function(returnAlert){alert(returnAlert);});
]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
</xp:repeat>
what I want to do is call ssjs from csjs as you can see, but the strange thing to me is the click thing didn't work immediately, for example, when I first open the page, and click the input field, and enter "1" in it, it should be popup alert just when i left the field, but nothing happend, then I click this field again, and enter "2", then I left the field, but I just got the alert "1" on the secreen...sorry for my poor expressions...
so my question is I should refresh the field just when I left the field immediately, but why the XSP.partialRefreshPost not working? I tried to refresh repeat1, not working either
update 2015/06/13:
At last, I found view.postScript....so....
One obvious problem is the partialRefreshPost id won't work. You're combining #{id:...} which runs server-side to calculate the corresponding client-side ID. But the ID that you're passing is a client-side ID for the first row of the repeat. Even if that corresponded to a server-side component, you're trying to hard-code every row to map to a field in row 1 of the repeat ("repeat1:0").
Is there a reason for not using a normal partial refresh and using view.postScript in your SSJS to post CSJS back to the browser? You're triggering a partial refresh posting data back anyway. I'm not sure what the final line of your code is doing, but it seems like it's calling another server-side request to add a response. So the code seems to do two calls to the server, to trigger an alert. I don't know the design and what that subsequent rpc call is doing, but if it can be achieved in a single partial refresh, that would seem better.

learning how to use xe:dominoViewEntriesTreeNode

I realized my question was too vague on adding navigation items dynamically, so I am rewriting the question.
I have discovered the xe:dominoViewEntriesTreeNode control from the xpages. I think I can use this to add navigation items to the navigator control based on entries in the view.
I am struggling to find very much in the way of documentation or resources that break down how to do that. Can anybody to me to a good reference or example code?
You can use dominoViewListTreeNode to build a menu based on views in a database (and not documents in those views).
Here is an example of using xe:dominoViewListTreeNode to dynamically build a menu based on all views called "Test*" (using regex in the filter property). When selecting a menu item from the menu, the name of the view is submitted to the server (using EL notation for the viewEntry.getName() method).
The example also contains an onItemClick event handler that "catches" the name of the view as the submitted value and stores this in a sessionScope variable. The event handler then redirects to a views.xsp XPage that could contain a Dynamic View Panel control where you could use the sessionScope variable to control what view to show.
The sessionScope variable is also used to mark the selected menu item as "selected".
<xe:navigator id="navigator1">
<xe:this.treeNodes>
<xe:dominoViewListTreeNode filter="Test.*" submitValue="#{viewEntry.name}" var="viewEntry">
<xe:this.selected><![CDATA[#{javascript:viewEntry.getName() == sessionScope.clickedView}]]></xe:this.selected>
</xe:dominoViewListTreeNode>
</xe:this.treeNodes>
<xp:eventHandler event="onItemClick" submit="true" refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:sessionScope.clickedView = context.getSubmittedValue();
context.redirectToPage("views.xsp");}]]>
</xp:this.action>
</xp:eventHandler>
</xe:navigator>
Instead of the onItemClick method to redirect to an XPage, you could compute the href property of xe:dominoViewListTreeNode to return the name of an XPage.
I have a short presentation called "XPages Extension Library - Create an app in 1 hour (almost)" that presents this technique (and other techniques).
I assume you have an area on your page carrying the navigation items, e.g. links to some pages with link texts?
I would then use a repeat control with a datasource/javascript source that returns the document item values from your profile document or something.
If you are not into repeat controls then you should consider to read this: http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_repeat_controls
On this page there is also a sample dealing with a profile document.
By the way: using profile documents was always a crutch, so consider to youse "normal" config documents instead.

xPages - adding a field down the track

I want to add a field to an existing xPage, and need to run past a few xPage fundamentals to check their validity.
I have done the following:
In order to ensure that my dev and prod sites work, the form the document is bound to is calculated (as the datasource resides in a diff db).
My new field is called 'NewField', and I have used simple data binding, chosen document1 (same as the rest of the fields on my document), and entered the new field name (can't select from the drop down as the document is calculated).
I have also created the field on the actual notes document, however I did not think I HAD to do this? Also, it's on a calculated subform so not sure if this is relevant? This is an xpage version of an existing notes form - both client and web access are applicable for the application.
I thought it would create the field specified in 'Bind To' if it was missing, however even with it on the notes form, it still does not store the value.
Whats gone wrong:
The field is created on the document, however the value is not being populated. The value stored against the notes document is an empty string.
There is nothing complicated here, not doing anything funky, yet the value is not mapped?
The rest of the fields (created when I created the initial document) are mapped correctly.
Any suggestions? Is this a rookie error with adding fields to an existing xPage?
A
As requested, here is the code for both the data binding and a field that does work, and one that does not.
Here is my initial code to define document1. It does call an agent post save, however this agent does nothing with the field value that is not being assigned.
<xp:this.data>
<xp:dominoDocument var="document1" action="openDocument">
<xp:this.databaseName><![CDATA[#{javascript:var sname = ("","test\\testdb.nsf");
return sname;}]]>
</xp:this.databaseName>
<xp:this.formName><![CDATA[#{javascript:return "MainForm"}]]>
</xp:this.formName>
<xp:this.postSaveDocument>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var sname = #Name([CN]",#Subset(#DbName(),1));
var dbname = "test/testdb.nsf";
var dbTest:NotesDatabase = session.getDatabase(sname, dbname);
if (dbTest.isOpen()){
ag = dbTest.getAgent("UpdateDoc");
noteid = document1.getDocument().getNoteID();
ag.run(noteid);
} }]]></xp:this.script>
</xp:executeScript>
</xp:this.postSaveDocument></xp:dominoDocument>
</xp:this.data>
And then here is the code for the field assignment that is not working.
<xp:inputText value="#{document1.NewField}"
id="inputText25" disableClientSideValidation="true" styleClass="entryboxes">
</xp:inputText>
And then here is the code for a field assignment that is working.
<xp:inputText value="#{document1.CLRef}"
id="inputText4" rendered="#{javascript:#IsNewDoc()}" styleClass="entryboxes">
</xp:inputText>
I thought that this was fairly standard.
update 8/10
So - I have worked out that this happens only when I have conditionally hidden cell. This has a basic formula, asking the row to be visible based on the value of another field. The display / no display functionality is working (see below for code), yet, for a reason unknown to me, it does not save the value.
var hw = getComponent("module");
var hwv = hw2.getSubmittedValue();
hwv == "Product1" || hwv == "Product2" || hw2v == "Product3"
If I create any other field NOT hidden (and make up the field name), it maps to the document correctly (as I thought it should). The good news is I have managed to replicate with a new form, so it appears to be with me wanting to hide a row conditionally, and not an issue with my form? Any ideas?
OK - I continued to have issues with the field mapping when the cell was hidden in certain conditions. So, I ended up making the cell visible, and then having the field mandatory only in certain circumstances. So, question not answers, but a work around used.

Creating a response document via a button

I have two forms, Organisation and Contact. Contact is a response to Org, and each form has an XPage where the form can be filled in, saved etc. When opening edit_contact.xsp directly and creating a document (not as a response to an org), everything works fine.
On edit_org.xsp, I have a button with 2 events. The first copies some values into sessionScope so I can inherit them into the Contact. The second is a "Create Response Document" event which creates a new response with the parent ID being the current org document, and sends the user to edit_contact.xsp. Pressing the button changes XPage correctly and the field inheritance works fine, but pressing "Submit" on the Contact form doesn't save anything, and no document is created.
This exact same setup works 100% as desired in another database, I have no idea why it won't work properly here. Is there an obscure setting somewhere I am missing?
<xp:button value="Create Contact" id="button1" rendered="#{javascript:!document1.isEditable()}">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script>
<![CDATA[#{javascript:var doc = document1.getDocument();
sessionScope.PFirstName = doc.getFirstItem("P_Firstname").getValueString();
sessionScope.PSurname = doc.getFirstItem("P_Surname").getValueString();
sessionScope.PFamily = doc.getFirstItem("P_Family").getValueString();
sessionScope.PDOB = doc.getFirstItem("P_DOB")
sessionScope.PAGE = doc.getFirstItem("P_Age").getValueString();}]]
</xp:this.script>
</xp:executeScript>
<xp:createResponse name="/edit_contact.xsp" parentId="#{javascript:document1.getNoteID()}">
</xp:createResponse>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>`
Here is a link that shows what I am trying to do (minus the field inheritance):
http://min.us/mKSJED8tT
Currently the forms and views all work, but the document created with the "Response" form appears not to be a response document - it has no $REF field. This setup works perfectly in a different database - what is going on?
It's hard to tell what might be happening without seeing any code. Since it works for you in another database, could it be an ACL issue? Where the user you're logging in as - possibly - anonymous - doesn't have the ability to create documents?
Instead of "push" approach go for "pull" - just open response page with url parameter of parent document. In its postNewDocument event initialize field values from it.
It would be useful to get an update on two key points made by others:
Is ignoreRequestParams set on your response document datasource? If not, regardless of what you're trying to define for the second datasource, it's UNID etc are pulled from the request parameters. So both datasources are effectively the same datasource.
Is it throwing a validation error? If so, nothing will be saved.
There are two possible issues:
First the use of getFirstItem("x") is not a best practice. So:
sessionScope.PDOB = doc.getFirstItem("P_DOB")
would be storing a NotesItem in the sessionScope which will not work. It is recommended to use:
sessionScope.PDOB = doc.getItemValueString("P_DOB");
Second the use of getNoteID() might not be returning what you want (Which is the UNID of the document). Use .getDocument().getUniversalID() instead.
<xp:createResponse
name="/edit_contact.xsp"
parentId="#{javascript:document1.getDocument().getUniversalID()}">
</xp:createResponse>
-edited-
/Newbs

Resources