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
Related
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.
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 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.
I have an inputText which is a required field on my Document.
<xp:inputText value="#{Cdoc.txt_NumeCompanie}" id="txt_NumeCompanie"
required="true" defaultValue="#{javascript:param.value}">
<xp:this.validators>
<xp:validateRequired message="Numele companiei este obligatoriu." loaded="true">
</xp:validateRequired>
</xp:this.validators>
<xp:eventHandler event="onchange" submit="false" disableValidators="true">
<xp:this.script><![CDATA[ XSP.partialRefreshPost("#{id:scrollDiv}", {
onComplete: function() {
XSP.partialRefreshPost("#{id:pers}");
}
});
]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
I also checked the Process data without validation ( from the Server tab ... ). But still no refresh is taking place.
You do not include the required second parameter on your second partialRefreshPost. Try this:
XSP.partialRefreshPost("#{id:scrollDiv}", {
onComplete: function() {
XSP.partialRefreshPost("#{id:pers}", {});
}
});
Update: you can use the onComplete event of the eventHandler to run your 2nd partial refresh. So use the traditional partial refresh of the first component and then run your 2nd partial refresh through the onComplete event:
<xp:inputText value="#{Cdoc.txt_NumeCompanie}" id="txt_NumeCompanie" required="true" defaultValue="#{javascript:param.value}">
<xp:this.validators>
<xp:validateRequired message="Numele companiei este obligatoriu." loaded="true">
</xp:validateRequired>
</xp:this.validators>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="scrollDiv">
<xp:this.onComplete><![CDATA[XSP.partialRefreshPost("#{id:pers}", {});]]></xp:this.onComplete>
</xp:eventHandler>
</xp:inputText>
By setting submit="false", you're preventing any server-side processing from being triggered by the eventHandler itself. That means disableValidators="true" is irrelevant, because the XPages lifecycle is not being processed from the eventHandler.
Instead the refresh is being generated from the client-side code, the XSP.partialRefreshPost. That doesn't have an option to disable validation, as covered here How to disable validators using the XSP.partialRefreshPost method?. disableValidators="true" doesn't and cannot influence the processing of the partialRefreshPost. So chances are your validation is still running. To confirm that, add an errors panel into the initial refresh area.
partialRefreshGet may work, I'm not sure.
My usual approach in these scenarios is to refresh a single area that comprises both areas you wish to refresh. Then set submit="true" and disableValidators="true". But bear in mind that even if validation is disabled, data conversion is still checked so if you enter a text value in a number field, the partial refresh would still fail.
Ok, first thing to do is open your toolbox and look at the network traffic. This will help you to understand what is going on. You can use FireBug in FireFox or Developer tools in Google Chrome.
What network traffic occurs for the above snippet?
Edit:
Still waiting to hear what network traffic is generated...
But just looking at your code... - try set submit="true". I had a look at some of the code I have, and I am not sure that the extra empty param is required :-)
Could you explain what you are trying to obtain?
Edit
Ok, I see what you want. I had a quick view in some code. I am not doing exactly the same that you are. However, when I nest the partial refreshes I typically do it this way:
<xp:button id="button1"
styleClass="btn btn-danger btn-lg#{FishingTripEdit.typeInput eq '1' ? ' inActive' : ''}">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial"
refreshId="inputTypeSelected" disableValidators="true">
<xp:this.action>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:MyBean.setData('0')}]]></xp:this.script>
</xp:executeScript>
</xp:this.action>
<xp:this.onComplete><![CDATA[XSP.partialRefreshGet("#{id:buttonTopHolder}")]]></xp:this.onComplete>
<xp:this.script><![CDATA[return document.getElementById("#{id:triggerAsk}").value!=='1' ? true : confirm("Sure?")]]></xp:this.script>
</xp:eventHandler>
</xp:button>
So for the "first" refresh I use the builtin markup - and onComplete I add the second as plain JavaScript. I know this is an example with a partialRefreshGet (which I prefer to use if at all possible due to less overhead). But perhaps it can give you an idea....
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.