In an app I am serving multiple languages via strings properties files.
In a menu the reference in link may look as followed:
<xp:link
text="#{strings.menu_label_mandate_request}"
title="#{strings.menu_label_mandate_request}"
value="/mandate.xsp" styleClass="plainLink">
</xp:link>
This is working fine.
Now I want to define the links in a java class and loop through it via a repeat control but keeping in tact the multi-language support.
In my Menu class I create an array of Page objects and set the properties as followed:
this.navigation = new ArrayList<Page>();
this.navigation.add(new Page("menu_label_mandate_request", "mandate.xsp", "fa fa-users", false));
...
I have added a repeat control on an xsp to initiate the class (via a managed bean):
<xp:repeat id="rptNavigation" rows="30"disableOutputTag="true" value="#{javascript:menu.navigation}" var="nav">
However I have problems setting the correct prototyping (if possible):
<xp:link
text="#{strings.nav.label}"
title="#{strings.nav.label}"
value="/mandate.xsp" styleClass="plainLink">
</xp:link>
So i get the error message:
java.util.MissingResourceException: Can't find resource for bundle java.util.PropertyResourceBundle, key nav
Can't find resource for bundle java.util.PropertyResourceBundle, key nav
Does anyone have an idea what I have overlooked?
You are expressing what the dynamic value nav should be as a literal object and not as a reference. Write:
<xp:link
text="#{strings[nav.label]}"
title="#{strings[nav.label]}"
value="/mandate.xsp" styleClass="plainLink">
</xp:link>
Related
I'm passing a doc datasource into a Custom Control using a property defintion named docDatasource and set to a datatype com.ibm.xsp.model.domino.DominoDocumentData. Just by chance I stumbled upon an editor type named "Datasource Picker". Sounded promising, so I set it to use for m prop definition.
Upon binding the CC into my Xpage - where my doc DS is defined - I indeed can use the picker to choose my datasource ,just as I had expected. But then building the Xpage code I receive an error saying
The value of the property docDatasource cannot be primitive.
So obviously the datatype and the editor don't go along well. There's no harm done really, but I'm curious to learn what else this editor could be used for.
What to do with name of the data source passed to custom control parameter: retrieve its object by simple binding #{requestScope[compositeData.docDatasource]}.
That will return your data source and you can use this binding where needed. Or store it inside local variable and use it instead.
I dont know where you went wrong.
Here is xpage source:
<xp:this.data>
<xp:dominoDocument var="document1" formName="asdf" action="openDocument" documentId="08f6"/>
</xp:this.data>
<xc:doccc dds="#{javascript:document1}"/>
and custom control:
<xp:label id="label1">
<xp:this.value><![CDATA[#{javascript:compositeData.dds.getItemValueString("fl_name");}]]></xp:this.value>
</xp:label>
with custom control property named dds, type com.ibm.xsp.model.domino.DominoDocumentData and editor DataSourcePicker.
I am trying to understand the dataContext better and tried creating a dataContext referencing a document.
<xp:this.dataContexts>
<xp:dataContext var="doc1">
<xp:this.value>
<![CDATA[#{javascript:
var db:NotesDatabase = sessionAsSigner.getDatabase("","privDb.nsf");
var adoc:NotesDocument = db.createDocument();
return adoc }]]>
</xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>
I then tried using EL and javascript to bind fields on my xpage to the dataContext
<xp:inputText id="inputText2" value="#{doc1.lastname}"></xp:inputText>
<xp:inputText id="inputText1" value="${javascript:doc1.firstname}"></xp:inputText>
But when I save, it does't save anything.
<xp:button value="save" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
print(doc1.getClass().getName() )
doc1.save();
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
The print command showing me the class name is showing up as lotus.domino.local.Document
The document is saved to the database, but it has no values other than $UpdatedBy. I can't seem to bind fields to the edit boxes.
The reason I am going down this path is twofold, 1. I want to use sessionAsSigner so I can keep the database security on the remote db (privDb.nsf) at No Access for anonymous and default, and 2. I want to learn a little more about dataContext, data sources and binding. I have read the "easy" way of using a Public Document, using the $PublicAccess field, etc, which is the "Old School" Notes way, and yes, I could do it that way, but want to understand how to do it using dataContexts, if possible.
A dataContext is basically a scoped variable, scoped lower than viewScope but higher than requestScope, scoped to a component. That component can be an XPage, a Custom Control or a Panel (yes, dataContexts can be added to a Panel too).
Like other scoped variables on the page, a simple save action does not save dataContexts. If you want a variable that's create-able and save-able, that's the Data Object. That has specific createObject and saveObject properties, where you define what should happen when they're called by the XPages runtime.
Similarly, like the other scoped variables, it needs to be serialized, so you cannot store a Domino object in them. So you can't store a NotesDocument in them. You need to wrap a normal Java object around a NotesDocument. With a greater understanding of XPages it becomes apparent that's what the dominoDocument datasource is doing (creating properties for all fields on the document, storing its note id, UNID, adding other properties like whether it's in edit mode or new etc).
A final point, as Jesse says, dataContexts are re-evaluated multiple times during a partial refresh. I haven't re-tested recently, but under 8.5.3 dataContexts bound to an XPage or Custom Control were re-evaluated more than dataContexts bound to a Panel, so I'd recommend the latter.
The immediate problem that you're running into is that #{}-bound dataContexts are re-evaluated constantly, several times during a page load and, I believe, every time they're referenced. Generally, the rule of thumb with dataContexts (and don't get me wrong - I like them) is that they should either be extremely low-cost, like a quick mathematical calculation, or be ${}-bound. The latter wouldn't work here, though, since the document wouldn't survive past the first load.
The tack you may want to try is to use a dataContext like this:
<xp:dataContext var="docData" value="${javascript: new java.util.HashMap() }" />
Basically, using a simple object as a holding pen. Then, in the save action, create the new document and set all of the values from "docData" there, like:
var db = sessionAsSigner.getDatabase("", "privDb.nsf");
var doc = db.createDocument();
doc.replaceItemValue("firstname", docData.get("firstname"));
doc.replaceItemValue("lastname", docData.get("lastname"));
doc.save();
There are a few caveats to this approach:
Each save will create a new document, rather than editing the existing one. You could change this by storing the UNID back into the map and fetching it from the DB, though
It should work as-is in this case, but you'll have to be mindful of data types. For example, if you have a multi-value control, then the XPages runtime will probably create an ArrayList, which you would have to convert to a Vector for storage unless you're using the OpenNTF Domino API
And as a final note, that binding you have for firstname is almost definitely not what you want. That may just be an artifact of the testing you were doing, but I'd be remiss if I didn't mention it.
I have a number of custom controls layed out on an xpage. I want these controls to be displayed in the order of a setting in a notes document
So my xpage might look like this in DDE
CustomControl1
CustomControl2
CustomControl3
CustomControl4
but when the custom control is displyed on the webpage the custom controls should be displayed in the following order (based on the setting document)
CustomControl4
CustomControl1
CustomControl2
CustomControl3
anyone have any ideas how to accomplish this (server side)
You can use the xp:switchFacet in combination with a xp:repeat to calculate the order at runtime like this:
<xp:repeat
id="repeat1"
rows="30"
var="rowEntry">
<xp:this.value><![CDATA[#{javascript:var arr = ["Control1","Control3","Control2"];return arr;}]]></xp:this.value>
<xe:switchFacet
id="switchFacet1"
selectedFacet="#{javascript:rowEntry}">
<xp:this.facets>
<xp:panel xp:key="Control1">Control1</xp:panel>
<xp:panel xp:key="Control2">Control2</xp:panel>
<xp:panel xp:key="Control3">Control3</xp:panel>
</xp:this.facets>
</xe:switchFacet>
</xp:repeat>
Instead of the Array arr you can use data based on a document or xsp.propertie. The Output of this code is Control1 Control3 Control2 and in your Designer you have your controls inside switchFacet in following order: Control1 Control2 Control3.
Without knowing the real pain of your project I can only assume that the individual position needs to be defined at runtime, or something like that.
In general: controlling a page's layout is a pure CSS job. On the resulting page almost anything can be positioned almost anywhere within the page's range, and it doesn't matter where it was placed at design time. So, if you enclose your custom controls within their own containers (panels / divs) then you should be all set. You might define positioning classes in CSS and then have some SSJS code decide which class is assigned to what div.
If for example you have a custom control for each day of the week, and you always want to have the current day at the top-most position, then you could define 7 css classes as in ".today", ".todayPlus1", ".todayPlus2" ... ".todayPlus6". Write your positioning rules for each class. And finally compute each panel's styleClass property based on the current week day.
I realized my question was too vague on adding navigation items dynamically, so I am rewriting the question.
I have discovered the xe:dominoViewEntriesTreeNode control from the xpages. I think I can use this to add navigation items to the navigator control based on entries in the view.
I am struggling to find very much in the way of documentation or resources that break down how to do that. Can anybody to me to a good reference or example code?
You can use dominoViewListTreeNode to build a menu based on views in a database (and not documents in those views).
Here is an example of using xe:dominoViewListTreeNode to dynamically build a menu based on all views called "Test*" (using regex in the filter property). When selecting a menu item from the menu, the name of the view is submitted to the server (using EL notation for the viewEntry.getName() method).
The example also contains an onItemClick event handler that "catches" the name of the view as the submitted value and stores this in a sessionScope variable. The event handler then redirects to a views.xsp XPage that could contain a Dynamic View Panel control where you could use the sessionScope variable to control what view to show.
The sessionScope variable is also used to mark the selected menu item as "selected".
<xe:navigator id="navigator1">
<xe:this.treeNodes>
<xe:dominoViewListTreeNode filter="Test.*" submitValue="#{viewEntry.name}" var="viewEntry">
<xe:this.selected><![CDATA[#{javascript:viewEntry.getName() == sessionScope.clickedView}]]></xe:this.selected>
</xe:dominoViewListTreeNode>
</xe:this.treeNodes>
<xp:eventHandler event="onItemClick" submit="true" refreshMode="complete">
<xp:this.action>
<![CDATA[#{javascript:sessionScope.clickedView = context.getSubmittedValue();
context.redirectToPage("views.xsp");}]]>
</xp:this.action>
</xp:eventHandler>
</xe:navigator>
Instead of the onItemClick method to redirect to an XPage, you could compute the href property of xe:dominoViewListTreeNode to return the name of an XPage.
I have a short presentation called "XPages Extension Library - Create an app in 1 hour (almost)" that presents this technique (and other techniques).
I assume you have an area on your page carrying the navigation items, e.g. links to some pages with link texts?
I would then use a repeat control with a datasource/javascript source that returns the document item values from your profile document or something.
If you are not into repeat controls then you should consider to read this: http://xpageswiki.com/web/youatnotes/wiki-xpages.nsf/dx/Work_with_repeat_controls
On this page there is also a sample dealing with a profile document.
By the way: using profile documents was always a crutch, so consider to youse "normal" config documents instead.
how can I add custom control on the basis of sessionScope variable. I try include page container control but no luck:
<xp:this.afterPageLoad><![CDATA[#{javascript:sessionScope.put("viewName","ccViewAll.xsp");}]]></xp:this.afterPageLoad>
<xp:text escape="true" id="computedField1">
<xp:this.value><![CDATA[#{javascript:sessionScope.get("viewName")}]]></xp:this.value>
</xp:text>
<xc:appLayout>
<xp:this.facets>
<xp:panel xp:key="facetMiddle">
<xp:include id="include1">
<xp:this.pageName><![CDATA[${javascript:sessionScope.get("viewName")}]]>
</xp:this.pageName>
</xp:include>
</xp:panel>
</xp:this.facets>
</xc:appLayout>
The above code give me error Error 404 HTTP Web Server: Item Not Found Exception. But when I hardcode the viewname that is ccViewAll.xsp instead of sessionScope.get("viewName"), its work fine.
-MAK
You can use the dynamic content control or the switchFacet control if you have the ExtLib for XPages. The Teamroom template (demo application that comes with the ExtLib) uses these in the "allDocuments" Xpage or the "allDocsAllTab" custom control, these are good examples to look at.
If you don't have the ExtLib you could use the loaded / rendered property of a custom control to decide which one gets loaded.
e.g.
<xp:panel key="MiddleColumn">
<xc:customControl1 loaded="${javascript: if(viewScope.control == "customControl1")}"></xc:customControl1>
<xc:customControl2 loaded="${javascript: if(viewScope.control == "customControl2")}"></xc:customControl2>
</xp:panel>
loaded = false means that nothing will be done for this control.
rendered = false means that the control will be created but hidden, you can change this later to show it.
use rendered if its going to change for example when a button is clicked and loaded when its decided at start up and won't change while the user is logged in.
If you are using this to show a different view in the domino database based on some other selection that I would suggest looking at the Extension Libraries 'Dynamic View Panel' control.
Using this control means you won't need to create different custom controls for each view that you want to use, just a single page with this control and point it to the correct view to display via a scope variable.
If you need to customize how each view displays you can create a viewControl bean to set additional properties based on the view that it is showing.
Something worth to mention is that if you don't use the ExtLib - If you're using Partial Refresh then you HAVE to use the rendered property, since the loaded property only can be calculated on pageLoad.
From my understanding this means that every custom control will be computed by the server and that's probably not something you want. (Added overhead, for one thing.)
/J