How to pass var to facet in xpages and update facet? - custom-controls

I want to make switchFacet for practice, but in vain. Actually dont know where to start. Can this be written only in java? Here is my code. I try to pass variable through sessionScope. Button updates value, but facet doesnt. Facet changes value if I click URL and then ENTER.
This is my Xpage:
<xc:FacetContainerCC viewPanelTest="#{sessionScope.VarTest}">
<xp:this.facets>
<xp:div xp:key="SecondCC"><xc:SecondCC></xc:SecondCC></xp:div>
<xp:div xp:key="FirstCC"><xc:FirstCC></xc:FirstCC></xp:div>
</xp:this.facets>
<xp:button value="#{javascript:sessionScope.VarTest}" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:if (sessionScope.get("VarTest") == "First")
sessionScope.put("VarTest", "Second"); else sessionScope.put("VarTest","First");}]]>
</xp:this.action>
</xp:eventHandler>
And my facet:
<xp:callback id="callback1">
<xp:this.facetName><![CDATA[#{javascript:
var viewPanelTest = sessionScope.get("VarTest");
if (viewPanelTest == "First") return "FirstCC";
else return "SecondCC";}]]>
</xp:this.facetName>
I noticed there is a difference between updating page by button and re-entering URL. Button makes POST request, re-entering URL makes GET request.
FirstCC and SecondCC are custom components dropped on facet.
And... sessionScope variables with same name co-exists on client and server, right?

Facets are computed on page load. As long as your page is loaded, the facet won't get recomputed. Thus your facetName will not get updated. Works as designed. The switchFacet loads all facet names but renders out only the selected one.
When you hit enter at the URL you get a new instance of that page (with a new viewScope), while the button does a page refresh (same viewScope) and thus no recomputation of facetName.
The switchFacet was created to accommodate exactly your use case since the existing facet functionality can't cover it.

Related

Link directly to a notebook page in a view

I have an view that extends the current project view, where we add multiple tabs (notebook pages) to show information from other parts of a project.
One of these pages is an overview page that summarizes what is under the other tabs, and I'd like to link the headlines for each section directly to each displayed page. I've currently solved this by using the index of each tab and calling bootstrap's .tab('show') method on the link within the tab:
$(".overview-link").click(function (e) {
e.preventDefault();
var sel = '.nav-tabs a:eq(' + $(this).data('tab-index') + ')';
$(sel).tab('show');
});
This works since I've attached a data-tab-index="<int>" to each header link in my widget code, but it's brittle - if someone adds a tab later, the current indices will be broken. Earlier I relied on the anchor on each tab, but that broke as well (and would probably break if a new notebook page were inserted as well).
Triggering a web client redirect / form link directly works, but I want to show a specific page in the view:
this.do_action({
type: 'ir.actions.act_window',
res_model: 'my.model.name',
res_id: 'my.object.id',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'current'
});
Is there any way to link / redirect the web client directly to a specific notebook page tab through the do_action method or similar on FormWidget?
If I understood well you want to select the tab from the JavaScript (jQuery) FormWidget taking into account that the id could change if anybody install another module that adds another tab
Solution 0
You can add a class to the page in the xml form view. You can use the id of the element selected by this class name in order to call the right anchor and select the right tab item. This should happen when the page is completely loaded:
<page class="nb_page_to_select">
$('a[href=#' + $('.nb_page_to_select').attr('id') + ']').click()
NOTE: As you have said the following paragrah I assume that you know where to run this instruction. The solution I suggest is independent of the index.
This works since I've attached a data-tab-index="<int>" to each
header link in my widget code, but it's brittle - if someone adds a
tab later, the current indices will be broken. Earlier I relied on the
anchor on each tab, but that broke as well (and would probably break
if a new notebook page were inserted as well).
Solution 1
When the page is loaded you can get the tab list DOM object like this:
var tablist = $('ul[role="tablist"]')
And then you can click on the specifict tab, selecing by the text inside the anchor. So you don't depend on the tab index:
tablist.find('a:contains("Other Information")').click()
I think if you have two tabs with the same text does not make any sense, so this should be sufficient.
Solution 2
Even if you want to be more specific you can add a class to the notebook to make sure you are in the correct notebook
<notebook class="nt_to_change">
Now you can use one of this expressions in order to select the tab list
var tablist = $('div.nt_to_change ul.nav-tabs[role="tablist"]')
// or
var tablist = $('div.nt_to_change ul[role="tablist"]')
Solution 3
If the contains selector doesn't convince you because it should be equal you can do this as well to compare and filter
tablist.find('a').filter(function() {
return $.trim($(this).text()) === "Other Information";
}).click();
Where "Other Information" is the string of the notebook page
I didn't tried the solution I'm giving to you, but if it doesn't work at least may be it makes you come up with some idea.
There's a parameter for XML elements named autofocus (for buttons and fields is default_focus and takes 1 or 0 as value). If you add autofocus="autofocus" to a page in XML, this page will be the displayed one when you open the view.
So, you can try to add this through JavaScript, when the user clicks on the respective link -which honestly, I don't know how to achieve that by now-. But you can add a distinctive context parameter to each link in XML, for example context="{'page_to_display': 'page x'}". When you click on the link, I hope these context keys will arrive to your JS method.
If not, you can also modify the fields_view_get method (here I wrote how to do that: Odoo - Hide button for specific user) to check if you get the context you've added to your links and add the autofocus parameter to the respective page.
As you said:
This works since I've attached a data-tab-index="" to each header
link in my widget code, but it's brittle - if someone adds a tab
later, the current indices will be broken.
I assume that your app allow multi-user interaction in realtime, so you have to integrate somewhere in your code, an update part function.
This function will trig if something has changed and cleanout the data to rebuilt the index in order to avoid that the current indices will be broken.

Complex Xpage takes long for partial refreshs

I have a complex xpage with lots of nested custom controls. Everytime I execute a partial refresh it takes over 4 seconds to finish. If I remove the complexity it works just fine and is fast as wished.
I put a test on this complex Xpage and even with partial execution mode this simple test takes over 4 seconds to finish.
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="refreshPanel" disableValidators="true" execMode="partial" execId="inputText1">
</xp:eventHandler>
</xp:button>
<xp:div id="refreshPanel">
<xp:inputText id="inputText1"></xp:inputText>
</xp:div>
execution time of partialrefresh
Does anyone have a hint on this? Any server settings which can be adjusted?
Even though it is a partial refresh all XPage controls' values are submitted to server. "partial" means in this case that only the label1 part returns to client. But it is a full submit and this might take time...
You can submit partially though. Add
execMode="partial" execId="button1"
to your eventHandler properties. This time only the execId's value gets submitted. Put in execId the id that needs to be submitted (maybe a panel) for this partial refresh.
Have a look here for more information on partial execution mode .
There's a few little tweaks you can make to help speed things up (in general) however I'm not sure how they would help with a single complex page, but.....
Change the server page persistence setting to keep the current page in memory
Minimize the use of SSJS
Partial Execution mode (which Knut has already mentioned)
Support Concurrent User Load
Keep the Maxheap size at ¼ of the available RAM
For your XSP properties, enable “Use run-time optimized java script
and CSS resources”
Set your value bindings so they compute on the page load.
All of the above are tips/recommendations I've read from other developers over the years and I implement/try to implement myself where possible so might be worth trying some/all to see if it helps?

Replace Orbeon Form with a new one asynchronously via AJAX

I am using Orbeon forms with Hybris. We have several pages linked together where a user needs to go through them in a sequence (checkout process).
The content of the Orbeon form is dynamically being determined based on actions from previous steps. E.g.
If user adds Product A to the cart on the step 1, only two fields will be visible on the form located on step 2, if he adds another (Product B) on step 1, one more field should be visible on the form.
I am using certain preprocessor class which prefills some of the hidden fields on the form and then the logic for dynamic display is on the Form itself, based on those hidden fields. This works in a simple scenario when moving back and forth, through the steps.
However, the problem is that I need to have a HTML Mini-cart displayed on the page as well (not part of Orbeon Form), which can also trigger adding/removing of the products asynchronously.
So while I am on step 2 where the form is displayed, the user can also remove/re-add some of the products -> therefore, this needs to trigger asynchronous re-rendering of the form and change the display of the form (with new fields added or removed).
I'm using AJAX for this async stuff and the problem I am facing is that a lot of Orbeon-specific Javascript files and variables is being generated when the page loads for the first time, and some random FormID is used. This FormID is different when I retrieve the new form from the back-end and when trying to replace the HTML content I'm getting various errors in the console, because old Form id is used all over the place.
Does anyone have any suggestion if this could be achieved and how to approach this problem?
Update: Example of "hidden" field glass-coverage-selected
<xf:instance id=""fr-form-instance"" xxf:exclude-result-prefixes=""#all"">
<form>
<glass-coverage-selected/>
<section-1>
<massive-exterior-walls/>
</section-1>
...
Later, a bind is created:
<xf:bind id=""section-40-bind"" ref=""section-40"" name=""section-40"" relevant=""instance('fr-form-instance')/glass-coverage-selected = 'yes'"">
<xf:bind id=""previous-glass-insurance-bind"" ref=""previous-glass-insurance"" name=""previous-glass-insurance"">
<xf:required id=""validation-156-validation"" value=""true()""/>
</xf:bind>
And that bind is used to control the visibility of certain section:
<fr:section id=""section-40-control"" bind=""section-40-bind"">
<xf:label ref=""$form-resources/section-40/label""/>
<fr:grid>
<xh:tr>
<xh:td>
<xf:select1 id=""previous-glass-insurance-control"" appearance=""full"" bind=""previous-glass-insurance-bind"" class=""previous-insurance"">
<xf:label ref=""$form-resources/previous-glass-insurance/label""/>
<xf:hint ref=""$form-resources/previous-glass-insurance/hint""/>
<xf:help ref=""$form-resources/previous-glass-insurance/help""/>
<xf:alert ref=""$form-resources/previous-glass-insurance/alert[1]"" validation=""validation-156-validation""/>
<xf:alert ref=""$form-resources/previous-glass-insurance/alert[2]""/>
<xf:itemset ref=""$form-resources/previous-glass-insurance/item"">
<xf:label ref=""label""/>
<xf:value ref=""value""/>
<xf:hint ref=""hint""/>
</xf:itemset>
</xf:select1>
</xh:td>
</xh:tr>
</fr:grid>
</fr:section>
You can manipulate the values of form fields in JavaScript, in the browser. If you want to set the value of "hidden fields", you make sure that those fields as not hidden by putting false() under Visibility for the field in Form Builder. If you do this, for security reasons, the value of the field isn't even sent to the browser by Orbeon Forms, and it can't be set from JavaScript. Instead, to be able to set the value from JavaScript, you need to hide the control with CSS. The simplest way to do this is to add the class xforms-disabled for that field in the Control Settings dialog.
Then, assuming the name of the control in Form Builder is my-control, in JavaScript you can write var control = ORBEON.jQuery('*[id $= "my-control-control"]'); ORBEON.xforms.Document.setValue(control.attr('id'), '42');. Note the -control added at the end of the name of the control. And to test this first, I recommend you don't put the CSS class, so you can more easily see if setting the value works.
For the documentation on the above setValue() and other JavaScript APIs, see the page Client-side JavaScript API.

Using a dijit.Menu like a combo box to select a value

I get a list of column values from a view that I feed into a comboBox to select a value and store it in a field. From a look and feel I would like to feed this list to dijit.Menu I have not done much with dojo and have not been able to find an example that could point me in the right direction.
You can use the drop-down button from ExtLib which would provide a similar look and feel:
<xe:dropDownButton
id="dropDownButton1">
<xe:this.treeNodes>
<xe:basicContainerNode>
<xe:this.label><![CDATA[#{empty viewScope.myValue?"Select a value...":viewScope.myValue}]]></xe:this.label>
<xe:this.children>
<xe:basicLeafNode
label="Label 1"
submitValue="value1">
</xe:basicLeafNode>
<xe:basicLeafNode
label="Label 2"
submitValue="value2">
</xe:basicLeafNode>
</xe:this.children>
</xe:basicContainerNode>
</xe:this.treeNodes>
<xp:eventHandler
event="onItemClick"
submit="true"
refreshMode="partial"
refreshId="dropDownButton1">
<xe:this.action><![CDATA[#{javascript:var value=context.getSubmittedValue();
if(value!=null && value!="") viewScope.myValue=value;}]]></xe:this.action>
</xp:eventHandler>
</xe:dropDownButton>
Here, viewScope.myValue is the secret data binding. You have to handle the data validation separately (maybe via an inputHidden component). Also you might use repeatTreeNode instead of beanLeafNode, so you would have calculated list of options.

xPages - Pager onClick Event issue

I'm trying to put an onClick event on a pager which is in a dataView. I want the onClick event to update a sessionScope variable. I've added the code and tested it, I can't get it to work. I made a test button and set it up with the same onClick event and the variable is updated correctly.
Right now, if I click the pager it works correctly and pages through the dataView. But, it doesn't update the sessionScope variable. Any ideas what could be causing this?
<xp:pager partialRefresh="true" id="pager8" xp:key="pagerBottomLeft"
styleClass="mblFooterLeft">
<xp:this.rendered><![CDATA[#{javascript:isPagerDisplayed();
}]]></xp:this.rendered>
<xp:pagerControl id="pagerControl2" type="Previous">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="testLabel">
<xp:this.action><![CDATA[#{javascript:incPagerDisplay();
sessionScope.pagerDisplay++;}]]></xp:this.action>
</xp:eventHandler>
</xp:pagerControl>
</xp:pager>
Any help would be greatly appreciated.
While you're asking for an onClick event update to a sessionScope'd variable, it seems from your code that your end attempt is to save the page'd state the user is on, so they can navigate away/back and retain that from the sessionScope var. Assuming that's all you're trying to do with it, I recommend you check out and use the xe:pagerSaveState control.
Brad Balassaitis wrote a blog post on using it with a Data View, which may assist you. Oliver Busse tweaked that implementation a bit and demonstrates with his OSnippet; a massage to the SSJS call.
Here's Oliver's snippet:
<xe:pagerSaveState id="pagerSaveState1" refreshId="theView" partialRefresh="true">
</xe:pagerSaveState>
<xp:this.beforePageLoad><![CDATA[#{javascript:viewStateBean.restoreState = true;}]]></xp:this.beforePageLoad>

Resources