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.
Related
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.
I've got a wicket dropdown choice, and when I select something, I want to update some components within the form. This is working fine using wicket (1.4) ajax. However, it's updating the whole form including the dropdownchoice itself. There are quite a lot of items in the dropdown list (maybe 2000) so it's not great from a performance point of view.
Here's the page hierarchy:
form (Form)
|----packageDDC (DropDownChoice)
|----pptview (RefreshingView)
|----buy (Button)
packageDDC.add(new AjaxFormComponentUpdatingBehavior("onchange") {
protected void onUpdate(AjaxRequestTarget target) {
//--snip-- update pricepoints which back up the pptview
target.addComponent(form); //ie the form
}
}
In the ajax debug window I can see all the dropdown choice options being re-sent every time
What I want to do is only update the pptview and the Button via ajax, not the contents of the dropdownchoice.
I tried adding pptview to the target, but it complains that RefreshgViews can't be updated via ajax.
I tried wrapping the pptview with an EnclosureContainer, but wicket didn't like that either (something about setRenderBodyOnly)
So I tried using an WebMarkupContainer (called 'pptcontainer') and setting pptview to be a child of that - but now the pptview is not updated. Instead it says (in Ajax debug):
"ERROR: Wicket.Ajax.Call.processComponent: Component with id [[purchaseButton2f]] was not found while trying to perform markup update. Make sure you called component.setOutputMarkupId(true) on the component whose markup you are trying to update."
"ERROR: Wicket.Ajax.Call.processComponent: Component with id [[pptcontainer2e]] was not found while trying to perform markup update. Make sure you called component.setOutputMarkupId(true) on the component whose markup you are trying to update."
well these objects definitely do have this set to true:
buy.setOutputMarkupId(true);
pptcontainer.setOutputMarkupId(true);
pptcontainer.setOutputMarkupPlaceholderTag(true);
So the page is not updated correctly.
What am I doing wrong?
The new hierarchy is:
form (Form)
|----packageDDC (DropDownChoice)
|----pptcontainer (WebMarkupContainer)
| |----pptview (RefreshingView)
|----buy (Button)
I faced same problem with popup window which content is provided by ajax call. In my case the problem was solved by changing html placeholder "wicket:container" to "div" element as proposed in this article: http://sha.nnoncarey.com/blog/archives/36
After this change generated html has correct id and wicket do not have problem to find it and replace content with AJAX response.
I have a single visual source page where i including a 2 pages through composition, body changes dynamically based on the action of the buttons. Button which is available in wizardButton (like next & previous). So now there are 2 forms available in page.
The form available in wizardBody
Another form available in wizardButton
Currently i'm able to navigate the pages back and forth, but the problem is not able to take the form values of the body when click the buttons in wizardButtons.
Even i tried with parameters in the controllers to get the value.
Could somebody help me how to solve this issue. Kindly let me know in case of any further details
<apex:page label="CreationWizard" showHeader="false" sidebar="false" title="Creation Wizard" controller="WizardController">
<!- Body differs based on the action button -->
<apex:composition template="{!wizardBody}" />
<apex:composition template="{!wizardButton}"/>
</apex:page>
is it possible to delay loading of some controls on an xpage?
This is the problem: let's say you have a control that does a fultextsearch and displays the result in a repeat control. this ft search might take a long time and will hold the webpage loading in a waiting state until the search result is ready.
I want my page to load most of the data initally, and some "time consuming" controls should be loaded in to the page as a sperate request after the inital load.
this way the user will immediatly see the webpage, but some of the data on the page will load a little bit later without holding the webpage in a waiting state from the server.
possible?
The downside to using rendered is that all the value bindings will still evaluate, even if the corresponding markup isn't sent to the page. So the trick here is making sure the components don't even exist until you want them to.
Every component has a getChildren() method. This returns a mutable List of components, which has a add() method. This allows you to add components to the page on the fly, either while the page is loading, or later during an event. For the purposes of what you're trying to do, you would want to defer adding the "expensive" components until a subsequent event.
Create an event handler attached directly to the view root (), give it a unique ID (e.g. "loadExpensiveComponentsEvent", set its refresh mode to partial, set a refresh ID to whatever div or panel will contain the search results, and set its event name to an arbitrary event (e.g. "loadExpensiveComponents"). This prevents your event from being triggered by actual user behavior. Set the event's code to SSJS that will inject your components.
Then add a script block () to trigger the event after the page has loaded:
XSP.addOnLoad(function(){
XSP.firePartial(null, "#{id:loadExpensiveComponentsEvent}");
});
Your page will load without the search result components. Once the page has fully loaded, it will trigger the component injection event automatically.
For guidance on how to code the injection event, open the Java file that has been generated from your existing page to see what components need to be injected and what to set their values to.
You can pack them into a panel and set their rendered status to rendered=#{viewScope.pageFullyLoaded}. Then in the onLoad event have a XSP. partialRefresh request where you set viewScope.pageFullyLoaded=true
A little ugly but doable. Now you can wrap that code into your own custom control, so you could have a "lazyGrid", "lazyPanel" etc.
Not sure why I did not think of this before. the dynamic content control in extlib actually solves this problem. the dcc can be triggered onClientLoad both using javascript and ssjs afer the page has loaded.
one problem I am facing now is that I am already using the dcc on my site so I need to put another dcc within my dcc. and this seem to be a bit buggy. I have reported it to the extlib team on openNTF.
Recently im working on a project and im trying to generate form elements with the help of ajax technology (implementing a form with codes). the situation is that the user should be able to select from a list of options and then due to his select another list of options should be appeared, then due to his/her select from the second sets of options he/she should see the third series of options. now the problem is that when the user tries to change the first option in the first set, the second option will be regenerate but the third one still sticks on the page. I was trying to use the form_sate['rebuild'] = TRUE
but it did not work and all form elements disappeared. can any one help me to see which code should be implemented and where it should be used?
Without any code it's almost impossible to help, except to say check out the examples modules, specifically the ajax_example module.
The basic principle is that you need a <div> container surrounding your 2nd and 3rd select elements, which will be replaced by the #ajax set on the first element. Then you need another container inside that one surrounding only the 3rd select element, which will be replaced by the #ajax set on the 2nd select element.
Hope that helps.
well.. the form page may contains previous values because of $_POST fields variables..
for example if I want to display clear "add" form on POST submit,
I do this tric to clear drupal previous form values via ajax:
<?php
// AJAX POST handler...
....
$my_form = drupal_render(drupal_get_form("the_form", ...));
$errors = form_get_errors();
if (!$errors) {
// re-render clean form, unset your POST fields....
unset($_POST['link_path']);
unset($_POST['link_title']);
unset($_POST['parent']);
unset($_POST['weight']);
$my_form = drupal_render(drupal_get_form("the_form", ...));
}
?>