When I enable field length validation it validates only when I submit the form...
<?xml version="1.0" encoding="UTF-8"?>
<xp:inputText id="inputText1">
<xp:this.validators>
<xp:validateLength maximum="20"
message="You can input max 20 characters">
</xp:validateLength>
</xp:this.validators>
</xp:inputText>
<xp:message id="message1" for="inputText1"></xp:message>
so how to show the error message once user typed in 21st character? Assume need a code for CSJS...
Add an client side event handler "onkeyup" to your inputText control:
<xp:eventHandler
event="onkeyup"
submit="false">
<xp:this.script><![CDATA[
thisEvent.srcElement.value = thisEvent.srcElement.value.substring(0, 20)
]]></xp:this.script>
</xp:eventHandler>
It shortens the text to max length if necessary.
You can show the message as alert
var element = thisEvent.srcElement;
if (element.value.length > 20) {
element.value = element.value.substring(0, 20);
alert("You can input max 20 characters");
}
or you can add a client message text field to your XPages and fill it with the message whenever the max length was reached
<xp:inputText id="inputText1">
<xp:this.validators>
<xp:validateLength maximum="20"
message="You can input max 20 characters">
</xp:validateLength>
</xp:this.validators>
<xp:eventHandler
event="onkeyup"
submit="false">
<xp:this.script><![CDATA[
var element = thisEvent.srcElement;
var message = document.getElementById("#{id:clientMessage}");
if (element.value.length > 20) {
element.value = element.value.substring(0, 20);
message.innerHTML = "You can input max 20 characters";
} else {
message.innerHTML = "";
}
]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
<xp:text id="clientMessage" value="" />
Related
I am binding all of my components to a java source. One field is a radio button, and if the user selects "Other" as a value, then I must show an field labeled "Other" for the user to fill in.
I cannot use SJSS to get the value of the radio button as it doesn't exist yet. But then how do I found out what the user selected? In the change event should I set a viewScope var and refresh the secondary area and check for the value of the viewScope var?
<xc:cc_CommonFormField
id="cc_CommonFormField14"
label="Savings"
placeholder="">
<xp:this.facets>
<xp:radioGroup
xp:key="field"
id="radioGroup1"
value="#{doc.prjSav}">
<xp:selectItem
itemLabel="Financial"
itemValue="Financial">
</xp:selectItem>
<xp:selectItem
itemLabel="Safety"
itemValue="Safety">
</xp:selectItem>
<xp:eventHandler
event="onchange"
submit="true"
refreshMode="partial" refreshId="refresh1">
</xp:eventHandler>
</xp:radioGroup>
</xp:this.facets>
<div id = "refresh1">
<div id = "innerRefresh"
rendered="#{javascript:what to put here}">
</xc:cc_CommonFormField>
<xc:cc_CommonFormField placeholder="Enter Other Reason...">
<xp:this.facets>
<xp:inputText
id="inputText3"
xp:key="field"
value="#{doc.prjOther}">
</xp:inputText>
</xp:this.facets>
</xc:cc_CommonFormField>
</div>
</div>
Here is the code that is correct:
<xc:cc_CommonFormField
id="cc_CommonFormField14"
label="Savings and/or Revenue From the Project"
placeholder="">
<xp:this.facets>
<xp:radioGroup
xp:key="field"
id="radioGroup1"
value="#{doc.prjSav}">
<xp:selectItem
itemLabel="Financial"
itemValue="Financial">
</xp:selectItem>
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="norefresh">
<xp:this.script><![CDATA[var result=null;
for(var i=0; i<document.forms[0].elements.length; i++){
if(document.forms[0].elements[i].name=="#{id:radioGroup1}" ){
if(document.forms[0].elements[i].checked == true){
result=document.forms[0].elements[i].value;
break; //remove this if for check box groups and collect multiple values above instead
}}}
var sel = x$("#{id:innerRefresh}");
if (result == "Other")
{
x$("#{id:innerRefresh}").removeClass("scoHidden");
x$("#{id:innerRefresh}").addClass("scoVisible");
}
else
{
x$("#{id:innerRefresh}").removeClass("scoVisible")
x$("#{id:innerRefresh}").addClass("scoHidden");
}
XSP.partialRefreshGet("#{id:innerRefresh}")]]></xp:this.script>
</xp:eventHandler></xp:radioGroup>
</xp:this.facets>
</xc:cc_CommonFormField>
<div id="refresh1">
<xp:div id="innerRefresh" styleClass="scoHidden">
<xc:cc_CommonFormField placeholder="Enter Other Reason...">
<xp:this.facets>
<xp:inputText
id="inputText3"
xp:key="field"
value="#{doc.prjOther}">
</xp:inputText>
</xp:this.facets>
</xc:cc_CommonFormField>
There are 2 approaches to solve this:
- server side rendering using SSJS
- client side rendering using JS
When you opt for the former, you have to submit your data (partial including the hidden div) to recompute your rendered property.
Client side: you render the div in any case, but give it a class attribute that maps to CSS display:none.
In your client side JS you add an onChange handler to the radio buttons and change the class to something visible (and back if others are unselected). This saves you from a server round trip.
Only caveat: (best handled in your Java) you need to dismiss an eventually entered value in the field for other if other wasn't selected.
My Xpage is in read mode. There is a Notes field with a "Add Notes" button. Click the button, user adds notes in dialogbox and clicks "Add Notes". The button does the work of adding the notes and saving the document.
I want to update the underlying notes field so the user will see their additions. How can I accomplish this?
<?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="pnlAll">
<xp:this.data>
<xe:objectData saveObject="#{javascript:PCModel.save()}"
var="PCModel">
<xe:this.createObject><![CDATA[#{javascript:var pc = new com.scoular.model.PC();
var unid = sessionScope.get("key");
if (unid != null) {
pc.loadByUnid(unid);
sessionScope.put("key","");
viewScope.put("readOnly","Yes");
} else {
pc.create();
viewScope.put("readOnly","No");
}
viewScope.status = pc.status;
return pc;}]]></xe:this.createObject>
</xe:objectData>
</xp:this.data>
<xp:inputTextarea id="inputTextarea3" cols="1" rows="10"
disabled="true" value="#{PCModel.notes}">
</xp:inputTextarea>
<xp:br></xp:br>
<xp:button value="Add Notes" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:getComponent("dialogAddNotes").show()}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br>
<xp:div styleClass="modal fade" id="model1" role="dialog">
<xe:dialog id="dialogAddNotes" styleClass="modal-dialog"
title="Add Notes">
<xp:div style="margin-left:10.0px;margin-right:10.0px">
<xp:inputTextarea id="inputTextarea4" value="#{viewScope.addNotes}"
cols="1" rows="18">
</xp:inputTextarea>
</xp:div>
<xp:div styleClass="modal-footer">
<xp:button type="button" styleClass="btn btn-primary" id="button17"
value="Add Notes">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="dialogAddNotes">
<xp:this.action><![CDATA[#{javascript:var newLne = "\n";
var oldNte = PCModel.notes;
var usrNme:String = userBean.displayName;
var dte = session.createDateTime(#Now());
var lnkStr:String = "Notes added by " + usrNme + " at " + dte + newLne + newLne + viewScope.addNotes + newLne + newLne + oldNte;
var newNotes:String = lnkStr
PCModel.notes = newNotes;
PCModel.save();}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:div>
</xe:dialog>
</xp:div>
</xp:panel>
</xp:view>
You need to partially refresh the updated text area field called inputTextarea3 instead of the dialog.
I have a repeat control on an XPage displaying item summary. To view other details, the user clicks on a button (inside the repeat) which will set a viewScope variable to the rowIndex of the repeat and then open up a modal to display the remeaining item details.
I am unable to set the viewScope variable from the button. It's like it cannot get a handle to the rowIndex.
I have a computed text field set to display the rowIndex at the beginning of the repeat (which works fine).
I'm obviously missing something elementary. Any assistance would be appreciated.
Relevant source code follows:
<xp:repeat id="repeat1" rows="50" var="entry" indexVar="rowIndex">
<xp:this.value><![CDATA[#{javascript:var m = sessionScope.assetMap;
if(m!=null){
m.entrySet()
}}]]></xp:this.value>
<div>
<small>
<label>
<xp:text escape="true" id="num">
<xp:this.value><![CDATA[#{javascript:var itemNum:Number = parseInt(rowIndex + 1);
itemNum.toPrecision(0);}]]></xp:this.value>
</xp:text>
. Description:
</label>
<xp:text escape="true" id="desc">
<xp:this.value><![CDATA[#{javascript:#Word(entry.getValue(),"~",3);}]]> </xp:this.value>
</xp:text>
</small>
</div>
<br></br>
<div>
<xp:button type="button" value="Details ..." id="button2">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:try{var itemNum:Number = parseInt(rowIndex + 1);
viewScope.currentItem = itemNum.toPrecision(0);
}catch(e) {
requestScope.errstatus = e.toString();}}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
02/04/2015 Update:
I have isolated the problem to the rendered property of the custom control. The code works if I don't use the rendered property.
The code in the onCLientLoad of my XPage (CSJS) is:
$(window).on('load resize', function(){
var screenWidth = $(window).width();
var sDevice = '';
switch(true){
case (screenWidth < 768):
sDevice = 'mobile';
break;
case (screenWidth < 922):
sDevice = 'tablet';
break;
case (screenWidth >= 922):
sDevice = 'desktop'
}
XSP.partialRefreshPost( '#{id:pnlList}', {params: {'sDevice': sDevice}} );
});
And the source for my custom control in the XPage with the rendered property is:
<xp:panel rendered="#{javascript:param.sDevice == 'mobile'}">
<xc:ccSYS_AppLayoutReq_p xp:key="panelContentFacet">
<xp:this.facets>
<xc:cc_cartracking_p xp:key="contentFacet"></xc:cc_cartracking_p>
</xp:this.facets>
</xc:ccSYS_AppLayoutReq_p>
</xp:panel>
Thanks,
Dan
You have to set option "Set partial execution mode" execMode="partial" in your button.
Look for the whole answer at your colleague's question https://stackoverflow.com/a/28330481/2065611
I have the following dialog, the password field focus is set when the dialog has focus but this only works when first loaded, I want to set the password field focus when the passwords don't match (i.e. after clicking the OK button and running the SSJS).
<xe:dialog id="dialogConfirmPassword" title="Confirm Your Password">
<xe:dialogContent id="dialogConfirmPasswordContent">
<xp:messages id="messagesConfirmPassword" layout="table" />
<xp:table style="width:100%" id="tableConfirmPassword">
<xp:tr>
<xp:td>
<xp:label value="Password" id="lblPassword"
for="confirmPassword" />
</xp:td>
<xp:td>
<xp:inputText id="confirmPassword"
password="true">
</xp:inputText>
</xp:td>
</xp:tr>
</xp:table>
</xe:dialogContent>
<xe:dialogButtonBar id="dialogButtonBarConfirmPassword">
<xp:button value="OK" id="btnConfirmPasswordOk">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:try{
var confirmPassword:String = getComponent("confirmPassword").getValueAsString();
if (session.verifyPassword(confirmPassword, HTTPPassword)){
/* RUNS NOTES AGENT */
getComponent('dialogConfirmPassword').hide();
return true;
} else {
facesContext.addMessage("messagesConfirmPassword", new javax.faces.application.FacesMessage("You have entered an incorrect password, please try again.") );
/* WANT TO SET FOCUS FOR PASSWORD FIELD */
return false;
}
} catch (e) {
facesContext.addMessage("messagesConfirmPassword", new javax.faces.application.FacesMessage("ERROR! " + e.toString()) )
return false;
}}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xe:dialogButtonBar>
<xp:eventHandler event="onFocus" submit="false">
<xe:this.script><![CDATA[dijit.byId("dialogConfirmPassword").focus();]]></xe:this.script>
</xp:eventHandler>
</xe:dialog>
Can I set the password field focus after setting the facesContext.addMessage or another way?
Update: The following output script worked:
<xp:scriptBlock id="scriptBlockConfirmPassword">
<xp:this.value><![CDATA[#{javascript: if(viewScope.PWDSuccess === null) {
return "";
};
var result = "dojo.byId(\"";
result += getComponent("confirmPassword").getClientId(facesContext);
result += "\").focus();";
return result;}]]></xp:this.value>
</xp:scriptBlock>
A few pointers:
Don't go after components like getComponent("confirmPassword") but bind your component to a scope variable, makes better code
You can't directly run a Client JavaScript action in your SSJS (where you indicated)
Your event handler never works since the XPages ID is different from the clientside ID
A outputscript probably can solve your challenge
So modify your code (only essentials here):
<xp:inputText id="confirmPassword" password="true" value="#{viewScope.confirmPWD}">
</xp:inputText>
<xp:button value="OK" id="btnConfirmPasswordOk">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:executeScript>
<xp:this.script><![CDATA[#{javascript:try{
viewScope.PWDSuccess = session.verifyPassword(viewScope.confirmPWD, HTTPPassword);
if (viewScope.PWDSuccess){
/* RUNS NOTES AGENT */
getComponent('dialogConfirmPassword').hide();
} else {
facesContext.addMessage("messagesConfirmPassword", new javax.faces.application.FacesMessage("You have entered an incorrect password, please try again.") );
}
} catch (e) {
facesContext.addMessage("messagesConfirmPassword", new javax.faces.application.FacesMessage("ERROR! " + e.toString()) );
viewScope.PWDSuccess = false;
}
return viewScope.PWDSuccess
}]]></xp:this.script>
</xp:executeScript>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:outputScript>
<this.value><!CDATA[#{javascript:if(viewScope.PWDSuccess) {return "";};
var result = "dijit.byId(\"";
result += getComponent("confirmPassword").getClientId();
result += "\").focus();";
return result;
}]]</this.value>
</xp:outputScript>
(Typed off my head - will contain errors).
Let us know how it goes.
I am trying to change the display format of a date/time control based on a combo box selection. I thought it would be simple to do.
I am using a js if statement with getComponent instead of datasource.getValue as I thought it would grab the value before it is submitted. I receive an error that the component is null.
Can anyone explain why I am getting null here but in a computedField with getcomponent the value shows?
if (getComponent('comboBox1').getValue()==0)
{'both'}
else
{'date'}
The type of the converter is computed during page load, and during this it is not possible to access a component with getComponent method. Additionally, it is not possible to recompute the type of the converter this way. Event if you use the page load/dynamically trick, the type of the converter will not recomputed.
But you can change the type of the converter in the partial refresh:
<xp:comboBox id="comboBox1">
<xp:selectItem itemLabel="One" itemValue="1"></xp:selectItem>
<xp:selectItem itemLabel="Null" itemValue="0"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="inputText1">
<xp:this.action><![CDATA[#{javascript:
var cmp:com.ibm.xsp.component.xp.XspInputText = getComponent("inputText1");
var converter:com.ibm.xsp.convert.DateTimeConverter = cmp.getConverter();
var value = getComponent("comboBox1").getValue();
if( value == 0) {
converter.setType("both");
}else{
converter.setType("date");
}
}]]>
</xp:this.action>
</xp:eventHandler>
</xp:comboBox>
In this example, the datefield is inputText1 and looks like this:
<xp:inputText id="inputText1">
<xp:this.converter>
<xp:convertDateTime type="time" />
</xp:this.converter>
<xp:dateTimeHelper />
</xp:inputText>