I'm not sure if I am missing out a very simple thing. Normally, XPages maintain "aria-required" and "aria-invalid" attributes for validation.
However, for DateTime Picker (standard one), it's always aria-invalid="false".
Here is a simple test I have used in Domino 9.0.1:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument var="document1" formName="TestForm"></xp:dominoDocument>
</xp:this.data>
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="panel1">
</xp:eventHandler>
</xp:button>
<xp:panel id="panel1">
<xp:inputText id="inputText1" required="true">
<xp:dateTimeHelper id="dateTimeHelper1"></xp:dateTimeHelper>
<xp:this.converter>
<xp:convertDateTime type="date"></xp:convertDateTime>
</xp:this.converter>
</xp:inputText>
<xp:messages id="messages1"></xp:messages>
</xp:panel>
</xp:view>
Generated HTML before the button clicked contains:
<input type="text" aria-haspopup="true" role="textbox" data-dojo-attach-point="textbox,focusNode" autocomplete="off" class="dijitReset dijitInputInner" aria-invalid="false" tabindex="0" aria-required="true" id="view:_id1:inputText1" value="">
After clicking, I can see Messages component has been aggregated but aria-invalid is false.
<input type="text" aria-haspopup="true" role="textbox" data-dojo-attach-point="textbox,focusNode" autocomplete="off" class="dijitReset dijitInputInner" aria-invalid="false" tabindex="0" aria-required="true" id="view:_id1:inputText1" value="">
(I removed dojo stuff wrapping the input)
After some digging, I found that this problem is not related to XPages.
XPages provides the aria-invalid attribute correctly if validation fails. However, dojo controls automatically change it during rendering. So it's not possible unless you inject a correction into Dojo component rendering code.
Related
Been struggling with jQuery syntax. I am trying to setup some JS code to expand/collapse an accordion panel (http://www.w3schools.com/Bootstrap/bootstrap_collapse.asp). I can get it to expand and collapse with x$("#{id:collapsePanel2}").collapse("toggle"); That works fine but does not collapse the other panels. You can see this at https://www.netexperts.com/xpages1.nsf/collapse.xsp. There is a "parent" parameter which says it takes the selector. I have tried "#view:_id1:accordion" (the generated id of the parent panel), I have tried parent="#view:_id1:accordion" and a few other combinations. The docs at http://www.w3schools.com/Bootstrap/bootstrap_ref_js_collapse.asp under the .collapse options say the parent is a parameter that takes the parent selector but I can't seem to pass this in without throwing a JS error.
<?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:navbar
id="navbar1"
headingText="Collapse Panel"
pageWidth="full">
</xe:navbar>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:panel
styleClass="panel-group"
id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<xp:link
escape="true"
id="link1">
Collapsible panel
<xp:eventHandler
event="onclick"
submit="false">
<xp:this.script><![CDATA[
//function is called using x$("#{id:inputText1}", " parameters").
x$("#{id:collapsePanel}").collapse( "toggle");
function x$(idTag, param){ //Updated 18 Feb 2012
idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
return($("#"+idTag));
}
]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</h4>
</div>
<xp:panel
id="collapsePanel"
styleClass="panel-collapse collapse">
<div class="panel-body">Panel Body</div>
<div class="panel-footer">Panel Footer</div>
</xp:panel>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<xp:link
escape="true"
id="link2">
Collapsible panel
<xp:eventHandler
event="onclick"
submit="false">
<xp:this.script><![CDATA[
//function is called using x$("#{id:inputText1}", " parameters").
x$("#{id:collapsePanel2}").collapse("toggle");
function x$(idTag, param){ //Updated 18 Feb 2012
idTag=idTag.replace(/:/gi, "\\:")+(param ? param : "");
return($("#"+idTag));
}
]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</h4>
</div>
<xp:panel
id="collapsePanel2"
styleClass="panel-collapse collapse">
<div class="panel-body">Panel Body</div>
<div class="panel-footer">Panel Footer</div>
</xp:panel>
</div>
</xp:panel>
</xp:view>
You need to update the jquery something like this
$( "#link" ).on( "click", function(event) {
event.preventDefault();
$('#accordion .panel-collapse').collapse('hide');
$('#collapseTwo').collapse('toggle');
});
Working example http://bootsbin.com/bin/view/46/
I have a custom control with an editbox in a modal:
<xp:inputText id="inputText1"></xp:inputText>
In the onclick event of a button , I would like to read the value of this editbox
var demo = getComponent("inputText1").getValue();
Of course this doesn't work in a custom control, since he doesn't has a handle to inputText1. How can this be done ?
EDIT
Herewith I'm posting my 'whole' code.
Even with a scoped variable it isn't working .... :(
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:styleSheet href="bootstrap-modal.css"></xp:styleSheet>
<xp:script src="/bootstrap-modalmanager.js" clientSide="true"></xp:script>
<xp:script src="/bootstrap-modal.js" clientSide="true"></xp:script>
<xp:script src="/JQueryXSnippet.js" clientSide="true"></xp:script>
</xp:this.resources>
<div id="Modal1" class="modal fade" tabindex="-1" data-focus-on="input:first" style="display: none;">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal One</h4>
</div>
<div class="modal-body">
<p>Modal 1 </p>
<div class="form-group">
<label for="inputText1">First Name:</label>
<xp:inputText id="inputText1" value="#{viewScope.input1}">
<xp:this.attrs>
<xp:attr name="class" value="form-control"></xp:attr>
<xp:attr name="data-tabindex" value="1"></xp:attr>
</xp:this.attrs>
</xp:inputText>
</div>
<xp:button value="Demo" id="button2">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="demoPanel">
<xp:this.action><![CDATA[#{javascript:viewScope.message = "input1 = "+viewScope.input1;}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:panel id="demoPanel">
<xp:text escape="true" id="computedField1"
value="#{viewScope.message}">
</xp:text>
</xp:panel>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn btn-primary">Close</button>
</div>
</div>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
$(document).ready(function(){
x$("#{id:button1}").click(function(){
x$("#{id:Modal1}").modal(
{backdrop: true,
keyboard: false,
show: true
}
);
});
});
]]></xp:this.value>
</xp:scriptBlock>
<xp:button value="Login" id="button1" styleClass="btn btn-info btn-lg">
</xp:button>
</xp:view>
The advice from Tim Tripcony was to go to the model layer, not the component. As ever, he was spot on. Bind the Edit Box to something, probably in this scenario a viewScope variable. Then retrieve the value from that. It's likely to be more efficient than getComponent().
There are two ways that spring to mind are worth trying:
Add the button to the custom control and that way, it'll just work as is.
Set the id to be computed from a custom property of the control control-id and pass in a string e.g. my-made-up-id and then refer to it from any button out-with the custom control. Note the ${} binding is needed when computing the id.
Custom Control
<xp:inputText
id="${javascript:compositeData.control-id}">
</xp:inputText>
XPage
<xc:FieldTest control-id="my-made-up-id"></xc:FieldTest>
<xp:button
value="Get Value"
id="button1">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:var demo = getComponent("my-made-up-id").getValue();
print("Demo: " + demo);}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
In my dynamic view panel, I need to have sortable columns. Problem is when sorting a column it resubmits the XPage and whole content is refreshed. Is there a way to achieve sorting only on the dynamic view panel without submitting whole page? Also, I want to hide this control if selected view is empty.
Here is my code:
<xp:panel id="searchPanel">
<xp:table styleClass="table table-condensed" style="width:auto">
<xp:tr>
<xp:td style="border-top:0px" styleClass="text-nowrap" id="searchColumn">
<xp:inputText id="inputText1" value="#{viewScope.searchterm}" styleClass="form-control"></xp:inputText>
<xp:link escape="true" text="" id="searchLink">
<i class="fa fa-search" style="margin-left:5px"></i>
<xp:eventHandler event="onclick" refreshMode="partial" immediate="false" submit="true" refreshId="ddPanel">
<xp:this.action><![CDATA[#{javascript:var pager:com.ibm.xsp.component.xp.XspPager = getComponent("pager");
pager.gotoFirst();}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
<xp:link escape="true" text="" id="clearSearchLink">
<i class="fa fa-times" style="margin-left:5px"></i>
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="ddPanel" onComplete="showHideTableToggle()">
<xp:this.action><![CDATA[#{javascript:viewScope.remove("searchterm");}]]></xp:this.action>
<xp:this.script><![CDATA[dojo.byId(getID("inputText1")).value=""]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</xp:td>
<xp:td style="border-top:0px;padding-left:20px">
<xp:link escape="true" id="addButton" onclick="return false;" styleClass="btn btn-primary" style="color:rgb(255,255,255)" text="Add">
<i class="fa fa-plus" style="margin-right:5px"></i>
<xp:eventHandler event="onclick" submit="false" refreshMode="complete">
<xp:this.script><![CDATA[XSP.openDialog(getID('contentDialog'),'',{"docID":"","newDoc":"false"});]]></xp:this.script>
</xp:eventHandler>
</xp:link>
</xp:td>
</xp:tr>
</xp:table>
<xp:panel id="ddPanel">
<xe:dynamicViewPanel id="mainView" width="100%" dataTableStyleClass="table table-bordered table-condensed table-hover" rows="10" var="myView"
showColumnHeader="true" customizerBean="com.hcl.igdm.PickerViewBeanMainViews" partialRefresh="true" refreshId="ddPanel"
>
<xe:this.data>
<xp:dominoView var="mainDataView" dataCache="nodata">
<xp:this.viewName><![CDATA[#{javascript:sessionScope.showView.split("~")[0]}]]></xp:this.viewName>
</xp:dominoView>
</xe:this.data>
<xe:this.rowAttrs>
<xp:attr name="onClick">
<xp:this.value><![CDATA[#{javascript:return "javascript:myJSFunction();"}]]></xp:this.value>
</xp:attr>
</xe:this.rowAttrs>
</xe:dynamicViewPanel>
<xp:pager layout="Previous Group Next" partialRefresh="true" id="pager" for="mainView" title="Pager"></xp:pager>
</xp:panel>
It's a long time ago this question was placed... but maybe the answer can help other people.
You can put the dynamicViewPanel into a 'dynamic content' container (from ExtLib) with partial events attribute set to true. This will convert the full update events into partial refresh events.
<xe:dynamicContent partialEvents="true">
...
</xe:dynamicContent>
I have a problem with a pager performing a partial refresh (since we have server side redirection rules, full refresh is not an option here). If I place the pager on the same page rather than in a custom control then it refresh the main panel. But if I place the pager in a custom control, then it only updates the repeat control, which means the pager control itself doesn't get refreshed. Here is a simple example to demonstrate the problem.
<?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">
<xp:panel id="mainPanel">
<xc:ccPager pagerFor="repeat1" id="myPager" />
<xp:repeat id="repeat1" rows="3" var="row">
<xp:this.value><![CDATA[#{javascript:var cars = ["Saab", "Volvo", "BMW", "Saab", "Volvo", "BMW"];
return cars; }]]></xp:this.value>
<xp:text escape="true" id="computedField1" value="#{row}">
</xp:text>
</xp:repeat>
</xp:panel>
</xp:view>
And here is the code for the custom control:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:pager layout="Previous Group Next" partialRefresh="true"
id="pager1" for="#{compositeData.pagerFor}" partialExecute="false">
</xp:pager>
</xp:view>
Any tips would be welcome. In this case the pager is simple, but I have a more complex pager and a pagesizes control with 45 lines of source code, so I would prefer to create a re-useable custom component if possible at all.
UPDATE
My current pager custom control:
<div id="page-result" class="pagination pagination-small clearfix">
<div class="col-lg-12 pagination clearfix">
<div class="results form-inline left">
<fieldset>
<xp:pager id="pager1" alwaysCalculateLast="true"
for="#{compositeData.strPagerFor}" styleClass="pag-control"
partialRefresh="true" partialExecute="false">
<xp:pagerControl id="pagerControl1" type="FirstImage"
styleClass="firstPage" image="/PagerArrowhead2-L_d.png">
</xp:pagerControl>
<xp:pagerControl id="pagerControl2" type="PreviousImage"
styleClass="previousPage" image="/PagerArrowhead-L_d.png">
</xp:pagerControl>
<xp:pagerControl id="pagerControl5" type="Group"
currentStyleClass="active" styleClass="pages">
</xp:pagerControl>
<xp:pagerControl id="pagerControl3" type="NextImage"
styleClass="nextPage" image="/PagerArrowhead.png">
</xp:pagerControl>
<xp:pagerControl id="pagerControl4" type="LastImage"
styleClass="lastPage" image="/PagerArrowhead2.png">
</xp:pagerControl>
</xp:pager>
<label>
<xp:text escape="true" id="computedField1" value="#{compositeData.strLabelText}" />
</label>
</fieldset>
</div>
<div class="results form-inline">
<fieldset>
<label> Results per page: </label>
<xe:pagerSizes id="pagerSizes1" for="#{compositeData.strPagerFor}"
sizes="7|15|25|50|all" text="{0}" styleClass="resultPage"
partialRefresh="true" partialExecute="false"
refreshId="#{compositeData.strRefreshPanel}" />
</fieldset>
</div>
</div>
</div>
I've hit this problem before. I think it's because the view container cannot find the pager when it's in a separate custom control. I presume it's using something like getComponent, which can't navigate down into custom controls, so it can't locate the pager to update it.
Can you extract the code for your pagesizes control into a Java dataObject / bean / util or dataContext / SSJS library? Then reference it with one line of SSJS or EL?
The other option would be to use a plugin and build a component that extends the repeat control and adds the pager to the relevant header?
Tim Tripcony gave a great demo of a quick and dirty way of doing it http://www.notesin9.com/2012/04/04/notesin9-064-global-custom-controls-fixed/
I am looking to create an Xpage self registration site that registers users onto the Domino name and address book. I am just doing a proof of concept.
I will put the code below, but it is a fairly simple matter of capturing the user details, dropping their details and password into the NAB and then, hey presto the user should be able to immediately log into the app.nsf.
At the moment I am manually putting the user into a group that is listed in the ACL as manager on app.nsf (for testing, I am putting them in the group prior to creating the user - just mentioning it in case it is important).
It basically works, BUT, there is a rather large delay. As in, it takes many minutes and sometimes more. After some research I discovered the console command "show nlcache reset" and a lotusscript/java/javascript code version of it. But it seems to have no effect, either coded or manually from the console - (there is also no response from the console that the command has been initiated, just a new line, is this normal?).
The only quirky thing is that the OU=99123456789 (or something similar, it is a company identifier). So a user will look something like this Fred Citizen/99123456789/Domain (don't think this should matter). The user will however log in as "Fred Citizen" and password.
Any ideas?
We are running 9.0.1
Thanks in advance.
Cheers
Damien
Code Below:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.data>
<xp:dominoDocument var="userreg" databaseName="names.nsf"
formName="Person">
</xp:dominoDocument>
</xp:this.data>
<fieldset class="userreg">
<label for="FirstName">First Name</label>
<xp:inputText id="FirstName" value="#{userreg.FirstName}">
</xp:inputText>
<br />
<label for="LastName">Last Name</label>
<xp:inputText id="LastName" value="#{userreg.LastName}">
</xp:inputText>
<br />
<label for="CompanyName">Company Name - ABN</label>
<xp:inputText id="CompanyName" value="#{userreg.CompanyName}">
</xp:inputText>
<br />
<label for="Level0_1">Service ID</label>
<xp:inputText id="Level0_1" value="#{userreg.Level0_1}">
</xp:inputText>
<br />
<label for="HTTPPassword">Password</label>
<xp:inputText id="HTTPPassword" value="#{userreg.HTTPPassword}"
password="true">
</xp:inputText>
<br />
<label for="InternetAddress">Email Address</label>
<xp:inputText id="InternetAddress" value="#{userreg.InternetAddress}">
</xp:inputText>
<br />
<xp:text escape="true" id="type" value="#{userreg.type}"
rendered="false">
</xp:text>
<xp:button value="Register" id="userreg_submit">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:actionGroup>
<xp:actionGroup>
<xp:modifyField name="type" value="Person">
</xp:modifyField>
<xp:modifyField name="FullName">
<xp:this.value><![CDATA[#{javascript:var fullNameArray = new Array();
var first = getComponent("FirstName").getValue();
var last = getComponent("LastName").getValue();
var abn = getComponent("CompanyName").getValue();
fullNameArray[0]= "CN=" + first + " " + last + "/OU=" + abn + "/O=RR1";
fullNameArray[1] = first + " " + last;
return fullNameArray;
}]]></xp:this.value>
</xp:modifyField>
<xp:saveDocument></xp:saveDocument>
</xp:actionGroup>
</xp:actionGroup>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:br></xp:br>
<xp:br></xp:br></fieldset></xp:view>
I had the same issue with my portal users registering and have been able to over come this with the following code. It's virtually instant. This code is in my register button after my bean creates the user in the nab and updates all the groups, acl etc....
sessionAsSigner.sendConsoleCommand( session.getServerName(), "lo updall yourNabDBName -t ($VIMGroups)" );
sessionAsSigner.sendConsoleCommand( session.getServerName(), "lo updall yourNabDBName -t ($Users)" );
sessionAsSigner.sendConsoleCommand( session.getServerName(), "lo updall yourNabDBName -t ($ServerAccess)" );
sessionAsSigner.sendConsoleCommand( session.getServerName(), "sh nl r" );
For a lotuscript version of this you need to manually refresh a couple of views in the nab and then it worked.
Dim nabFullNameView As NotesView
Dim nabServerAccessView As NotesView
Dim nabUsersView As notesview
Set nabUsersView = dbNAB.GetView("($Users)")
Set nabFullNameView = dbNAB.GetView("($LDAPCN)")
Set nabServerAccessView = dbNAB.GetView("($ServerAccess)")
Call nabFullNameView.Refresh
Call nabServerAccessView.Refresh
Call nabUsersView.Refresh
'Closing session commits person document to NAB.
Call s.Close