Prevent javascript component from refreshing my entire page - xpages

I am adding pivottable.js (after reading of it here http://www.wissel.net/stw/wisselblog.nsf) into an xPages application. The component loads but I have a problem when the component which allows for filtering of the data, is done on my xPage it refreshes the entire page instead of just the chart.
Here is a demo of the app, You can see that when you hit the drop down on a category you can filter and once that is done it does a partial refresh to filter the data.
http://nicolas.kruchten.com/pivottable/examples/mps.html
On my xPage when I filter the data it refreshes the entire page. Is there a way to prevent this behavior?
Below is the relevant code. The code not included is just grabbed from the project unmodified but I can include if needed.
I am using using a custom control on the xpage
<xc:ccPivot disableTheme="true"></xc:ccPivot></xp:view>
Inside the custom control there isn't much. I have tried calling the script at the bottom of the page but that made no change.
<script type="text/javascript" src="callPivotTable"></script>
<xp:this.resources>
<xp:script src="/pivot.js" clientSide="true"></xp:script>
<xp:styleSheet href="/pivot.css"></xp:styleSheet>
<xp:script src="/jquery-ui-1.9.2.custom.min.js"
clientSide="true">
</xp:script>
<xp:script src="/d3_renderers.js" clientSide="true"></xp:script>
</xp:this.resources>
<div id="output" style="margin: 10px;"></div>
Here is the callPivotTable script
$(function(){
var derivers = $.pivotUtilities.derivers;
$.getJSON("./xRest.xsp/restService2", function(mps) {
$("#output").pivotUI(mps
);
});
});
You will notice that I am not calling jQuery as a resource here. That is because I am using the bootstrap4xpages extension library which loads jQuery. Not sure if that makes a difference or not.
Here is a link to a working nsf. It is using the bootstrap extension library, and bootstrapv2.3.2 but will load fine in bootstrap3 with the same problems.
Link to nsf

No need to change the pivot.js code. It simply does not work when pivot table is rendered inside form. Any widget refresh forces post of the entire form. Couldn't make it work by code change, so I have turned off the form generation and now it works as expected.
Go to XPage properties (All properties tab) and set property createForm to false. Or in source it should like like this:
<?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"
createForm="false">
<xp:this.resources>
<xp:script
src="/pivot.js"
clientSide="true">
</xp:script>
<xp:styleSheet
href="/pivot.css"></xp:styleSheet>
<xp:script
src="/jquery-ui-1.9.2.custom.min.js"
clientSide="true">
</xp:script>
<xp:script
src="/jquery.ui.touch-punch.min.js"
clientSide="true">
</xp:script>
</xp:this.resources>
<xp:scriptBlock>
<xp:this.value><![CDATA[$( function() {
$("#output").pivotUI( [ {
color : "blue",
shape : "circle"
}, {
color : "red",
shape : "triangle"
} ], {
rows : [ "color" ],
cols : [ "shape" ]
});
});]]></xp:this.value>
</xp:scriptBlock>
<div
id="output"
style="margin: 10px;">
</div>
</xp:view>

Related

loading a javascript library and not getting an object returned

I am trying to load a javascript library in XPages.
Normally in HTML the reference looks as followed:
<html>
<head>
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
</head>
<body>
</body>
</html>
which gives me a Hammer object in the DOM which I can work further with.
In XPages I have made the following setup:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" disableTheme="true"
dojoForm="false" dojoTheme="false" dojoParseOnLoad="false"
createForm="false">
<xp:this.resources>
<xp:script src="https://hammerjs.github.io/dist/hammer.js"
clientSide="true">
</xp:script>
</xp:this.resources>
</xp:view>
alternatively:
<?xml version="1.0" encoding="UTF-8" ?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" disableTheme="true" dojoForm="false" dojoTheme="false" dojoParseOnLoad="false" createForm="false">
<xp:this.resources>
<xp:headTag tagName="script">
<xp:this.attributes>
<xp:parameter name="script" value="text/javascript" />
<xp:parameter name="src" value="https://hammerjs.github.io/dist/hammer.js" />
</xp:this.attributes>
</xp:headTag>
</xp:this.resources>
</xp:view>
But the Hammer object is not present in the DOM!
What am I doing wrong?
hammer.js uses AMD. Here's a snippet from the hammer.js source code where AMD is used:
if (typeof define == TYPE_FUNCTION && define.amd) {
define(function() {
return Hammer;
});
} else if (typeof module != 'undefined' && module.exports) {
module.exports = Hammer;
} else {
window[exportName] = Hammer;
}
Unfortunately AMD loading conflicts with Dojo in XPages. See this answer on how to remove AMD loading.
In your case you need to download hammer.js, change the AMD loading part, add it to your nsf and then load the script from your nsf instead.
You remove the AMD loading part by changing the code in hammer.js to for instance this:
//if (typeof define == TYPE_FUNCTION && define.amd) {
// define(function() {
// return Hammer;
// });
//} else if (typeof module != 'undefined' && module.exports) {
if (typeof module != 'undefined' && module.exports) {
module.exports = Hammer;
} else {
window[exportName] = Hammer;
}
Another way to handle this is to use the AMD loader which is a part of Dojo on newer versions of Domino.
This code implements the basic example from the hammer.js documentation (I'm only using jQuery for the ready function):
require({
async: true,
aliases: [['jquery', '//code.jquery.com/jquery-1.11.3.min.js'],
['Hammer', '//cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.4/hammer.min.js']]
}, ['jquery', 'Hammer'], function(jq, Hammer){
$(function() {
var myElement = document.getElementById('myElement');
// create a simple instance
// by default, it only adds horizontal recognizers
var mc = new Hammer(myElement);
// listen to events...
mc.on("panleft panright tap press", function(ev) {
myElement.textContent = ev.type +" gesture detected.";
});
});
});
Then just add the code to your xpage in a script tag:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:styleSheet href="./css/basicImpl.css"></xp:styleSheet>
<xp:script src="./js/basicImpl.js" clientSide="true"></xp:script>
</xp:this.resources>
<div id="myElement"></div>
</xp:view>
I'm also using the stylesheet from the example:
#myElement {
background: silver;
height: 300px;
text-align: center;
font: 30px/300px Helvetica, Arial, sans-serif;
}
A few things in no particular order:
You can test that the script is loaded by using Chrome Dev Tools, and go to console. You can then put script in to call the hammer code. If the script isn't loaded then you will get an error. Your script might already be loaded.
Verify with the Hammer js site that you are loading the script properly
Try putting your code in the onClientLoad event which just loads client code.
Put the Hammer.js code into your NSF, to make sure you do not have connection issues to github. Use Package Explorer and put the files in the WebContent/js folder. Put any CSS in the WebContent/CSS folder

Domino 9 / Dojo 1.8 - Date Time Picker without default value

I want a Date Time Picker control WITHOUT a default value. Doesn't seem to be possible anymore :-(
To reproduce, create a blank XPage and place a Date Time Picker control. Open the XPage in the browser and you will see that it defaults to today.
I didn't found any way to set the default to an empty value. I tried setting all properties/data/default to 0, null, empty string and so on - no luck.
I tried the data-dojo-probs attribute with value:'', this sets the default to 1970-1-1, but not to blank.
Any ideas?
This is a known issue in ND9, reported as SPR DEGN966F5V.
A work around for the issue (from SPR) is to modify the widget prototype in the postCreate function to prevent the value from being reset.
require([
"dojo/_base/lang",
"ibm/xsp/widget/layout/DateTextBox",
"ibm/xsp/widget/layout/TimeTextBox",
"ibm/xsp/widget/layout/DateTimeTextBox"
], function(lang, DateTextBox, TimeTextBox, DateTimeTextBox){
var a = {};
lang.mixin(a, {
postCreate: function(){
this.inherited(arguments);
}
});
DateTextBox.extend(a);
TimeTextBox.extend(a);
DateTimeTextBox.extend(a);
});
Here is an example of it working.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.resources>
<xp:script clientSide="true">
<xp:this.contents><![CDATA[
require([
"dojo/_base/lang",
"ibm/xsp/widget/layout/DateTextBox"
], function(lang, DateTextBox){
var a = {};
lang.mixin(a, {
startup: function(){
this.inherited(arguments);
this.set("value", null);
}
});
DateTextBox.extend(a);
});
]]></xp:this.contents>
</xp:script>
</xp:this.resources>
<xp:inputText id="inputText1" value="#{sessionScope.inputText1}">
<xp:this.converter>
<xp:convertDateTime type="date" />
</xp:this.converter>
<xp:dateTimeHelper />
</xp:inputText>
</xp:view>
i dont know how it works in notes 9 but you could remove it with a CSJS like:
var field= dojo.byId('#{id:field}')
field.value ="";
hope it helps..
In applications I've previously applied a dojoType on the dateTimeHelper with this code:
<xp:dateTimeHelper id="dateTimeHelper1" dojoType="dijit.form.DateTextBox">
</xp:dateTimeHelper>
That was to address a problem in previous Domino versions where the page refreshed as soon as the picker was selected in certain flavours of IE.
This code seems to work fine on R9 without needing the workaround.

How to update Beans or Scoped Variables when transitioning in XPages Mobile Controls

I'm having a problem trying to set values on a mobile page before transition into that page.
I have a scriptblock based on Tony McGuckin's OpenNTF XSnippet. http://openntf.org/XSnippets.nsf/snippet.xsp?id=calling-server-side-jscode-during-mobile-page-transitions
So when I transition to a page with the ID of "appPage3" I call the method "facilityCheckIn" if the JSON-RPC service.
I'm trying to set the value in a viewScoped managed bean and for testing am trying to set a normal viewScope variable.
As best as I can tell the Mobile Page is not picking up the changes. It's not showing the viewScope at all. I'm not sure what's up with that.
I believe the managed bean is getting it's value but it's like the page is getting rendered first. The value on the Mobile page is blank the first time
and then if I either refresh, or exit and go back to the page it does display. I tried accessing the bean via SSJS and EL.
I really need to be able to set values for mobile pages as I transition into and out of a page.
Any advice would be appreciated.
Thanks!!
I've updated the code below to show the complete Mobile Page. I've not included anything for the custom control that should be displaying the fields but can if necessary.
I've created a 6ish minute video demonstrating the problem and that also shows all the relevant code.
http://traffic.libsyn.com/notesin9/SO-Question.mp4
Thanks!!!
<xp:this.resources>
<xp:styleSheet href="/.ibmxspres/dojoroot/dijit/themes/tundra/tundra.css"></xp:styleSheet>
<xp:styleSheet href="/mobile.css"></xp:styleSheet>
</xp:this.resources>
<xc:ccDebugToolbar defaultCollapsed="false"
collapseTo="left"></xc:ccDebugToolbar>
<xe:singlePageApp id="singlePageApp1"
selectedPageName="home">
<xe:djxmHeading id="djxmHeading1" label="My Inventory"></xe:djxmHeading>
<xe:appPage id="homeID" pageName="home">
<xe:djxmHeading id="djxmHeading2" label="My Inventory">
</xe:djxmHeading>
<xc:mob_menu_home></xc:mob_menu_home>
</xe:appPage>
<xe:appPage id="appPage2" pageName="facility" resetContent="true">
<xc:mob_menu_facility></xc:mob_menu_facility>
</xe:appPage>
<xe:appPage id="appPage8" pageName="show" resetContent="true">
<xc:mob_menu_show></xc:mob_menu_show>
</xe:appPage>
<xe:appPage id="appPage3" pageName="facilityCheckIn"
resetContent="true">
<xc:mob_page_CheckInOut header="Check In at Facility"
scanType="Receiving" scanLocation="Facility">
</xc:mob_page_CheckInOut>
</xe:appPage>
<xe:appPage id="appPage5" pageName="facilityCheckOut"
resetContent="true">
<xc:mob_page_CheckInOut header="Check Out from Facility"
scanType="Shipping" scanLocation="Facility">
</xc:mob_page_CheckInOut>
</xe:appPage>
<xe:appPage id="appPage6" pageName="showCheckOut"
resetContent="true">
<xc:mob_page_CheckInOut header="Check Out from Show"
scanType="Shipping" scanLocation="Show">
</xc:mob_page_CheckInOut>
</xe:appPage>
<xe:appPage id="appPage7" pageName="showCheckIn"
resetContent="true">
<xc:mob_page_CheckInOut header="Check In at Show"
scanType="Receiving" scanLocation="Show">
</xc:mob_page_CheckInOut>
</xe:appPage>
<!-- SUB PAGES -->
<!-- GET MANIFEST Page -->
<xe:appPage id="appPage4" pageName="manifest" resetContent="true">
<xc:mob_page_Manifest></xc:mob_page_Manifest>
</xe:appPage>
</xe:singlePageApp>
<xe:jsonRpcService id="jsonRpcService1" serviceName="appService">
<xe:this.methods>
<xe:remoteMethod name="setCurrentPage">
<xe:this.arguments>
<xe:remoteMethodArg name="pageName"></xe:remoteMethodArg>
<xe:remoteMethodArg name="direction"></xe:remoteMethodArg>
</xe:this.arguments>
<xe:this.script><![CDATA[print("PageName " + pageName);
print("Direction " + direction);
viewScope.put("vsPage", "test");
App.setCurrentPage(pageName);
App.setCurrentDirection(direction);
return "";]]></xe:this.script>
</xe:remoteMethod>
</xe:this.methods>
</xe:jsonRpcService>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:scriptBlock id="scriptBlock1">
<xp:this.value><![CDATA[
XSP.addOnLoad(function(){
// Begin App Page 1
var newPage = dijit.byId("#{id:appPage3}");
if(null != newPage){
dojo.connect(newPage, "onBeforeTransitionIn", function(){
var deferred = appService.setCurrentPage("facilityCheckIn", "onBeforeTransitionIn");
deferred.addCallback(function(result) {
console.log(result);
});
});
dojo.connect(newPage, "onBeforeTransitionOut", function(){
var deferred = appService.setCurrentPage("facilityCheckIn", "onBeforeTransitionOut");
deferred.addCallback(function(result) {
console.log(result);
});
});
dojo.connect(newPage, "onAfterTransitionIn", function(){
var deferred = appService.setCurrentPage("facilityCheckIn", "onAfterTransitionIn");
deferred.addCallback(function(result) {
console.log(result);
});
});
dojo.connect(newPage, "onAfterTransitionOut", function(){
var deferred =appService.setCurrentPage("facilityCheckIn", "onAfterTransitionOut");
deferred.addCallback(function(result) {
console.log(result);
});
});
}
// End App Page 1
// Begin Home Page
var newPage = dijit.byId("#{id:homeID}");
if(null != newPage){
//console.log("Inside home Page")
dojo.connect(newPage, "onBeforeTransitionIn", function(){
var deferred = appService.homePageReset();
deferred.addCallback(function(result) {
console.log(result);
});
});
dojo.connect(newPage, "onBeforeTransitionOut", function(){
var deferred = appService.homePageReset();
deferred.addCallback(function(result) {
console.log(result);
});
});
}
// END Home Page
// Insert new Code ABOVE THIS LINE
}); // This ends the block that holds all the functions
]]></xp:this.value>
</xp:scriptBlock>
I was faced with a similar problem some time ago and I had to create another XPage (with a SingleApplication) to navigate to instead of just jumping to an appPage within the same SingleApplication.
Not ideal, however, it also solved another issue for me since I wanted to be able to jump directly to the page in question (using a QR code).. ;-)
/John
PS. Will follow your findings here to see if it can be done. This kind of issues with the mobile controls (I had some with typeahead as well) sort of pushed me in direction of other frameworks, for now, anyway...
David.... You said that doing a refresh did correct the issue, also Stephan mentioned that the transition to the new page happens before the result comes back from the RPC which I think is correct. So in your callback, while not optimal but may at least get you moving forward, you could fire off a partial refresh by using:
XSP.partialRefreshGet("#{id:appPage3}",{});
//I've had partial success with the "#{id:appPage3}" call, so I usually use a full
// dojo query like:
var appPageId = dojo.query("[id$='appPage3']")[0].id
I don't know if that will correct your issue, but it appears everything is working as it should with the exception of that value being populated.
Ok I believe I have this solved. I had no luck using the RPC control stuff from Tony's XSnippet. That works of you're leaving the page but not if your entering. This seems to work as desired.
To deal with entering you need to put your code in the Rendering event of the header.

Getting properties/parameters from page level

I wonder if I can get the parameters and/or properties of an xpage or custom control programmatically.
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" id="layout">
<xp:this.properties>
<xp:parameter name="testcc.xsp" value="Test 1"></xp:parameter>
<xp:parameter name="index.xsp" value="Main"></xp:parameter>
</xp:this.properties>
...
How can I access this parameter list to use it e.g. in a repeat control?
EDIT
You both are right, thank you! But this works only on a page, not in a custom control.
EDIT
You both are great :-)
BUT: I should revise my question:
I have a custom control where I defined the properties. Within the SAMe custom control I want to access these properties in a repeat control.
Both your answers seem to assume that the access to these properties is from the view (page) level, right?
I tested Svens way - this works if I access the props in the CC from the page level.
EDIT
So this is the code of the CC:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.properties>
<xp:parameter name="param" value="val"></xp:parameter>
</xp:this.properties>
<xp:label value="#{javascript:facesContext.getProperty('param')}"
id="label1">
</xp:label>
</xp:view>
As you can see I just want to access the property within the CC itselt, not from the page level.
You can get the properties by accessing facesContext:
facesContext.getProperty("index.xsp")
EDIT:
If you set the properties in a custom control, the properties are not added to the view root. The are set as attributes of the custom control (com.ibm.xsp.component.UIIncludeComposite).
To access them you first have to give your CC an Id:
<xc:ccProp id="myId" />
This allows you to access the custom control like a component with the getComponent() method and retrieve the attribute properties which contains the properties:
<xp:label id="labelProperty">
<xp:this.value><![CDATA[#{javascript:
var cc:com.ibm.xsp.component.UIIncludeComposite = getComponent("myId");
var arrList:java.util.ArrayList = cc.getAttributes().get("properties");
arrList.get(0).getName()}]]>
</xp:this.value>
</xp:label>
EDIT 2:
You can access the CC (which is the parent of the label in this example) this way if you don't want to give your CC an ID:
Code of the custom control:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:this.properties>
<xp:parameter name="param" value="val"></xp:parameter>
</xp:this.properties>
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:
this.parent.getAttributes().get("properties").get(0).getName()
}]]></xp:this.value>
</xp:label>
<xp:label id="label2">
<xp:this.value><![CDATA[#{javascript:
this.parent.getAttributes().get("properties").get(0).getValue()
}]]></xp:this.value>
</xp:label>
</xp:view>
Hope this helps to clarify the issue.
To get the property list you can use the view.getProperties(). It returns an object of java.util.List which you can use to loop through individual properties (which are objects of com.ibm.xsp.complex.Parameter). Below is the snippet.
var allProperties:java.util.List = view.getProperties();
for (var i=0 ; i<allProperties.size() ; i++) {
var property:com.ibm.xsp.complex.Parameter = allProperties.get(i);
// property.getName();
// property.getValue();
}
If you want to put it in a repeat then you can bind it to view.getProperties() and then get its individual values. You code would then look something like this:
<xp:repeat rows="30" value="#{javascript:view.getProperties()}" var="property">
<xp:text escape="true">
<xp:this.value><![CDATA[#{javascript:property.getName() + " - " + property.getValue();}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
</xp:repeat>
If you've got values that you want to use on the various parts of an Xpage, whether directly on the page, in a custom control, or in a repeat, I would recommend that you put the values into sessionScope variables. This allows you to change them easily as the user enters information on the Xpage.
For example, sessionScope.PODocUNID = poDoc.getDocument().getUniversalID(); would put the UNID of the purchase order document that I'm working with into a sessionScope variable named PODocUNID. Then, you can pull up the value any time you want by simply referencing sessionScope.PODocUNID in your code.
Alternatively, you could use Russ Maher's current favorite toy, the Managed Bean (see his three-part video on Notes in 9, starting at: http://notesin9.com/index.php/2012/11/02/notesin9-084-sharing-managed-beans-in-xpages/ )

xe:restService not loading data after page is rendered

I have a xPage with following content:
<xe:restService id="restService" preventDojoStore="false">
<xe:this.service>
<xe:viewJsonService viewName="vwChartData"
contentType="text/json">
<xe:this.columns>
<xe:restViewColumn columnName="x" name="valuex"></xe:restViewColumn>
<xe:restViewColumn columnName="y" name="valuey"></xe:restViewColumn>
</xe:this.columns>
</xe:viewJsonService>
</xe:this.service>
</xe:restService>
How to fetch the data after page load? From what I understand it should create a Dojo store, although it never loads the data.
When I add to the page a xe:djxDataGrid, hide it from user, I can easily access the data from created Dojo Store, either by referring directly the restService variable, or through djxDataGrid.
Solution:
<xp:scriptBlock>
<xp:this.value><![CDATA[XSP.addOnLoad( function() {
var ds = eval('restService');
ds.fetch({
onComplete : function(items, request) {
console.log(items);
}
});
});]]></xp:this.value>
</xp:scriptBlock>
When you look at the source code when using a data grid you would see all the source code necessary to link your Dojo store. Watch the XSP.onLoad for trigger code.

Resources