Below is a simple Xpage with a radiobuttongroup that is bound to a sessionScope variable. The onclick event of the radio button group sets the computed field. There is an afterPageLoad event to initialize the session scope variable.
All works great. However, if I clear the session Scope variables (using the debug custom control) then the onclick event never fires. The user can click on the radio button all they want and the computed field never changes until the page is reloaded by pressing F5. Below is my page source. Any suggestions on how to get the onclick event to fire? This would be a big usability issue if the user let the session variables time out and then starting clicking on the radio button.
Howard
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.afterPageLoad><![CDATA[#{javascript:print("starting the afterPageLoad event");
if (sessionScope.init != true|| sessionScope.radioData == null){
//setup default values'
print("initing the value");
sessionScope.init = true;
sessionScope.radioData = "Red";
}
}]]></xp:this.afterPageLoad>
<xp:radioGroup id="radioButtons" value="#{sessionScope.radioData}">
<xp:selectItem itemLabel="Red"></xp:selectItem>
<xp:selectItem itemLabel="Green"></xp:selectItem>
<xp:selectItem itemLabel="Blue"></xp:selectItem>
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="computedField1">
<xp:this.action><![CDATA[#{javascript:print("starting onclick event");
if (sessionScope.init != true){
print("reload the page");
context.reloadPage();
}
var radioval = getComponent("radioButtons").getValue();
getComponent("computedField1").setValue(radioval); }]]></xp:this.action>
</xp:eventHandler></xp:radioGroup>
<xp:text escape="true" id="computedField1"></xp:text></xp:view>
I've seen this same behavior several times in my own xpage apps, and I've found that the problem happens only for partial page refreshes where the target refreshId is not large enough in scope to reload all of the data needed by the control. When I change the refreshId to the page's main div (or use full page refresh), then all works as expected.
I think that it's the call to location.reload in the client-side code which is actually fixing your issue. I'd be interested to learn if increasing the scope of refreshId also fixes the problem.
Related
is it possible to call a Dialog component in typeAhead functionality?
What i want to do is If the user enter a word which does not come up with typeAhead function then a dialog should appear on the screen.
Please find my code below (my dialog is in custom control)
Regards
Cumhur Ata
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">
<xe:dialog id="dialogConfirmation">
<div class="modal-body">
<p>You have entered a word which is not in the list</p>
<p class="text-warning">Please add/or cancel<small> </small></p>
</div>
<xe:dialogButtonBar id="dialogButtonBar1">
<xp:button value="Hayır" id="btnConfirmYes">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[XSP.closeDialog('#{id:dialogConfirmation}')]]></xp:this.script>
</xp:eventHandler></xp:button>
<xp:button value="Evet" id="btnConfirmNo" styleClass="btn-primary">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:openNewFrm(sessionScope.extDbPath,sessionScope.expDbPage)}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xe:dialogButtonBar>
<xe:this.title><![CDATA[#{javascript:var c = "Warning";return c;}]]></xe:this.title></xe:dialog></xp:view>
You can hijack the result of the typeahead and call your dialog if it's empty by using this snippet:
https://openntf.org/XSnippets.nsf/snippet.xsp?id=typeahead-onstartoncomplete-event
I don't have Domino Designer at hand right now so I can't try this out. So this is just guessing:
I don't think that you can use the standard Edit Box control's type ahead feature for something like that; instead you could program this yourself using the control's "onkeyup" event, mimicking the standard type ahead. the idea is that a classic type-ahead operates as a filter to a list of possible entries, just like a "getElementsByKey" method that is fired upon each key stroke. So with every event you check what's in the Edit Box so far, compare it to your list, then display the filtered result to the user in a pop-up (you may want to use a dojo tooltip here).
If the result is null you can bring up your dialog instead.
The scenario:
Clicking on a link, I'm accessing an xpage, let say: start.xsp.
I've added the following script to this .xsp:
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
XSP.addOnLoad(function(){
XSP.openDialog("#{id:dialogSearch}")
});
]]></xp:this.value>
</xp:scriptBlock>
So, every time this .xsp is loaded, a dialog is showing up. The problem is the current start.xsp is being refreshed/loaded many times, considering the fact that the xpage have numerous fields/controls on it. It is uncomfortable every time this start.xsp is loaded / refreshed the dialog to show up.
Is it possible to show the dialog after the start.xsp is loaded but ONLY when the above link was clicked? I do want firstly to go to that xpage and after this to show the respective dialog.
Thanks for your time.
Set a session scope variable in your link
<xp:link
escape="true"
text="Link"
id="link1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
sessionScope.showDialogSearch = "yes";
context.redirectToPage("start.xsp")
}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
Add a rendered attribute to your start.xsp's xp:scriptBlock
<xp:this.rendered><![CDATA[#{javascript:
var show = sessionScope.showDialogSearch;
sessionScope.remove("showDialogSearch");
return show
}]]></xp:this.rendered>
This way dialog is only be shown if start.xsp was executed from your link and only once because it gets deleted at first use in rendered attribute.
to perform a fulltext search users want to simply enter their query into a simple inputText. Then as soon as they hit ENTER the search itself should kick in.
Currently we tried to solve it like this:
the inputText is bound to a sessionScope variable myQuery
the input also has an OpenPage action bound to its onchange event
the page that is to be opened contains a viewPanel with a search filter bound to our sessionScope variable, as well as some more FT filtering fields.
This works fine in Firefox and Chrome but not in IE; obviously IE isn't recognizing the ENTER key as an onchange-trigger.
So I tried to record and analyze the keystrokes using the control's onkeyup event using something like
var q=sessionScope.get("myQuery");
return q.charCodeAt(q.length-1);
Works fine for all standard characters, but not for the ENTER key (where I would have expected to receive code 13).
I currently do have some kind of workaround using CSJS code in the control's onkeyup event as in:
if(event.keyCode===13){
var p=location.pathname.split("/");
p.pop();
location.replace(p.join("/") + "/search.xsp");
}
But this has some side effects which has some potential to make things more complicated, and it feels a bit like some hack. So I'd prefer to solve it using server side scripting.
Question is:
is there a way to capture an ENTER key stroke so that we can react to it?
or are we maybe on a completely wrong track here?
In IE the onchange event is not fired until the input loses focus: http://komlenic.com/231/internet-explorer-onchange-on-enter/
So you could use some CSJS to catch the ENTER press in IE, and drop focus from the input using .blur() method, which will in turn trigger the onchange event
A quick example I tried out that seems to work:
<xp:inputText id="inputText2" value="#{document1.text1}">
<xp:eventHandler event="onkeyup" submit="false">
<xp:this.script><![CDATA[
var kc = thisEvent.keyCode?thisEvent.keyCode:"";
if(kc != "" && kc == "13") {
var input = dojo.byId("#{id:inputText2}");
input.blur();
}]]>
</xp:this.script>
</xp:eventHandler>
<xp:eventHandler event="onchange" submit="true" refreshMode="complete">
<xp:this.action>
<xp:openPage name="/formInput.xsp"></xp:openPage>
</xp:this.action>
</xp:eventHandler>
</xp:inputText>
I have the following test XPage.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:panel id="pagePanel">
<xp:text escape="true" id="didThePageCompile">
<xp:this.value><![CDATA[#{javascript:var d = new Date();
return d.toLocaleString();}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="dialog1"
onStart="XSP.openDialog('#{id:dialog1}')"
onComplete="XSP.closeDialog('#{id:dialog1}')">
<xp:this.action><![CDATA[#{javascript:var agent = database.getAgent("runLongTime");
var response = agent.run();
// var d = getComponent("dialog1");
// d.show();
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xe:dialog id="dialog1" title="Test Dialog">This is a test dialog</xe:dialog></xp:panel>
</xp:view>
The agent "runLongTime" just sleeps for 10 seconds. This works fine. When I click the button however the dialog box does not show up. I checked the source and it generates the correct code, and that code works when I manually put it into the console.
I don't get any errors and the agent executes fine. I've also tried changing the refreshId to "pagePanel", but still the same.
XSP.openDialog() and XSP.closeDialog() each trigger a partial refresh. The XPages client-side API includes logic for preventing multiple partial refresh operations from executing in parallel, which is likely preventing your dialog from displaying because by the time it attempts to run the refresh to show the dialog, it's already running your button event.
Add a JSON-RPC (called "Remote Services" in the control palette) to the page. Move your button's server event code to a method of the RPC. You can then change the button event to be purely client-side: call XSP.openDialog(), then call the RPC method and close the dialog in the onComplete of that method. This should prevent the race condition you're currently experiencing.
as far as I can see here, you are trying to open/close the same dialog.
Some days ago I got the same issue and wondered why this was not working.
Finally I checked the events and ended with the spectacular result that onStart and onComplete fired up almost parallel.
Maybe you could try to insert a timeout (window.timeout) before calling the XSP.closeDialog event.
Here is what I want to do. I have a Custom Control with a button. If I drop it on a page, I want the end user programmer to define what happens when the button is pushed.
I had three ideas on how to accomplish this but don't know if they are possible of of course would not know how to do it.
Pass a function through a custom property. The button would call that custom property as a function. Is this possible? If so how would I do it? I tried it but it did not seem to work.
compositeData.ssjs_clickOK("Hello World");
Define a custom event for the custom control. Is this possible? How would it be done.
Have the button call a unique function name that would need to be defined by the user programmer. Seems a little messy but it might work. How can my button code see if the function has been defined? Would if (MySpecialFunction != null) work?
I am using the following in one of my applications to pass a function to a custom control:
1) In the custom control, add a property that will hold the function to be executed in our action button. I'll call mine 'querySave'. The property type must be 'javax.faces.el.MethodBinding'. The editor must be 'Method Binding Editor'.
2). Here is the code behind the action button in the custom control for this example:
if (compositeData.querySave) if (!compositeData.querySave.call()) return;
currentDocument.save();
This says: If there is a function defined in the property 'querySave', call it. If the function returns false, don't save the document.
3) Define a SSJS function that does what you need the action button to do. I normally place mine in a SSJS library. Make sure that the XPage has access to the function.
4) In the XPage that contains this control, use the editor for the property we created in step 1 (querySave in this example) and enter the name of the function that you created in step 3. IMPORTANT: Do not add the parenthesis or parameters when you enter the function name - if you do, the function will be executed at load time rather than when you click the action button. Also, don't add any code directly to the editor, just the function name. Any code in this editor will also execute at load time.
Thanks to Bill Hanson for the answer on Experts Exchange.
--
Update:
Here's a concrete example of such a custom control with a custom property where the SSJS function in question is called validateDocument:
<xc:component_buttons>
<xc:this.validateFunctionName><![CDATA[#{javascript:validateDocument}]]></xc:this.validateFunctionName>
</xc:component_buttons>
And here is an example of the button in the custom control that calls the function:
<xp:button id="submit" value="Save">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:this.condition><![CDATA[#{javascript:
if (compositeData.validateFunctionName) {
compositeData.validateFunctionName.call();
}}]]>
</xp:this.condition>
<xp:save></xp:save>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
Have a look at this question: Pass javascript code to Custom Control
My answer describes how to pass SSJS code from a custom control via a method binding.
EDIT:
Create a custom control "ccSSJS"
Add a property ssjsCode to your CC
Type: javax.faces.el.MethodBinding
Editor: Method Binding Editor
This is the source of the CC
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:
var args = new Array();
args["abc"] = "123";
args["cde"] = "456";
compositeData.ssjsCode.invoke( facesContext, null );
}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:view>
Create a XPage and add the Custom Control
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<xc:ccSSJS>
<xc:this.ssjsCode>
<![CDATA[#{javascript:
var app = facesContext.getApplication();
app.createMethodBinding("#{javascript:print( args['abc'] )}", null);
}]]>
</xc:this.ssjsCode>
</xc:ccSSJS>
</xp:view>
If you click the button in the custom control, the method ssjsCode will be invoked.
Define an arbitrary event handler anywhere on the page containing the custom control, and be sure to give it an id:
<xp:eventHandler id="customEvent" event="customEvent">
<xp:this.action>
<![CDATA[#{javascript://your custom code}]]>
</xp:this.action>
</xp:eventHandler>
You can then pass the method binding associated with that event handler into a custom property of the custom control that accepts a MethodBinding (or "object"):
getComponent("customEvent").getAction();
Then, from inside your custom control, you can call the method binding directly:
var customMethod = compositeData.get("customEvent");
var customArguments = compositeData.get("customEventArguments");
customMethod.invoke(facesContext, customArguments);
Make sure that the "customEventArguments" property for your custom control is set to allow multiple... this forces the property to be treated as an array, which the invoke method expects.
Here's a fourth option. Don't include the button on your Custom Control. Instead, add an Editable Area, with id="actionButton". You can tell the developer they need to add a button and where by using something like this in the Design Definition of the Custom Control:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" style="background-color:#4a4a4a;color:#fff;">
Add a button to the facet below.<br/>
<xp:callback facetName="actionButton" id="callback1"></xp:callback>
</xp:view>
The inclusion of xp:callback on the Design Definition will give a drop area at runtime for the developer to add the button. (The button may need to be in a Panel or other Container Control, I'm not sure). If you already have other Editable Areas on your Customer Control, youo'll need to include those in the Design Definition as well.