Making dynamicContent control dynamic - xpages

I'm having difficulty getting a dynamicContent control to work the way I would like it. I found this bit of code in pasteBin and I think it might just be what I need, but I would like to understand what it is doing before I try implementing it.
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:button value="Switch!" id="switchButton">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="dynamicCustomControl">
<xp:this.action><![CDATA[#{javascript:viewScope.controlName = 'cc_test2.xsp';
getComponent('dynamicCustomControl').show(null)}]]></xp:this.action>
</xp:eventHandler></xp:button>
<xe:dynamicContent id="dynamicCustomControl">
<xp:include id="customControlInluder">
<xp:this.pageName>
<![CDATA[${javascript:viewScope.controlName||"cc_test1.xsp"}]]>
</xp:this.pageName>
</xp:include>
</xe:dynamicContent>
</xp:view>
In particular I don't understand the syntax of this line:
<![CDATA[${javascript:viewScope.controlName||"cc_test1.xsp"}]]>
I prefer to use viewScope.get("controlName") rather than the short form viewScope.controlName but I don't understand the significance of the || in this line of code.
also the line
<xp:include id="customControlInluder>
is probably an inconsequential typo.
The process looks fairly simple and would appears that it would do the job for me. Just want to make sure I understand it before going down that road.
Edit and update ---
This very brief code snippet might just be one of the best kept secrets around. I have just worked through it and each of the Custom Controls displayed withing the dynamicCustomControl contains a dynamicContent control. So was able to very nicely embedded a dynamicContent inside a dynamicContent. Which to this point I was never really able to get to work correctly. Now it works very smoothly with minimal fuss & muss. Thanks for the comments and assistance.

The JavaScript code viewScope.controlName||"cc_test1.xsp" returns as result the value of viewScope.controlName if it is not
0
null
undefined
NaN
"" or '' (=empty string)
false
Otherwise it returns "cc_test1.xsp". You can find a detailed explanation here.
This is a short and tricky way of
viewScope.controlName ? viewScope.controlName : "cc_test1.xsp"
or
if (viewScope.controlName) {viewScope.controlName} else {"cc_test1.xsp"}
The id in <xp:include id="customControlInluder"> is not used in code so it doesn't matter how it is called although "customControlIncluder" would sound much better.
I always use the short version for scope variables for getting and setting values and haven't had issues with that yet.

Bill, think it is just saying "use the viewscope control name, and if null then use cc_test1.xsp". If the first case is true, then the second isn't evaluated.
JavaScript OR (||) variable assignment explanation

Related

Hiding xp:mesages box when empty

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

dominoDocument data source returning null on open on specific XPage

I'm having a problem where I can't Open/Create documents using a specific XPage in my application.
For simplicity sake, I have 2 XPages, one called XSP1.xsp and one called XSP2.xsp.
Each, at the top of the page, has a declared dominoDocument:
<xp:this.data>
<xp:dominoDocument var="XSP1Doc" formName="XSP1">
</xp:dominoDocument>
</xp:this.data>
Each page has, amongst other things on it, buttons that I'm hiding using the following code:
<xp:button styleClass="btn btn-primary" value="Label" id="myButton">
<xp:this.rendered><![CDATA[#{javascript:XSP1Doc.getItemValueString("Status") == "myStatus"}]]></xp:this.rendered>
</xp:button>
When I open XSP1.xsp either with or without a ?documentId parameter in the querystring, it opens. When I open XSP2.xsp, it does not open, but rather tells me that:
Script interpreter error, line=1, col=9: 'XSP2Doc' is null
I cannot for the life of me determine what is different between the two pages. Even further and stranger, I can open documents created with XSP1.xsp in XSP2.xsp, and documents created in XSP2.xsp in XSP1.xsp, but I cannot open XSP2 with no document, or with a document created with the form used in XSP2.
I realize this may be confusing, please ask for clarification if necessary.
Edit:
I removed the buttons and the page loaded, and it loaded the back-end variables (assuming I had a documentId in the querystring). If I create a document using this page, then I can in fact open it. I still can't open the page if the buttons exist (which, if I have to test for null isn't a big deal, but I don't get why it would be different between two different pages.)
One thing I noticed is that the way I'm creating the XSP2 document, it doesn't have a $Revised field for some reason. Could this be causing this problem?
Edit 2: Well, I just copied XSP1, deleted everything in it, and recreated XSP2, and it works fine now. Still no idea what's different but at least I'm past it.
Interested in an answer so I'll leave this up.
Edit 3: Turns out I was doing something in a field that was recycling an object with the name XSP2 due to some shenanigans. I don't think this is a real issue. Not sure what do do here. Delete this question?

Xpages Combobox setting values in managed bean

I am trying to pass arguments to a managed bean.
The bean is configured and works, has two methods "getResponsible" and "setResponsible".
Calling "myLookup.responsible" works.
I cannot pass arguments to my bean, and can't figure out why.
The below code does not work.
<xp:comboBox id="comboBox1">
<xp:selectItems>
<xp:this.value><![CDATA[#{myLookup.setResponsible("Something")}]]>
</xp:this.value>
</xp:selectItems>
</xp:comboBox>
As soon as I type paranthesis ")", "(" or semicolon ";" i get error "Error in EL syntax". I guess I am making some fundamental mistake here.
The version of expression language does not allow parameters to easily be passed. This option may work http://blog.defrog.nl/2012/04/settings-bean-parameterized-method-call.html.
If parameters are required, I usually use SSJS, so:
#{javascript:myLookup.setResponsible("Something");
If the options won't vary during the page's life, you can always compute on page load, so:
${javascript:myLookup.setResponsible("Something");
I think you just made a simple "typo" as Paul stated indirectly in his reply. You wrote Javascript code but did not include the "javascript:" in the beginning of your expression.
If, however, you do want to use arguments with EL then have a look at this very interesting article. I haven't tried it out myself yet (but am going to do shortly) - but the two different examples (have a look at the comments) seem very interesting when you want to use EL. And I prefer EL over SSJS.
/John

xpages set value to field before action=newDocument

I have a simple button with the eventHandler:
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:openPage target="newDocument" name="/doc.xsp"></xp:openPage>
</xp:this.action>
</xp:eventHandler>
In lotusScript I would add a value to a field, before composing the form, using:
Call doc_new.ReplaceItemValue("txt_codformularmain","01")
....
....
Set uidoc = w.EditDocument ( True, doc_new )
I tried in the postNewDocument event of the doc.xsp
<xp:this.data>
<xp:dominoDocument var="Contr" formName="(fmFormularCIP)">
<xp:this.postNewDocument><![CDATA[#{javascript:Contr.replaceItemValue("txt_codformularmain", "01")}]]></xp:this.postNewDocument></xp:dominoDocument>
</xp:this.data>
I don't want every time the doc is created that value to be, let say, 01, BUT only when the doc. is composed by a specific button. Some other button will have the chance to add the 02 value for that field, and so on.
How can I achieve this in xpages development? Thanks for your time.
This is a little bit tricky in Web application development, because what you did in classical Notes development cannot be applied here. I had a lot of issues in this.
The classical scenario is that you have a Page X with some value (say txt_codformularmain will be 01). How do you decide this value "01"?
In some cases, this value is something you have in the page X. So you want to pass a specific value to the target page. One option could be using a query parameter (doc.xsp?myValue=01) and consume it on doc.xsp with param.myValue. Or, you might put a sessionScope variable before going to the target page and consume it by sessionScope.myValue.
It depends on what you need. Query parameter is somewhat safer, because the user might use the same button twice and sessionScope variable causes inconsistent behaviour. On the other hand, query parameter can be changed by the user, so you might have a breach in your application.
Some cases, you want to populate some values at the second page. Simple values (e.g. creation date, user names, etc.) can be populated with forms (i.e. compute value on load). Sometimes it would be more complicated (e.g. the department of the user where you have to lookup some views). For such cases, you need to use postNewDocument event at the target page.
After you have passed the value to the doc.xsp page, you will consume in the way you need. If it's not going to be edited on the page, you may again use postNewDocument event and set value by dataSourceName.replaceItemValue('itemName', param.myValue). If the value can be edited, you would use it as a default value on your field component.
One problem I have experienced: Once you set a value in postNewDocument, you may have problems to change it later, until the document data source saved. This is happening on specific cases. If you experience such a problem, you might use some editable field with readonly attribute. Just keep in mind :)

why does xPage process my Comments as though they weren't

I got a strange behavior in XPage, with commented Code.
I had a textfield with a lot of code in it which outputs html where I hit this issue.
While developing I had a lot of trouble with some fields other fields so I decided -to get a better idea what the field is doing- to copy the code from the troublefields to a comment inside my textfield to have the other code at my sight. But then the xPage started to behave strange until I found the issue.
The Code below is a example what caused my issue, it has two text fields in it one which sets a scope var and also has a comment which sets the same var but commented, ant another one which shows the ScopeVar. I thought this would output 'where I am' in the second text box but instead I got the 'Huhu I am here'.
<xp:text escape="true" id="computedField7">
<xp:this.value><![CDATA[#{javascript://
sessionScope.put("findme","where i am");
/* #{javascript:sessionScope.put("findme","HuHu I am here!");} */
return sessionScope.findme;}]]></xp:this.value>
</xp:text>
<xp:br></xp:br>
<xp:text escape="true" id="computedField6"
value="#{javascript:return sessionScope.findme;}">
</xp:text>
In my original Code where I hit this issue I wanted to comment the old #{} el out to use a JavaScript instead but keep the el in comment in the middle of the code.. same result. It seams that if you use #{ or ${ in a comment it will always get computed!
Got this fixed in notes 9. I am currently using 8.5.3.
Update:
As a small note: Be careful when using the dojoAttribute queryExpr because the query Looks like SSJS "${0}" and also gets interpreted as SSJS. I now use this:
<xe:this.queryExpr><![CDATA[${javascript:"*$\{0}*";}]]></xe:this.queryExpr>
to make it work. Thanks to Paul Stephen Withers for the tip with the \{.
This is a funny bug.
It is caused by pre-processor functionality of JavaScript interpreter. Normally, you can write #{javascript:...} in CSJS code to replace parts of code before it gets put to rendered page.
In your case it is SSJS. Again, the interpreter replaces the #{javascript:...} inside your SSJS code and thinks, work is done. This way you see the code on rendered page instead the result of executed code.
As a workaround, just delete # from /* #{javascript... and it will work like expected.
Is there a reason for having #{javascript:...} nested inside #{javascript:...} in your example. I'd strongly recommend against that, I wouldn't expect it to give good results.
Please see my answer also to stopping getClientId() computations in JS code. I think you're expecting it to work in a way it's not designed and unlikely to be changed to work.
Properties are just strings read at runtime from left to right. If "#{javascript:", "${javascript:" or "#{.." are found, the resulting code is run. Wrapping "//" around them or "/" and "/" either side has no effect, nor would I expect it to. The whole string is not passed to the parser, just the bit after "#{javascript".
The benefits of this are that you can get better performance by combining languages because only part of it gets passed to the parser and literal string (which is what the "/*" bit is) is just passed to the browser as it is. It means you can include EL and SSJS in a single value. I'm not sure you'd be able to do that if the change you're looking for was made.
For queryExpr, I'd suggest using ${javascript:"${0}"} instead - just escaping the {. See p121 XPages Extension Library.

Resources