How can we use ONLY client side script for "hide/whens"? - xpages

I am working on a large, worldwide application, which includes access from areas of low bandwidth. As such, I want to use a minimum of SSJS or partial refreshes for all the complex hide/when calculations. Here is what I have so far for a simple "hide/when":
A Yes/No radio button, with CSJS to show a panel ("Yes") or hide the
panel ("No").
The panel has a formTable inside it, and the values are shown or hidden, as per #1.
In the XPage's onClientLoad, the following code is run:
// "getRadioValue" is a simple script to return the value of a radio button
var v_value = getRadioValue("#{id:radioButton}");
v_div = '#{javascript:getClientId("radioButtonPanel")}';
// show or hide div simply use dojo to change the display of the panel
if (v_value == 'Yes') {
showDiv(v_div);
} else {
hideDiv(v_div);
};
For a new document, the onClientLoad script will hide the "radioButtonPanel" successfully. Changing the radio button to "Yes" will show the radioButtonPanel, just as clicking "No" will hide it. It works great! :-)
Once the document is saved and reopened in read mode, though, the onClientLoad CSJS event should read the saved value in the document, and decide to show the panel or not. When the document is opened in edit mode, the onClientLoad fires, reads the radioButton value and successfully shows or hides the panel.
This is what I've tried so far, to get it to work in read mode:
In CSJS, using "#{javascript:currentDocument.getItemValueString('radioButton'}" to get the value,
Doing some calculations in the "rendered" or "visible" properties, but that's SSJS and, if hidden, prevents any of the "show/hideDiv" CSJS visibility style changes.
Adding an old fashioned "div" to compute the style (which is what I used to do before XPages), but since I can't do pass-thru html any more, I can't seem to get a CSJS calculation for the style. Ideally, I can do something like this:
<div id="radioButtonPanel" style="<ComputedValue>">
Where the ComputedValue would read the back end value of the document, and decide to add nothing or "display:none".
Note that I don't want to use viewScopes, since this long form would need many of them for all the other hide/when's.
Is there any way to make this 100% CSJS? I feel like I'm very close, but I wonder if there's something I'm just missing in this whole process.

First, rather than computing style, I'd recommend computing the CSS class instead -- just define a class called hidden that applies the display:none; rule. Then toggling visibility becomes as simple as a call to dojo.addClass or dojo.removeClass.
Second, I see that you're using the #{id:component} syntax to get the client ID of the radio button but using SSJS to get the client ID of the panel. Use the id: syntax for both; this is still just a server-side optimization, but if there are many instances of these calculations, it adds up. Similarly, replace #{javascript:currentDocument.getItemValueString('radioButton'} with #{currentDocument.radioButton}. Both will return the same value, but the latter will be faster.
Finally, any attribute of a pass-thru tag (any component with no namespace, like xp: or xc:) can still be computed, but you'll need to populate the expression by hand, since the editor doesn't know which attributes for valid for these tags, and therefore doesn't provide a graphical expression editor. So if the ideal way to evaluate the initial display is by wrapping the content in a div, the result might look something like this:
<div class="#{javascript:return (currentDocument.getValue('radioButton') == 'Yes' ? 'visible' : 'hidden');}">
<xp:panel>
...
</xp:panel>
</div>

Related

xpages dojo validation textbox required

I have dojo validation textbox in my xpage with required property computed (compute dynamically) as below
var syn = getComponent("SynCboxGrp").getValue();
if (syn == "Yes")
{return true;
}else{
return false;
}
Here the component SynCboxGrp is radio button. The text box is required based on the radio button value. But its not working properly. Changing radio button value doesnt change the required behaviour.
Appreciate any help
Update
Thanks stwissel, Per Henrik Lausten and Eric.
I only have this ssjs in required property
<xe:djValidationTextBox id="UBOCAgent" value="#{dsRacDoc.UBOCAgent}" disableClientSideValidation="true">
<xe:this.required><![CDATA[#{javascript:var syn = getComponent("SynCboxGrp").getValue();
if (syn == "Yes")
{
return true;
}else{
return false;
}
}]]></xe:this.required>
</xe:djValidationTextBox>
I also tried this partial refresh code on my radio button onclick event XSP.partialRefreshGet("#{id:UBOCAgent}");
This still doesnt change the behaviour. It works based on the initial radio button value. On the negative side since its a get request it updates the field content from the server. I also tried Eric's suggestion disable client validation but that didnt help.
Eric, I am also trying to go with CSJS wherever possible but in this case the required property only has the SSJS option. So not sure how to try CSJS. Should i try creating my own dojo field instead of using one from entension lib? If so i am not sure how to compute required property for that. If you can help me with some sample code that would be great help.
Thanks for your time.
You're doing a partialRefreshGet, which is updating the Dojo Validation Text Box. But you never post back the updated value from the radio, so the server-side value for the radio button is still the initial value. Consequently, required is still false.
Check the markup when required is true on the Dojo Validation Text Box, but the validation triggers client-side, so there should be an attribute in the markup that you can manipulate via CSJS, if that is your preferred route.
Do you have particular latency issues that you need to work around? Picking up on this and the other question you have about doing a lookup to a database on click of a button, you're hitting a few problems. I don't know how experienced you are in XPages development, but it's not an approach I would recommend without a good understanding of client-side output and server-side component trees. The initial page load of your XPage will also be slower and the size of the HTML passed to the browser will be larger, because of the amount of CSJS passed to the browser.
I would recommend using the in-built partial refresh except for situations where browser to server network is a significant issue, and only then falling back to coding your own partial refresh posting a subset of data. In my experience, it's easier and quicker to develop, easier to debug and more flexible.
For this particular scenario, regardless of whether you code the partial refresh yourself or use inbuilt functionality, one point I'm not certain of is this. That once you set validation on the Dojo Validation Text Box, that validation runs client-side and may impact any attempt to un-set the required property by posting to the server. I haven't tested, so can't be certain.
One of two things:
as previously mentioned, to trigger your SSJS value change, ensure a partial (at least) refresh of the element containing your above code; can be done with a container element. Also, check to see if your field's disableClientSideValidation parameter is set (All Properties view).
convert your SSJS code (which merely returns a binary assessment of a Yes or otherwise, I'm assuming No, value) to CSJS
Of late, after the primary development of my current project, I have started favoring the CSJS approach, for the sake of decreasing server-side call backs; most especially in situations where display/rendering of a component is all I'm trying to achieve. If you go this route, remember that dojo.byId("#{id:myControlsServerNameHere}").value (for a text field, see below for scraping a radio button value) combined with setting the display or visible CSS properties can be very handy. As the docs describe, if you want it to exist on the page but not show (for formatting purposes, also, can preserve default value), go the visibility route, otherwise display property.
The CSJS scrape of radio values that I'm currently using is as follows:
var result=null;
for(i=0; i<document.forms[0].elements.length;i++){
if(document.forms[0].elements[i].name=="#{id:serverNameOfRadioElement}"){
if(document.forms[0].elements[i].checked == true){
result=document.forms[0].elements[i].value;
break;
}
}
}
//then handle your result, either with an if, or switch statement
Hope this helps. If there's more you're having trouble with, post back with more code to give the bigger picture.

How can I know what form control had focus when ribbon button pressed?

Have a custom JS function that gets called when a ribbon button is pressed in the context of a form. In my custom JS function I need to know what form field had the focus just prior to the ribbon button being pressed. I've tried 2 ways (below) without success. Is there any way to do this reliably?
Way #1
According to this, I can get the control I want passed as a parameter to my JS. I've tried using both PrimaryControlId and PrimaryControl parameters.
<JavaScriptFunction FunctionName="OnCustomBtnFunc"
Library="$webresource:myJSfile.js">
<CrmParameter Value="PrimaryControlId" />
<CrmParameter Value="PrimaryControl" />
</JavaScriptFunction>
For both, I get an object passed to OnCustomBtnFunc() but it does not seem to enable me to determine which form control had the focus prior to the ribbon button being pressed.
Way #2
I call Xrm.Page.ui.getCurrentControl(). This works for form fields of some types but not others e.g. if it is a string field it works but I get null for a lookup.
you won't like this answer, but you can use JQuery to quickly grab every Control in your Form and assign it an OnBlur event. This event can either assign the ID or the control itself to global variable, which you will make accessible to your ribbon (which fires from a different scope).
People do not like this approach because of the different context scopes of the variables and because it involves playing with "unsupported" features of plain HTML. However, if you ask only the question "how" and not "should we", then this is an easy way for "how" you would do this.

XPages remove documents on server and trigger partial refresh

I am struggling with the following.
On my XPage I have a viewpanel component, but it is not bound to a notesview datasource, but to a hashmap stored in viewScope. Reasons for this is beyond scope of my question.
Since the lines in my view are not actually linked to the documents I cannot use the standard checkboxes and the related getSelectedDocIds. I do however want a way to remove the selected documents. I have a column with checkboxes containing the unid of the corresponding row.
So long story short. I have an array of unids and want to perform an action that does the following:
Display a dijit.Dialog asking for confirmation
If OK clicked call a function that does the following:
Remove the documents based on the unids
Refresh the viewpanel
I am thinking of the following 2 solutions, but in doubt what would be best (maybe a third, even simpler solution?)
Have the OK button of the dojo dialog call a function that does an XmlHttpRequest to an XAgent or plain old LS agent
Have the OK button trigger an eventhandler that runs on the server as described by JeremyHodge here. But how would I pass the unids as parameter and refresh the view afterwards?
Thanks!
Cant you just make use of the extension library dialog with the dialog button control. In this button control you can then
A third option would be to add a column to your datatable/view which contains checkboxes. On the onchange event of these boxes you add an eventhander which adds the value to a viewScope variable.
A button on the bottom (or top.) of the page you add the code you need to remove the selected items from the hashmap, delete the documents associate with the selected id's. this button can be a ordinary button with a partial refresh on the viewpanel. When you run into the bug that you cant use buttons in a dialog please use the extension library dialog control because this fixes that issue for you.
If the current user does not have the correct access level to delete documents you could use the sessionAsSigner global (assuming the signer of the design element has the correct access levels).
This way you dont need to go call an xAgent by xmlthttprequest and can stay with the default xpage methodology.
I hope this helps in some way
I would second #jjbsomhorst in the use of the extension library for the dialog box - if you use one at all. Usually users don't read dialog boxes. So the approach would be add the column with the checkboxes, but don't bother with an event handler, but bind them ALL with their value to ONE scopeVariable. On submission that variable will then hold an array with the selected UNID.
Then render a page that lists these documents and have a confirm button. While the new page affords a server round-trip the likelihood, that users actually pay attention is way higher. What you can do:
Have the normal page that renders the dialog with editable checkboxes and when the user clicks "Delete" you set something like viewScope.confirmDeleteMode=true; and use that as condition for the checkboxes and make them read-only AND set the class of the selected rows to "morituri" which in your CSS would have something like .morituri { color: white; background-color : red; font-weight: bold } and a new button "Confirm Delete" (and hide the Delete button).
This way you only have one page to deal with.
I went for option 2, which has the possibility to provide the partial refresh id. I passed the unids as a submitvalue like:
function doRemove(unids){
XSP.executeOnServer(ISP.UI.removeEventID, ISP.UI.removeRefreshID, {
params: {
'$$xspsubmitvalue': unids
},
onComplete : function() {
//alert('test')
}
});
}
The ISP.UI.removeEventID performs the following code:
var unids = context.getSubmittedValue();
removeDocuments(unids); //SSJS function performing the actual delete
viewScope.reload = 'reload' //triggers the hashmap to be rebuild based on new documentcollection

The buttons in a repeat don't hide/show after refreshing the panel the repeat is in?

I have a repeat that has buttons embedded in it. The repeat is in a panel. When the user clicks a button the button should hide/show (I partial refresh the panel). The repeat is tied to a Domino view and I see the other values that I from that view get updated in the repeat, so, it does not seem like a view index issue (I refresh the view index in my code.)
If I use context.reloadPage() in my button onclick code then the buttons will hide/show like they should, but, that seems like I am using a sledge hammer! Any suggestions on how to get the buttons to recompute the visible property when the panel that holds the repeat is rendered? Another strange thing is that the visible property gets computed three times whenever I refresh the panel that holds the repeat.
thanks, Howard
I think your looking for
getComponent("<id>").setRendered(true / false);
Hi For Repeat control's entry is used to make our head hard. Because handling the entry by SSJS, we can get the value and set the value. But rendering part, id of the repeating component are same for all. So if we try to give reder as false. It hides all of our repeating component.
Try to use the following., [Put this in button onclick, and see the value of below]
var entryValue= getComponent("repeat1").getChildren().get(0).getValue()
getComponent("inputText1").setValue(entryValue)
But in client side we can easily handle. Because the id of the DOM object is unique for all repeating component.
var id1="view:_id1:repeat2:"+'2'+":button1"
document.getElementById(id1).style.display="none"
This will hide the third entry of your repeat control component.
Please see the post, You may get better idea
Found a solution. My original solution was getting values from the repeat rows (using the collection object, which was a viewentrycollection and using getColumnValues) to compute the rendered property for the buttons.
Instead, I created a viewScope variable (a Vector) that holds the state of the buttons (which set of buttons to show). This gets populated in the beforePageLoad event of the page.
The button onclick code updates this viewScope variable after performing its processing. All works very nice now. I guess it was something in the JSF lifecycle that kept the buttons from being properly updated. Using the viewScope variable works fine.
With addition to what Ramkumar said, you can use the index variable in the repeat control to identify each and every occurrences inside the repeat control. You will get more idea, if you inspect with element from firefox[You might need firebug]. Usually the field mentioned inside a repeat control itself can be considered as an array

Watir : How do we capture the subitems displayed by doing mouseover on a particular item

I am trying to work with mouseover on a particular Item in my application by using the following command
{
ie.text_field(:xpath, "//a[contains(text(),'Deal')]").fire_event('onmouseover')
}
On doing mouseover on a item, two subitems are displayed.
Is there any way to capture the sub items which are part of the Item by doing mouseover with which we can report that our test is pass or fail.
Please suggest.
Additional Information :
If we take example,On the StackOver flow page, If i do mouseover on my name, i get a window where i see activity, privileges, Logout and other stuff. This is really what i was looking for. Is there a way to capture the items displayed on the window on doing mouseover.
I also tried to capture the subitems with the following :
{
text=ie.text_field(:xpath, "//a[contains(text(),'Deal')]").fire_event('onmouseover')
puts(text.inspect)
}
On doing this "text" value is displayed as 'nil'.
My general tactic for such things is a combination of using IRB and the IE Developer tool, or Firebug.
using IRB, type out (or cut and paste) the watir statement to fire the onmouseover command to the proper element on the page.
Then have the developer tool rescan the DOM (there's a little refresh icon you can click) The use the tool to point to an element to point to one of the items in the stuff exposed by the onmouseover. Using the info from that, you can then figure out how to address those elements to get the text from the proper div, etc.
If I do that here to the info that opens up when I float the mouse over my name I can find out that it is a table of class "profile-recent-summary" Furthermore I can then look at the way the table is made up and see for example that the 'today' reputation value is in the second cell on that row.. The row also has the text 'reputation' in it.. so
browser.table(:class, 'profile-recent-summary').row(:text, /reputation/).cell(:index, 2).flash
Should flash the cell I want (index might be 1 if using firewatir or webdriver) I can then replace the .flash with something else like .text if I want to get the text from that cell (which is actually inside a link that sits in the cell)..
without seeing your code, I would 'inspect' the element that you are trying to verify and when_present (text) assert that its true

Resources