I have Save & Close button that is supposed to save current document and navigate to Home page.
The problem that it doesn't save the document (the doc is in edit mode). If I set button property submit="true" then it does save the doc by makes a replication conflict.
<xp:button id="buttonSaveClose">
<xp:this.value><![CDATA[Save & Close]]></xp:this.value>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete" immediate="false" save="true">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument
var="#{javascript:document1}">
</xp:saveDocument>
<xp:openPage name="/Home.xsp">
</xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Just delete <xp:saveDocument ... </xp:saveDocument>.
You already set event handler's save-parameter to true and the document gets saved automatically. No need to save it a second time with a save action.
<xp:saveDocument
var="#{javascript:document1}">
</xp:saveDocument>
I'm pretty sure this should be #{document1} - it should be Expression Language rather than SSJS. Using a very basic simple action like saveDocument is good for your first demo database but, as you're seeing, there's no way to troubleshoot it or work out why it's not working. I'd recommend moving onto proper code as soon as possible, as I do in all training courses I run - so #{javascript:document1.save();}. You'll need this as soon as you want more control and it's not rocket science. Similarly, #{context.redirectToPage("Home.xsp")} replaces the other simple action.
Related
UPDATED at bottom...
I have a custom control that contains an input field with typeAhead enabled:
<xp:inputText id="UTAN1" value="#{document1.UTAN}"
maxlength="5" styleClass="dbListFieldData" style="width:60px;font-size:13px;">
<xp:typeAhead mode="full" minChars="2">
<xp:this.valueList><![CDATA[#{javascript:var key = getComponent("UTAN1").getValue();
var path = new Array("","utans.nsf")
#Unique(#DbLookup(path,"(UTAN Lookup)",key,1,"[PARTIALMATCH]"));}]]></xp:this.valueList>
</xp:typeAhead>
<xp:eventHandler event="onchange" submit="true"
refreshMode="partial" refreshId="utanPanel1">
<xp:this.action>
<!-- DO SOME STUFF -->
</xp:this.action>
</xp:eventHandler>
</xp:inputText>
If I use the control inline on an xpage, typeAhead works fine.
However, if I insert the control in a dialog and call it to open:
<xp:link escape="true" id="link3" styleClass="dbListFieldData" >
<xp:eventHandler event="onclick" submit="false" id="eventHandler3">
<xp:this.script><![CDATA[XSP.openDialog('#{id:miniDialogUTAN1}');]]></xp:this.script>
</xp:eventHandler>
<xp:image id="image2" url="/edit2.png" style="height:16px;width:16px;" alt="Edit UTAN"></xp:image>
</xp:link>
The typeAhead seems to be partly crippled.
If the field is already empty, suggestions comes up fine.
If the field has an existing value, backspacing only shows the existing value as a suggestion.
Overwriting the value entirely gives no suggestions at all.
The only thing that will get the typeAhead working is to change the value and hit Enter. Basically, trigger an onChange. After that, ALL the typeAhead fields start working.
Any ideas on how to correct this behavior? FYI, I'm an xpages noob, so please go easy on me.
UPDATE 6/29/2021:
I'm still desperately in need of help with this. I've been playing around with Firebug and found something interesting.
In type-aheads on a normal Xpage form (where type-ahead works), entering each character calls a POST every time. However, when the type-ahead is in a dialog, typing in characters calls GETs. If I hit Enter or tab out of the field (triggering onChange), I get a single POST, and then clicking back into the field and typing, it starts using GETs again and it pulls valid suggestions as long as I don't backspace and type something different. Then the suggestions go away. This would be expected since GETs are cached and POSTs are not.
So what's going on with type-aheads in dialogs that makes them use GET, and how to I force it to use POST on every key press?
To people of the future...
After weeks of fretting over this and finally giving up I stumbled upon the solution by accident.
My dialog opens in read mode. I have both an Edit button and a Cancel button at top and bottom that were originally hard coded in both spots of the dialog custom control. Later I went to clean some stuff up and decided to create a single custom control for both sets of buttons. Once I did that, suddenly the type-aheads worked!
It turns out that when I copied and pasted the XML from the (supposedly) duplicate button panels to the new custom control, I had copied from the bottom button panel (which I rarely used while testing).
When I compared the XML for the top and bottom Edit Buttons, I found that the top one had the following attributes in the onClick:
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
while the bottom one had:
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="dialogOuterPanel1">
So, the full refresh broke my type-aheads and a partial refresh fixed them.
I have a document that contains a rating custom control (xInvolve, which is excellent!). In this application, administrators want the ability to delete certain ratings for a certain document or all of them (bad ratings on purpose, new version of the document, corrections made to the document ...).
I am showing up the ratings in a view, in a dialog box (the extension Library dialog box, not a Dojo one). In that dialog box, I have a "Delete All" button. That button calls a SSJS function that deletes the rating documents for the document that is currently opened, but I want to refresh the panel that displays the rating, as it should now be empty.
So far, I was able to close the dialog box, but I can't seem to get the panel to refresh. Here's the code for the "Delete All" button:
<xp:button value="Delete All" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:confirm
message="Are you sure you want to proceed?">
</xp:confirm>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:deleteAllRatings(pageDocument.getDocument().getUniversalID());
var c = getComponent("dialogPageRatings");
c.hide("PanelHeader")}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
The PanelHeader is the panel where the xRating control is inserted.
Should I try putting code in the onClose event of the dialog box? I tried but I didn't get more luck.
Thanks
So you can use client side code to achieve this. This is what we do:
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var strClientCode = "$('#editDeliveryAddressDialog').modal('hide'); window.location.reload();"
view.postScript(strClientCode);}]]></xp:this.script>
</xp:executeScript>
Hope it helps.
Ben,
Here is a solution using the RPC control. This control allows you to call server code directly from clientside javascript. I have frequently used it to call java methods, but haven't used it to call a SSJS function in a library. I make the assumption that it would work the same.
<xe:jsonRpcService id="jsonRpcService1" serviceName="myRPC"
pathInfo="rpc">
<xe:this.methods>
<xe:remoteMethod name="callDeleteAllRatings">
<xe:this.script><![CDATA[deleteAllRatings(universalID)}]]></xe:this.script>
<xe:this.arguments>
<xe:remoteMethodArg name="universalID" type="string" />
</xe:this.arguments>
</xe:remoteMethod>
</xe:this.methods>
</xe:jsonRpcService>
You would not be able to use getComponent in the RPC, so you would need to pass the UNID. You could pass this to the clientside using a <xp:hiddenInput> when you launch the window. You would close the window in the same manner that you do now (I think).
To call the method of the service, you would use myRPC.callDeleteAllRatings("Open ATM", ""); Again, you call the RPC from clientside.
IMO, once you learn what the RPC can do for you, you wonder how you made due without it.
As Mark suggested in a comment above, you should be able to use the onHide property. For example if you wanted to refresh a panel with serverSide id panel1
<xe:dialog id="dialog1" title="Example Dialog"
onHide=" XSP.partialRefreshGet('#{id:panel1}'); ">
This is working for me
Can you not just do a partial refresh? I do that with a simple dialog like so...
<xp:button value="Save and Close" id="button2" styleClass="btn btn-primary">
<xp:eventHandler event="onclick"
submit="true" refreshMode="partial"
refreshId="panelRefresh"
disableValidators="true"
onComplete="$('#myModal').modal('hide');">
<xp:this.action><![CDATA[#{javascript:var value:string=getComponent("inputText1").value;
document1.replaceItemValue("modalTest",value)}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
I'm setting value of input field on click event of the check box. The editable field is used to validate group of check boxes. This code works in one xpage. When I tried to replicate this code in other xpage, it is not working.
Here is a working code:
<xp:checkBox
text="pH"
id="checkBox1"
value="#{document1.PH}"
checkedValue="pH">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="routineSectionInput1"
execMode="partial">
<xp:this.action>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var
checkBox1:com.ibm.xsp.component.xp.XspInputCheckbox = getComponent("checkBox1");
var routineSectionInput1:com.ibm.xsp.component.xp.XspInputText = getComponent("routineSectionInput1");
if (checkBox1.getValue()=='pH'){
routineSectionInput1.setValue('Selected');
} else {
routineSectionInput1.setValue('');
}}]]></xp:this.script>
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:checkBox>
I used same field names and same code on another xpage and it is not working. What I'm doing wrong?
Best regards
When you work with component logic where binding is involved, don't go after the component, go after the data that defines their value. So your code would rather look like this:
<xp:checkBox text="pH" id="checkBox1"
value="#{document1.PH}" checkedValue="pH">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="partial"
refreshId="routineSectionInput1"
execMode="partial">
<xp:this.action>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var chkValue = document1.getItemValueString("PH");
viewScope.routineSection = (chkValue=="pH") ? "Selected" : "";
}]]></xp:this.script>
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:checkBox>
<xp:text id="routineSectionInput1" value="#{viewScope.routineSection}"></xp:text>
Hope that helps
If there are validation errors or components have data that is of the wrong data type (e.g. text that cannot be parsed as a number in a component bound to a Number field, so conversion errors), any SSJS will fail.
I'd recommend adding a print statement at the start of the SSJS to check whether it's firing. (If you're more confident with XPages, use a PhaseListener to check the correct phase is being triggered).
Also, it's worth adding an Display Errors control to catch any validation errors caught during the partial refresh, ensuring it's within the refresh area (otherwise the errors will not be displayed in the browser).
"Process data without validation" option may be of use here, it you want to skip validation. Note that conversion errors will still prevent SSJS running.
As Stephan says, use the datasource rather than the component, if possible. The datasource will have been updated before the SSJS fires.
Removed the checkboxes, compacted the database with -c and added back those checkboxes with the same code. It is working now.
I have a requirement for having several graphical buttons at the bottom of each screen of a Mobile App. I have used the logic found in Graham Acres NotesIn9 presentation (#121) to get the Mobile Control Tab Bar (xe.tabBar) to load at and stay at the bottom of the screen. However adding anything other than Tab Bar Buttons (xe:tabBarButton) causes the control to display off to the right of center, and not in the center or left justified as needed.
Here is code I tried:
<xe:tabBar
id="tabBar1">
<xe:this.dojoAttributes>
<xp:dojoAttribute
name="center"
value="true">
</xp:dojoAttribute>
</xe:this.dojoAttributes>
<xp:image
url="/BottomButtonHome.jpg"
id="image1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action>
<xe:moveTo
direction="Left to Right"
targetPage="homepage"
transitionType="slide">
</xe:moveTo>
</xp:this.action>
</xp:eventHandler>
</xp:image>
</xe:tabBar>
This resulted in a skewed to the right presentation and clicking the graphic did not successfully open the other page.
Since that did not work I tried a simple Panel control (xp:panel) with the graphics and that presented better but the links still fail to open the other page. Here is that code:
<xp:panel
style="height:54px;width:100%">
<xp:image
url="/bottomButtonHome.jpg"
id="image2">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action>
<xe:moveTo
direction="Left to Right"
targetPage="homepage"
transitionType="slide">
</xe:moveTo>
</xp:this.action>
</xp:eventHandler>
</xp:image>
</xp:panel>
Take a page from Bootstrap and see if Font-Awesome has an icon that would suite you. Then simply add it to your button with the respective CSS class
I'm working on an XPage for mobile users for an existing application. I want to use the mobile controls from the ExtLib for this.
I've build an XPage with a SinglePageApp and some AppPages on it. Now I want to build an AppPage with a search menu, a simple inputText and a button to start the search.
The inputText is bind to a sessionScope variable. I want to use the variable in a second appPage to get the search value and show a filtered/searched view.
The problem is the button. I'm not sure how to build this button to move to the other appPage. This is my last attempt, which fails with a runtime error.
Any idea how to get this running or what's wrong ?
Kind regards
Ingo
<xe:appPage id="appPage34" pageName="searchPage" preload="true" resetContent="true">
<xe:djxmHeading id="djxmHeading34" label="Search..." back="Home" moveTo="home">
</xe:djxmHeading>
<xp:inputText id="searchInput" value="#{sessionScope.searchValue}">
</xp:inputText>
<xp:button value="Search" id="button1" refreshMode="complete" type="submit">
<xp:eventHandler event="onclick" submit="true">
<xp:this.action>
<xe:moveTo targetPage="#searchResult"></xe:moveTo>
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xe:appPage>
What does the runtime error say?
If you can not get to the XPages log files on the server directly, then use XPages Log File Reader from OpenNTF to get easy access to the log files from a browser.
Does it work if you use "searchResult" as the value for targetPage (assuming the appPage is called "searchResult")?
I've now come to a solution with a static line item instead of a button, since I couldn't get the button working. I think it has something to do with the way the single page application handles the access to mobile pages.
The search page looks like this :
<xe:appPage id="appPage34" pageName="searchPage" preload="true" resetContent="true">
<xe:djxmHeading id="djxmHeading34" label="Search..." back="Home" moveTo="home">
</xe:djxmHeading>
<xp:inputText id="searchInput" value="#{sessionScope.searchValue}">
<xp:eventHandler event="onblur" submit="true" refreshMode="complete">
<xp:this.action>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:var svalue = getComponent("searchInput").getValue(); sessionScope.put("searchValue",svalue);}]]></xp:this.script>
</xp:executeScript>
</xp:this.action>
</xp:eventHandler>
</xp:inputText>
<xp:br></xp:br>
<xp:br></xp:br>
<xe:djxmLineItem id="djxmLineItem41" label="Start Search..." moveTo="#searchResult">
</xe:djxmLineItem>
</xe:appPage>
In the mobile page for showing the search result I simply get the value of the sessionScope variable, issue a full text search with the value and use the resulting document collection in a repeat control.
I still would rather use a button because the static line item is not really what a user expects as a gui element to start the search. But at least this is working.