This is my answer to a super simple question that had many scattered partial answers around the internets. Kind of a gist.
I am no longer working with xpages, but at home I still have my TDL app running. There is a very simple to do list view I compiled from some example template. The "mark done" button has been sitting as a dummy for years now - never found the easy way to get that function happening.
After googling my way to some Stack-O and Developerworks and a few Domino blogger q-and-a threads, I put this little pile of SSJS in place and ta-da! No original work here, just the gluing and duct taping - see answer:
This goes in the onClick event for my view "Complete Selected" button. The Status field is my target, with "3" mapped to "Done" in the app:
sessionScope.put("myIdList", getComponent("viewPanel1").getSelectedIds());
var docsForAction = sessionScope.get("myIdList")
var doc:NotesDocument;
for(i=0; i < docsForAction.length; i++){
var unid=database.getDocumentByID(docsForAction[i]).getUniversalID();
doc = database.getDocumentByUNID(unid);
doc.replaceItemValue("Status","3");
doc.save();
}
docsForAction = [];
viewScope.put("myIdList", docsForAction);
context.reloadPage();
Related
I'm writing a Primefaces 5.1 portlet.
It consists in a unique page containing a panelMenu, and I need that it starts with any panel collapsed everytime a user change page (on page loading).
But, if I open a panel, then change page, it will start showing that panel still opened.
I wasn't able to find any option to achieve this goal (e.g. collapsed=true, ignoreCookie=true or something similar).
The only solution I found was the following Javascript code:
PrimeFaces.widgets.myPanelMenu.collapseRootSubmenu(PrimeFaces.widgets.myPanelMenu.headers);
The problem is that this code will collapse any opened panel (so on page loading user is able to see panel menu collapsing animation) but it seems it doesn't store this state in its cookie/localstorage... the result is that on any page loading user can see this animation.
I'm sure it doesn't save its state, because the only way to "solve" the problem is to manually re-open and re-collapse the panels... then, on following page change, these menus start closed (and there is no animation).
I also tried to use PrimeFaces.widgets.sideMenuPanel.saveState() after collapsing, but with no success.
Do you have any idea about?
Thank you...
I found a solution to the problem.
If you read my discussion with Kukeltje (comments on my question), you will find that latest Primefaces' versions will solve the problem.
Otherwise, if you want to avoid upgrade or modify sources, and you need a quick fix based on Javascript only please read the following part of the answer.
It directly works on the component's state using JavaScript.
First of all you need to have a variable reference to your component:
<p:panelMenu model="#{menuBackingBean.menuModel}" widgetVar="sidePanelMenu" />
Then you should add the following JS code on document ready:
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
// 1. On page loading collapses possible opened panels
panelMenu.collapseRootSubmenu(panelMenu.headers);
// following line is commented because it never should be necessary is not necessary (unless unexpected situation I never verified)
//clearSidePanelMenuPreferences();
// 2. Call the "clear preferences" actions on click on two tpe of links: first level are the panel link (used to open/close the menu) and second level are the destination links
// We need to fork also on the first level links to be sure it works after user clicks there then exit from the page in another way
panelMenu.headers.children("a").click(function(){setTimeout(clearSidePanelMenuPreferences, 500)}); // setTimeout is necessary because this event should be fired after preferences are written
panelMenu.headers.siblings().find("a").click(function(){clearSidePanelMenuPreferences();});
The function called to clear preferences are the following:
function clearSidePanelMenuPreferences() {
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
panelMenu.expandedNodes = []; // clear the opened panels lists
panelMenu.saveState(); // store this information
}
Hope it helps
Please check this block of code
PF('myPanelMenu').headers.each(
function(){
var header = jQuery(this);
PF('myPanelMenu').collapseRootSubmenu(header);
header.removeClass('ui-state-hover');
}
);
I prefer to do this in order to execute this method only once and keep the menu option selected.
$(document).ready(function() {
if(location.pathname == "/cotizador/" || location.pathname == "/cotizador/faces/login.xhtml"){
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
// 1. On page loading collapses possible opened panels
panelMenu.collapseRootSubmenu(panelMenu.headers);
panelMenu.expandedNodes = []; // clear the opened panels lists
panelMenu.saveState();
}
});
There is a viewPanel having a column with showCheckbox="true".
Is it possible to restrict the users to select only one row/document ( and not multiple rows/docs. ) listed by the viewPanel?
Not in a View Panel. The View Panel is designed to offer a quick, simple approach with restricted functionality.
An alternative (possibly better) approach may be to have another column with a link or image that triggers whatever functionality you need. That will allow users to trigger functionality with a single click rather than two. The View Panel allows you to place controls in columns instead of just mapping to a column in the underlying view.
Alternatively, you could add a checkbox manually to a column, map to a scoped variable, and check/uncheck programmatically.
Paul's probably right on. My alternative for you is to use a repeat control. You can make it look however you want. Including a view control.
I have an example of this in this NotesIn9 show: http://notesin9.com/index.php/2011/07/11/notesin9-ee-009-using-java-hashmaps-and-treemaps-with-xpages/
Now in my example I did multiple values. But if instead of a HashMap or ArrayList if you kept your "selected" document id in a single value field like just a scoped variable.. then you'd get what you want. One document at a time.
I agree with Paul Stephans (also upvoted his answer because I think it would be the Nest solution) but if you insist on adding such a functionality to your viewPanel you can do this by adding a client side script to prevent the user from selecting more then one element:
First add the styleClass="rowCB" to your checkbox row and insert this code to your xpage:
<xp:scriptBlock>
<xp:this.value><![CDATA[dojo.ready(function(){
dojo.query('.rowCB>input').connect("onclick", function(evt){
var target = evt.target.id;
if(!window.crrCheckedElement){
window.crrCheckedElement = evt.target.id;
}else if(window.crrCheckedElement != target){
alert("You can select only one item!");
evt.target.checked = false;
}else if(window.crrCheckedElement == target){
window.crrCheckedElement = "";
}
})
});]]></xp:this.value>
</xp:scriptBlock>
Maby the code needs some improvement but this should be your way to go.
though maybe not the best solution, here is one possibility.
function deselectOtherDocs(viewName, currentDocId) {
var viewPanel:com.ibm.xsp.component.xp.XspViewPanel = getComponent(viewName);
var selectedIds = viewPanel.getSelectedIds();
for(var i=0; i<selectedIds.length; ++i){
if(selectedIds[i]!=currentDocId){viewPanel._xspSetIdUnchecked(selectedIds[i])}
return;
}
fire this off when a doc is checked and pass the view control name and the current doc's unid.
pardon any typos. I am writing from my phone.
edit: if you don't have to use a view control, then David Leedy's suggestion is the way to go. store the selected unid in a scope variable and let that determine which repeat row's box is selected. you might also consider using a radio button instead of a checkbox, since the former is understood as a single selector by users.
I've start to develop XPage 7 weeks ago, and i have a problem with "getting data".
On my first page i have a view with a lot of documents, and a button who redirect me on a new page, to create a new document. On my first page i can select a document and when i click on the button i put my id document selected on a sessionSCope.
Button script:
var viewPanel=getComponent("viewPanel1");
var docIDArray=viewPanel.getSelectedIds();
var docUID=database.getDocumentByID(docIDArray[0]).getUniversalID();
sessionScope.put("docUID", docUID);
context.redirectToPage("AjoutSuivi");
On my new XPage i want to get some data on my selected document so on clientLoad of the XPage i execute this script:
var docUID = sessionScope.get("docUID");
var doc:NotesDocument = database.getDocumentByUNID(docUID);
getComponent("contactname1").setValue(doc.getItemValueString("ContactName"));
On my database i have a field "ContactName" and on my XPage i have a field contactname1. I have try with "database.getDocumentByID(docUID)" and i'm sure that "database" is the good link of the database.
When i try it, there is nothing on the field contactname1 have u an idea why that's doesn't work ?
So much thank's if you can help me
Yann
PS: sorry for my bad english
Put your code into the event afterPageLoad and it should work (for the execution order of events take a look at XPage Cheat Sheet #1 - The Page Lifecycle).
Y4nn welcome to the XPages club. When you bind a control to a data source it is better to set the value in the data source than in the control. So you write:
document1.getDocument().replaceItemvalue(...)
(picking on a glass now, watch out for correct syntax)
Does anyone have a suggestion on how to print selected documents in an simple xPages view. I'm converting a legacy application. Which used the following Lotus script code to print. Thanks
Set db = session.CurrentDatabase
Set collection = db.UnprocessedDocuments
count = collection.count
If count = 0 Then
Goto errSelectDocs
End If
Stop
For i = 1 To count
'
Set note = collection.GetnthDocument (i)
Set Source2 = w.EditDocument( False, note )
Set Source3 = w.ComposeDocument("","","mRecensement imp")
Call Source3.print(1)
Call Source3.close
Call Source2.close
'----------------------------------
nextdocument:
Next
I'm going to answer here rather the following up in the comments of the Simon answer.
so ok. We're saying build a new page with a repeat control of the select documents. and the question asker is saying, I THINK, that it seems wrong to do:
doc:NotesDocument=database.getDocumentByID(rowData);
return doc.getItemValue("xxxx") for 30 + items
right. You don't want to do that. should work. But icky to do.
Probably what I would do is create a SSJS function to pass rowData into. In that function build an array. Load the document once... put all the items into the array and pass them back to the page with the repeat control.
Probably what you do then is have a panel and use either a dataContext or objectData that's bound to the panel. Inside the panel is your page and fields. Those fields just read from the dataContext or objectData. so you're only getting the document once. I guess you could even use just a scoped variable though I don't think there's an event to call code on each row. So you'd need to hack it into the first field maybe or something.
But that's what you want. I previously asked a question on StackOver flow about returning multiple parameters like this: How to pass variable parameters to an XPages SSJS function?
Maybe that's helpful.
Someone might come up with a better solution but one option is this.
First have your viewPanel on your first XPage. Select your documents and click a button. The code would do this.
var viewPanel = getComponent("viewPanel1");
sessionScope.documentIDs = viewPanel.getSelectedIds();
You then hand it off to another XPage which has a repeat control of the print structure for a document. It reads the document ID's and creates the page. Then just use the normal print command after loading.
window.print();
I built this function to find People Pickers by Field Title. Since the picker does not provide and TagName and Tile type of information and custom pages can have multiple people pickers, I used the NOBR tag which displays the title for each picker. This works flawlessly, but I think it can be sped up abit.
Please share your thoughts. Thanks you!!
function resetPickerInput(title){
var result="";
var tags=document.getElementsByTagName("NOBR");
var len=tags.length;
for(var i=0;i<len;i++){
if(tags[i].innerHTML.indexOf(title)>-1){
var div=tags[i].parentNode.parentNode.parentNode.getElementsByTagName("DIV");
var divLen=div.length;
for(var j=0;j<divLen;j++){
if(div[j].name=='upLevelDiv'){
result=div[j];
break;
}
}
}
}
return result;
}
Yes, you are doing it more complicated than you should.
This jQuery example looks very promising (Get Value of People Picker in Sharepoint):
var User = $("textarea[title='People Picker']").val().split("\\");
How do I get text from people picker textarea using javascript only
uses this: $(".ms-inputuserfield #content").each(function(){...
Another example: Hide People Picker control in SharePoint List Forms
Set a People Picker’s Value on a Form – Revisited with jQuery
$(this).find("div[Title='People Picker']").html(userName)
Since you didn't state your version: Retrieve Email Address from sharepoint people picker using javascript a solution for SP2007.