I want to create a button that executes different actions/scripts in a particular sequence.
First, I want to save the current document (server side action). After saving the document, a partial refresh is performed. Then a client side script should be carried out. When finishing this script, a new page is opened.
My current button:
<xp:button id="saveButton" value="Save">
<xp:this.rendered><![CDATA[#{javascript:if(document1.isEditable()){
return true;
}
else{
return false
}
}]]></xp:this.rendered>
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" immediate="false" save="true" refreshId="saveButton">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument var="document1"></xp:saveDocument>
<xp:openPage name="/viewer.xsp"
target="openDocument">
</xp:openPage>
</xp:actionGroup>
</xp:this.action>
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[
someScript();
]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script>
</xp:eventHandler>
</xp:button>
Currently, this button first executes all server side events and then the client script. Is there a solution to arrange the order of execution, so that I can execute server->client->server?
EDITED:
The functionality I want, splitted in three buttons:
<xp:button value="Label" id="execute1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="execute2">
<xp:this.action>
<xp:saveDocument var="document1"></xp:saveDocument>
</xp:this.action></xp:eventHandler></xp:button>
<xp:button value="Label" id="execute2"><xp:eventHandler event="onclick" submit="false">
<xp:this.script>
<xp:executeClientScript>
<xp:this.script><![CDATA[
someScript();
}});]]></xp:this.script>
</xp:executeClientScript>
</xp:this.script></xp:eventHandler></xp:button>
<xp:button value="Label" id="execute3"><xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:openPage name="/viewer.xsp" target="openDocument"></xp:openPage>
</xp:this.action></xp:eventHandler></xp:button>
So I would click execute1 => execute 2 => execute 3
The order of operation is:
run client-side (JavaScript) code defined in the action on the browser
submit the page to the server
JSF lifecycle kicks in and, after processing the browser content, server-side code runs
XPage is recalculated
HTML is passed back to the browser to be rendered
Adding JavaScript into the tag of the button won't achieve what you need, because it can't.
What you're looking for is to pass JavaScript back to the page to be run after everything else has happened. This is what view.postScript(String) will do for you - it literally posts script to run after (post) the HTML has been returned to the browser. Because this runs from the view object, this is server-side code, and passes a String that will be parsed by the browser as JavaScript.
Apart from view.postScript, as Paul suggests, you can add client-side JavaScript to onComplete for the event. This will run after the event is finished. I'm not certain if there is a big difference behind the scenes in regards to view.postScript vs onComplete.
You could create a hidden button (style="display: none;") that runs the second server side code. and trigger the click event for that after the first server event is complete.
Related
Is it possible to change the text on an <xp:button> element when the code under the onclick event is running (and the partial refresh is not completed)?
The XPages way is to use the onStart and onComplete properties (there's also onError):
<xp:button value="Search" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial"
onStart="changeText()" onComplete="unchangeText()">
<xp:this.action><![CDATA[#{javascript:serverFunction();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
You can pass the id of the button to the functions using "#{id:button1}" and use it to retrieve the button in CSJS.
We have something like that in place for our xp:button elements. We change the text and also add an FontAwesome "spinning wheel" until code is finished. At the start of our funtion we change text and icon, after code is finished - we revert to original xp:button text.
We use jQuery to modify the button element, basically you can to that with plain JavaScript (innerHTML) or dojo as well.
<xp:button value="Save" id="button-save" xp:key="submitSave">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[submitSaveFunction();]]></xp:this.script>
</xp:eventHandler>
</xp:button>
submitSaveFunction {
$("button[id$=button-save]").attr("disabled", "true").addClass("disabled").html('<i class="fa fa-spinner fa-pulse fa-fw"></i> Saving');
.....your code.....
$("button[id$=button-save]").removeAttr("disabled").removeClass("disabled").html('Save');
}
In my code I have the following Event Handler
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" disableValidators="true"
refreshId="#{javascript:compositeData.refreshid}">
<xp:this.script><![CDATA[
alert('clicked');
]]></xp:this.script>
<xp:this.action><![CDATA[#{javascript:print("jkafkjsfjk");}]]></xp:this.action>
</xp:eventHandler>
Unfortunately, I never get jkafkjsfjk printed in the console while CSJS code works fine and there's clicked alert pops up.
I know that the most common reason of not executing it has to do with some required fields equal to null or empty.
However, I've commented everything out but the code isn't executed anyway..
How do I resolve that and what's the reason?
Thanks in advance.
A couple of things...
First, if your CSJS returns false then the SSJS never runs (which is handy when you want the user to confirm...). I guess you should be alright here - but you could return true to be safe....
Second, partial refresh's, refresh ids, and partial executions can do things that may not be what you expect. So for the test I would just do a complete refresh and skip the id.
What kind of object is the eventhandler set on?
I normally use these kind of event handlers for links or buttons like:
<xp:button value="Add zone" id="button2" styleClass="btn-xs btn-primary">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="fields">
<xp:this.action><![CDATA[#{javascript:AssociationAdmin.createZone()}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
And this works fine :-)
Here is an example with a confirmation first:
<xp:link escape="true" text="Delete" id="link5" styleClass="action" rendered="#{javascript:!AssociationAdmin.isCatchInfoInUse(row.getKey())}">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete" id="eventHandler5">
<xp:this.action><![CDATA[#{javascript:AssociationAdmin.removeCatchInfo(row.key);}]]></xp:this.action>
<xp:this.script><![CDATA[return window.confirm("Are you sure you want to delete these fields: #{javascript:row.getName()}?\n\nDeletion cannot be undone!!")]]></xp:this.script>
</xp:eventHandler>
</xp:link>
Further notes:
You may want to submit without validating. You can do this by adding disableValidators="true" to your eventhandler
Please remember to have an id on the control where you have your eventhandler. Just wasted an evening and morning with the most strange behaviour cased by the button opening dialog did NOT have and id attributee...
Hope these samples will get you going :-)
/John
In an xp:dialog I have a button which loads back-end data and closes the dialog.
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick"
submit="true" refreshMode="partial" refreshId="rptContainer">
<xp:this.action><![CDATA[#{javascript:
//load data. will not add the code for that but works fine;
viewScope.put("customers",promoBean.getCampaign().getCustomers());
getComponent("dlgCampaign").hide();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
On the event I have set partial refresh on a xp:panel control that contains a repeat control that is data-binded to viewScope.
<xp:panel id="rptContainer">
<xp:repeat id="rptCustomers" var="obj" indexVar="index" value="#{viewScope.customers}">...
With help of the xpages debug toolbar I see that the viewScope from the button in the dialog is being updated. but when the dialog is closed the changes are not visible in the repeat control.
I have placed a button outside the dialogbox which only performs the partial refresh and then the repeat control renders the changes.
Can anyone explain me how I can update the repeat control from within the dialog box?
I tried the onHide property of the dialog and used as code:
XSP.partialRefreshGet('#{id:rptContainer}');
But this has no effect.
I even tried a full update for the button event but this has also no result.
Can someone guide me how to do it the proper way?
when I close a dialogbox through SSJS, I close it using following syntax:
<xp:button value="OK" id="btnOK">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:/*your processing code*/
var dialogBox = getComponent('/*id of the dialog to close*/');
dialogBox.hide('/*id of the component to refresh*/');}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Although the refresh mode is 'complete', only the component in the 'hide' method is refreshed.
Ok, to be honest: the headline doesn't tell the whole truth. I am using a custom control with several buttons (save, close, edit etc.) and a button that executes a POI action - it's generates a Word file.
I experience a problem here: after hitting the POI-button my other buttons (and the POI one as well) won't work anymore, no click is triggered.
Now the strange one: after waiting a few seconds (depending on which browser I use, Chrome is fastest) I am able to click the buttons (all of them) again so that I can e.g. leave (close) or edit my Xpage.
I don't know if that helps but here is the event code of my POI button:
<xp:button id="button5" styleClass="btn btn-sm printbutton"
style="display:none">
<i class="fa fa-print"></i>
<xp:text>
<xp:this.value><![CDATA[#{javascript:"Nach MS Word exportieren"}]]></xp:this.value>
</xp:text>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp_1:generateDocument documentId="wordprint"
loaded="true">
</xp_1:generateDocument>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Any ideas how to avoid this behavior or is it a bug in POI 4 XPages?
BTW: I am not able to execute another action right after getting the Word document, e.g. execute a script or open a page. That would be also an acceptable way to close the xpages after "printing".
Thanks in advance!
Oliver, add the client-side JS call XSP.allowSubmit() to your button in order to allow new submits after pressing the button:
<xp:button id="button5" styleClass="btn btn-sm printbutton" style="display:none">
<i class="fa fa-print"></i>
<xp:text>
<xp:this.value><![CDATA[#{javascript:"Nach MS Word exportieren"}]]></xp:this.value>
</xp:text>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp_1:generateDocument documentId="wordprint"
loaded="true">
</xp_1:generateDocument>
</xp:actionGroup>
</xp:this.action>
<xp:this.script><![CDATA[XSP.allowSubmit()]]></xp:this.script>
</xp:eventHandler>
</xp:button>
I believe the issue is you're generating the word document from the button on the same page.
Do the generation from an XAgent. Have the button call the XPage containing the XAgent and you should be fine.
I would like my "save" button to make a change in the document, to save the document , to give a confirmation message, and to refresh.
When using the following code on an iPhone I have to click 2 times before getting the confirmation message and the document doesn't refresh. When using the same code in Google chrome I get the confirmation message after 1 click, but the document or page also doesn't refresh. What's wrong?
<xp:button value="Save" id="button1" style="mybutton;font-size:14pt;font-family:Arial">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script>
<![CDATA[#{javascript:value1 = viewScope.get("client");
document1.replaceItemValue("visNrClient",value1);}]]>
</xp:this.script>
</xp:executeScript>
<xp:saveDocument var="document1"></xp:saveDocument>
<xp:confirm message="SAVED !!"></xp:confirm>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Are you moving from the same component in both cases? If you have onblur or onchange code on the component you were in, if that triggers a partial refresh, that will run and take precedence over the button click event, which would explain the need to click twice.