In my Xpages application I am using Bootstrap for theming. I have a xp:messages box that I have applied some Bootstrap CSS to.
When then are no messages I want to hide the panel that contains the messages. So I came up with the following:
<xp:panel rendered="#{javascript:facesContext.getMessages().hasNext()}" styleClass="alert alert-info">
<xp:text escape="true" value="#{javascript:facesContext.getMessages().hasNext()}">
</xp:text>
<xp:messages globalOnly="true" layout="list"></xp:messages>
</xp:panel>
However I notice sometimes the xp:messages control is empty but facesContext.getMessages().hasNext() returns true.
Is there another way to check the value of xp:messages? I tried getComponent(...).getValue() but than the application breaks.
This is what is returned to the browser:
<div class="alert alert-info">
<span>true</span><span role="alert"></span></div>
I write based on your example code, that is, you want to deal with global messages only. It's important that you pass a null value to the getMessages method. In that way you will be filtering for the global messages only or, in other words, messages that are not tied to a specific component.
All messages: facesContext.getMessages()
Global messages only: facesContext.getMessages(null)
Messages for specific component: facesContext.getMessages('whateverIdYouHave')
have you tried to directly apply the bootstrap class to the xp:messages control? e.g.
<xp:messages id="messages1" infoClass="alert alert-info"></xp:messages>
hereby you do not need the surrounding panel and compute the rendered property
You want to wrap that call into a function you keep in a SSJS library and use something like rendered=JavaScript:hasMessages() That makes is easier to understand the code later.
Inside the function you should be able to either iterate over the messages and check if they are empty (or eventually: messages you don’t care about it).
The JSF spec has getMessageList() that returns a Collection you can check with isEmpty(). Not sure if it has been implemented in XPages. Paul, as usual, has more info here:
http://www.intec.co.uk/how-many-xpages-errors-have-i-got/
The counter seems most reliable
Related
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 ask this just to be clear here: I accidently set the createForm property to false. I then expected a link event that should open another page only not to function any more.
Is this the intended behavior of SSJS events e.g. in links when you disable form creation?
As Per mentions, all events require a form: if they are full refresh, then the page needs a form to post to in order to trigger the redirect; if they are partial refresh, the form determines the contents of the AJAX POST.
The XPages runtime includes support for a form component, but it is not included in the component palette (and it cannot be added via Designer Preferences), so the only way to add it to a page is by editing the source XML directly. For example:
<xp:form>
<xp:link id="exampleLink" text="Example Text">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:// event code}]]></xp:this.action>
</xp:eventHandler>
</xp:link>
</xp:form>
There are (at least) two reasons why this component is useful:
It can improve performance. If a page contains different areas that are functionally distinct -- in other words, an event in one portion of the page does not need to be aware of data in any other portions of the page -- then wrapping each section in a separate form component causes each event to only post data that is contained inside the same form as the component that triggers the event. Your question indicates that the link that broke when you set createForm to false should navigate the user to another page; it's likely, therefore, that this event doesn't need to know about any field values, because the user is leaving the current page anyway. If that's true, wrap this link in its own form, and any fields inside a separate form, and the link will perform marginally faster because the browser doesn't have to post any field data... just the identifier for the link itself.
It can provide style flexibility. It's common for a developer to receive, separately from the actual end user requirements, pre-determined CSS from a designer unfamiliar with Domino -- for instance, if the site design is outsourced, or must adhere to corporate style guidelines. This often becomes a source of tension when the designer has made certain assumptions that, at first glance, are incompatible with the markup that Domino generates. One of the most common examples of this is when the site contains search features, because most web developers will have one form for search and a separate sibling form for any other fields. This can increase the cost of a project when either the designer or the developer has to revise a stylesheet that the designer already developed to account for a single form tag surrounding all the content. In short, suppressing the default form element and explicitly specifying form components where needed makes it easier to adhere to externally imposed style guidelines.
So there are many use cases where it's actually preferable to use one or more form components on an XPage... just remember that all data and events must be inside a form -- whether the default form that usually surrounds all the content, or a manually included form component -- and that forms cannot be nested. You can add as many form components as you want, but they must be siblings. No form can contain another form.
Yes, because you are doing HTTP POST requests and they require a form.
You can convert your POST request link to a GET request link instead be removing the SSJS event and creating a "basic" link instead:
<xp:link escape="true" text="Link" id="link2" value="/somepage.xsp"></xp:link>
But if you need the SSJS logic, then you also need to have a form.
I am trying to use the extension library component Remote Service (xe:jsonRpcService). I got some hints from here and here. Basically I am trying to save a document using RPC. The problem is that the document gets saved but it does not save any fields present on the XPage. Below is the sample XPage code:
<?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:this.data>
<xp:dominoDocument var="document1" formName="Test"></xp:dominoDocument>
</xp:this.data>
<xe:jsonRpcService id="jsonRpcService1" serviceName="service">
<xe:this.methods>
<xe:remoteMethod name="saveDoc">
<xe:this.script><![CDATA[print(">> " + getComponent("inputText1").getValue());
document1.save();
return true;]]></xe:this.script>
</xe:remoteMethod>
</xe:this.methods>
</xe:jsonRpcService>
<xp:br></xp:br>
<xp:inputText id="inputText1" defaultValue="testValue" value="#{document1.testField}"></xp:inputText>
<xp:br></xp:br>
<xp:button value="Save" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[var deferred = service.saveDoc();
deferred.addCallback(
function(result) {
alert(result);
}
);]]></xp:this.script>
</xp:eventHandler>
</xp:button>
</xp:view>
What I have done here is that, I created Remote Service (service) where I am saving the current document (document1). It saves the document but does not save the value in inputText1. Also, when I try to print the value of inputText1 it shows on console but it is not getting saved.
Is this the right way to do it? Or am I missing something here. Also what would be some scenarios where usage of xe:jsonRpcService would be justified?
There are (at least) two reasons for avoiding the use of a JSON-RPC for this type of operation:
This service component is designed to be as lightweight as possible, so, unlike standard events in XPages (which automatically post the entire HTML form), it sends only the data required to call the method, and receives only the data returned by the method. In your example, the method takes no arguments, so it's literally just sending enough information for the server to know what method to call; similarly, you're just returning a boolean value, so that's literally all that will be sent back. If you use your browser's development tools (i.e. Firebug or the built-in tools in Chrome) to inspect the network traffic, you'll see this reflected in the JSON packets that are sent in each direction. As a result, the server doesn't "know" anything that you don't explicitly "tell" it. So it's faster than a typical event because you're not posting anything that isn't explicitly related to the method you're calling... but you have to intentionally send everything that the method does need in order to run.
Another side effect of the component's focus on performance is that it skips the component tree serialization at the end of the JSF lifecycle. If you're keeping the current page in memory, that shouldn't be an issue, but if you're using the default option (save all pages to disk), the server will "forget" any changes that are made to the page during the method invocation. You can explicitly override this behavior on a case-by-case basis by directly telling the view root to serialize its state, but it's easy to forget that you have to do this manually, which typically causes a lot of frustration when you see indications server-side that it's doing what it's supposed to but the actual web page doesn't reflect that. It's best to just treat any RPC method as a "read-only" operation unless you're certain you'll always remember this odd side effect and understand how to circumvent it.
My advice is to think of JSON-RPC as "SOAP minus the stupid". Phrased more politely, it's conceptually identical to Web Services, but without all the complexity of Web Services. As such, these types of services are ideal for data operations that are useful within the context of the current page without being explicitly tied to the state of the current page.
Here are some examples of operations where a JSON-RPC method might be useful:
Informing a new user whether the user name they're choosing for their new account is already taken. Instead of binding a standard keyup event which would post the entire form, send only the value of the one field to the service, query it against the site registration records, and send back a boolean.
Live polling of related data that is prone to frequent updates. Suppose you're developing a CRM, and you want to display the stock price of the company whose account you're viewing. Using setInterval to periodically ask the RPC for the updated stock price, then doing manual client-side DOM manipulation if the price changes, again allows you to perform a somewhat complex operation with a minimum of network overhead.
This doesn't mean that you can't use an RPC for write operations... but for any operation that needs a complete, up-to-date context (i.e. current value of every field on the current page) to run correctly, a standard event handler is nearly always the better approach.
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
I have a custom control that has a property I have defined. I use the composite data a label which is on the custom control. The label displays just fine but I wanted to use the value of the label on a control that is outside the control with the label and composite data.
But it is returning null for the value of the label. It seems like that composite data is being calculated every time the label value is accessed and the reference point for the calculation seems to be the control accessing the label value rather than the control where it is contained.
I plan to use scope variables instead but is there any way I can make composite data work?
Composite data only exists inside the custom control that defines it. As such, it is far easier to reach outward than to reach inward.
In addition to scope variables, an alternative is to define a dataContext. This is technically still a use of scope variables, as it pushes a variable temporarily into the requestScope, but is slightly easier to work with because you don't have to explicitly tell it to do so... it just does it. For example:
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:this.dataContexts>
<xp:dataContext
var="labelValue"
value="#{someExpression}" />
</xp:this.dataContexts>
<xp:text
value="#{labelValue}" />
<xc:labelContainer
labelValue="#{labelValue}" />
</xp:view>
In the above example, everywhere within the container to which I've attached this labelValue dataContext, I can just refer to #{labelValue}, and it will return whatever value the expression for that dataContext returned. This provides you, then, a single variable that can be used within the XPage itself, but also passed into custom controls.
I like Tim's suggestion with the data context. However if you have to, you could access the value on the propertyMap of the control. This wouldn't be the label but the composite data. This is how components could 'talk back'