I am new to Orbeon and trying to implement a dynamic dropdown where the values are populated from a lookup xml on file system.
So here's the requirement:
The first dropdown (Animal) will have 2 values: Cat and Dog. Based on the selection, the second dropdown (Breeds) will display the list of breeds.
The 2 xml files containing the breeds are registered as resources and look like this:
CAT: \forms\quote\lookup\catbreeds.xml
<?xml version="1.0" encoding="UTF-8"?>
<breeds>
<item>
<value>C1</value>
<label>Domestic Long Hair</label>
</item>
<item>
<value>C2</value>
<label>Domestic Medium Hair</label>
</item>
<item>
<value>C3</value>
<label>Domestic Short Hair</label>
</item>
<item>
<value>C4</value>
<label>Mixed Breed</label>
</item>
<item>
<value>C5</value>
<label>Tabby</label>
</item>
<item>
<value>C6</value>
<label>Abyssinian</label>
</item>
<item>
<value>C7</value>
<label>American Bobtail</label>
</item>
</breeds>
DOG: \forms\quote\lookup\dogbreeds.xml
<?xml version="1.0" encoding="UTF-8"?>
<breeds>
<item>
<value>D1</value>
<label>Cavapoo</label>
</item>
<item>
<value>D2</value>
<label>Cockapoo</label>
</item>
<item>
<value>D3</value>
<label>Crossbreed</label>
</item>
<item>
<value>D4</value>
<label>Goldendoodle</label>
</item>
<item>
<value>D5</value>
<label>Labradoodle</label>
</item>
<item>
<value>D6</value>
<label>Maltipoo</label>
</item>
<item>
<value>D7</value>
<label>Mi Ki</label>
</item>
</breeds>
The form.xml looks like this:
Model:
<xf:instance id="fr-form-instance" xxf:exclude-result-prefixes="#all">
<form>
<section-1>
<animal/>
<breed/>
</section-1>
</form>
</xf:instance>
<xf:instance id="pets"
xmlns="">
<root>
<item name="Cat" value="cat"/>
<item name="Dog" value="dog"/>
</root>
</xf:instance>
Body:
<xh:body>
<fr:view>
<fr:body
xmlns:oxf="http://www.orbeon.com/oxf/processors"
xmlns:p="http://www.orbeon.com/oxf/pipeline"
xmlns:xbl="http://www.w3.org/ns/xbl">
<fr:section id="section-1-control" class="no-header-section" bind="section-1-bind">
<fr:grid>
<xh:tr>
<xh:td>
<fr:dropdown-select1 id="animal-control" ref="animal" bind="breed-bind">
<xf:label ref="$form-resources/animal/label"/>
<xf:itemset ref="instance('pets')/item">
<xf:label ref="#name"/>
<xf:value ref="#value"/>
</xf:itemset>
</fr:dropdown-select1>
</xh:td>
</xh:tr>
<xh:tr>
<xh:td>
<fr:dropdown-select1 id="breed-control" bind="breed-bind">
<xf:label ref="$form-resources/breed/label"/>
<xf:itemset
ref="doc('oxf:/forms/quote/lookup/catbreeds.xml')/breeds/item">
<xf:label ref="label"/>
<xf:value ref="value"/>
</xf:itemset>
</fr:dropdown-select1>
</xh:td>
</xh:tr>
</fr:grid>
</fr:section>
</fr:body>
</fr:view>
</xh:body>
Now, the "ref" attribute in ref="doc('oxf:/forms/quote/lookup/catbreeds.xml')/breeds/item"> is where the problem is. I am able to load the individual lists (in this case it loads Cat breeds) but I am not able to find a way to make it conditional.
I found that I cannot use XPath for ref (like this:oxf:/forms/quote/lookup/{animal}breeds.xml) to make the value dynamic.
I have been trying a lot of things but nothing is working out. It will be a great help if someone can give me a direction.
I'm on Orbeon 4.8
Since the value of ref is XPath, you can build the URL you pass to doc() dynamically, by doing something along these lines:
doc(concat(
'oxf:/forms/quote/lookup/',
instance('fr-form-instance')/section-1/animal,
'breeds.xml'
))/breeds/item
Related
I have and issue with empty form fields when I'm using tabs.
If I removing tabs usage, fields contains correct values.
My form uicomponent is large, so I will paste only important parts of configuration:
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">vendor_modulename_form.vendor_modulename_form_data_source</item>
</item>
<item name="label" xsi:type="string" translate="true">Item</item>
...
</argument>
<settings>
...
<namespace>vendor_modulename_form</namespace>
<dataScope>data</dataScope>
<deps>
<dep>vendor_modulename_form.vendor_modulename_form_data_source</dep>
</deps>
<layout>
<navContainerName>left</navContainerName>
<type>tabs</type>
</layout>
</settings>
<fieldset name="modules">
<settings>
<collapsible>true</collapsible>
<opened>true</opened>
<label translate="true">Details</label>
</settings>
<field name="name" formElement="input">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">module</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Name</label>
<dataType>text</dataType>
<visible>true</visible>
<dataScope>name</dataScope>
</settings>
</field>
...
</fieldset>
Console log and magento logs are empty. I'm working in developer mode.
Any ideas?
In the dataprovider you have to wrap all the content of your fieldset with the key after loading the data.
Eg:
$id = $this->request->getParam('id');
/** #var Collection $items */
$items = $this->collectionFactory->create()->addFieldToFilter('id', $id)->getItems();
foreach ($items as $item) {
$formData['modules'] = $item->getData();
$this->loadedData[$item->getId()] = $formData;
}
I tried to show the shipping country in the Admin Sales Order Grid but nothing works.
I did check the customer address configuration twice and there I can see that the country variable exists and should be shown. But in the grid you can not see the shipping country.
Any workarounds?
I found a solution. Creating a Module and Rewrite Class.
app/code/Vendor/ExtendedAdminGrid/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="sales_order_grid_data_source" xsi:type="string">Vendor\ExtendedAdminGrid\Model\ResourceModel\Order\Grid\Collection</item>
</argument>
</arguments>
</type>
</config>
app/code/Vendor/ExtendedAdminGrid/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_ExtendedAdminGrid" setup_version="2.0.0">
<sequence>
<module name="Magento_Sales"/>
<module name="Magento_Backend"/>
</sequence>
</module>
</config>
app/code/Vendor/ExtendedAdminGrid/Model/ResourceModel/Order/Grid/Collection.php
<?php
namespace Vendor\ExtendedAdminGrid\Model\ResourceModel\Order\Grid;
class Collection extends \Magento\Sales\Model\ResourceModel\Order\Grid\Collection
{
protected function _renderFiltersBefore()
{
$this->getSelect()->joinLeft(
["soa" => "sales_order_address"],
"main_table.entity_id = soa.parent_id and soa.address_type = 'shipping'",
array('country_id')
)
->distinct();
parent::_renderFiltersBefore();
}
protected function _initSelect()
{
$this->addFilterToMap('created_at', 'main_table.created_at');
$this->addFilterToMap('base_grand_total', 'main_table.base_grand_total');
$this->addFilterToMap('grand_total', 'main_table.grand_total');
$this->addFilterToMap('store_id', 'main_table.store_id');
$this->addFilterToMap('store_name', 'main_table.store_name');
$this->addFilterToMap('order_id', 'main_table.order_id');
$this->addFilterToMap('order_increment_id', 'main_table.order_increment_id');
$this->addFilterToMap('billing_name', 'main_table.billing_name');
$this->addFilterToMap('billing_name', 'main_table.shipping_name');
$this->addFilterToMap('status', 'main_table.status');
parent::_initSelect();
}
}
app/code/Vendor/ExtendedAdminGrid/view/adminhtml/ui_component/sales_order_grid.xml
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<columns name="sales_order_columns">
<column name="country_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Shipping Country ID</item>
</item>
</argument>
</column>
</columns>
</listing>
app/code/Vendor/ExtendedAdminGrid/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Vendor_ExtendedAdminGrid',
__DIR__
);
Thanks to Sergey for this great blogpost Modifying the default magento2 sales order grid — adding a coupon code column.
How to show first element of dropdown menu instead of "Please select"?
I am loading elements in 2 dropdown menus like this:
<xf:instance id="makes">
<root>
<item name="Tesla" value="Tesla"/>
<item name="Toyota" value="Toyota"/>
<item name="Suzuki" value="Suzuki"/>
<item name="Jeep" value="Jeep"/>
<item name="Alfa" value="Alfa"/>
</root>
</xf:instance>
<xf:instance id="models">
<root>
<item makeValue="Tesla" modelValue="Tesla S" modelName="Tesla S"/>
<item makeValue="Tesla" modelValue="Tesla X" modelName="Tesla X"/>
<item makeValue="Tesla" modelValue="Tesla Roadster" modelName="Tesla Roadster"/>
<item makeValue="Alfa" modelValue="Brera" modelName="Brera" />
<item makeValue="Alfa" modelValue="Giulietta" modelName="Giulietta"/>
<item makeValue="Alfa" modelValue="Spider" modelName="Spider"/>
<item makeValue="Alfa" modelValue="MiTo" modelName="MiTo"/>
<item makeValue="Alfa" modelValue="GT" modelName="GT"/>
<item makeValue="Suzuki" modelValue="Swift" modelName="Swift"/>
<item makeValue="Suzuki" modelValue="Samurai" modelName="Samurai"/>
<item makeValue="Suzuki" modelValue="Vitara" modelName="Vitara" />
<item makeValue="Toyota" modelValue="Corolla Verso" modelName="Corolla Verso"/>
<item makeValue="Toyota" modelValue="Aygo" modelName="Aygo" />
<item makeValue="Toyota" modelValue="Yaris" modelName="Yaris" />
<item makeValue="Toyota" modelValue="Avensis" modelName="Avensis"/>
<item makeValue="Toyota" modelValue="Rav4" modelName="Rav4"/>
<item makeValue="Jeep" modelValue="Grand Cherokee" modelName="Grand Cherokee"/>
<item makeValue="Jeep" modelValue="Commander" modelName="Commander"/>
<item makeValue="Jeep" modelValue="Compass" modelName="Compass"/>
<item makeValue="Jeep" modelValue="Liberty" modelName="Liberty"/>
<item makeValue="Jeep" modelValue="Patriot" modelName="Patriot"/>
<item makeValue="Jeep" modelValue="Renegade" modelName="Renegade"/>
</root>
</xf:instance>
And than bind them:
<xh:tr>
<xh:td>
<xf:select1 id="vehicle-make-control" bind="vehicle-make-bind" appearance="dropdown">
<xf:label ref="$form-resources/vehicle-make/label"/>
<xf:hint ref="$form-resources/vehicle-make/hint"/>
<xf:alert ref="$fr-resources/detail/labels/alert"/>
<xf:itemset ref="instance('makes')/item">
<xf:label ref="#name"/>
<xf:value ref="#value"/>
</xf:itemset>
</xf:select1>
</xh:td>
</xh:tr>
Same thing is for models, just control name is vehicle-model.
When i select vehicle make i got appropriate models for specified veh. make, but first element is please select of blank. I need to show Tesla S as first when Tesla is selected etc.
To have it like this:
OPEN PICTURE.
I set in initial value XPath expression $(.)[1] but it is not working.
I imagine this is for a form you created with Form Builder, and to which you added the <xf:instance id="makes"> and <xf:instance id="models"> by editing the source of the form.
If so, if you want the dropdowns to have the first item as their initial value, instead of "Please select", then you'll want to set the initial value of the field to the value of the first item. You can do so in the Control Settings dialog, in the Formulas tab. You can do so with an expression like:
instance('makes')/item[1]/#value
I'am try to convert XML on group of child nodes. Main information is in bill node, it must be group by bill numbers.
original XML, this is simplified version of original XML
<items>
<item>
<bill>10</bill>
<name>first (10)</name>
<price>111</price>
</item>
<item>
<bill>10</bill>
<name>second (10)</name>
<price>222</price>
</item>
<item>
<bill>10</bill>
<name>third (10)</name>
<price>333</price>
</item>
<item>
<bill>11</bill>
<name>first (11)</name>
<price>1</price>
</item>
<item>
<bill>11</bill>
<name>second (11)</name>
<price>2</price>
</item>
</items>
final file
<bills>
<bill>
<number>10</number>
<items>
<item>
<nameitem>first (10)</nameitem>
<priceitem>111</priceitem>
</item>
<item>
<nameitem>second (10)</nameitem>
<priceitem>222</priceitem>
</item>
<item>
<nameitem>third (10)</nameitem>
<priceitem>333</priceitem>
</item>
</items>
</bill>
<bill>
<number>11</number>
<items>
<item>
<nameitem>first (11)</nameitem>
<priceitem>1</priceitem>
</item>
<item>
<nameitem>second (11)</nameitem>
<priceitem>2</priceitem>
</item>
</items>
</bill>
</bills>
there is working XSLT, for basic grouping, but I have not idea, how to build another structure inside bill node, based on final XML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="group-by-bill" match="item" use="bill"/>
<xsl:template match="items">
<bills>
<xsl:for-each select="item[generate-id()=generate-id (key('group-by-bill', bill)[1])]">
<bill number="{bill}">
<xsl:copy-of select="key('group-by-bill', bill)"/>
</bill>
</xsl:for-each>
</bills>
</xsl:template>
Use this:
<xsl:template match="items">
<bills>
<xsl:for-each select="item[generate-id()=generate-id (key('group-by-bill', bill)[1])]">
<bill>
<number><xsl:value-of select="bill"/></number>
<items>
<xsl:for-each select="//item[bill = current()/bill]">
<item>
<xsl:copy-of select="name|price"/>
</item>
</xsl:for-each>
</items>
</bill>
</xsl:for-each>
</bills>
</xsl:template>
See Transform at https://xsltfiddle.liberty-development.net/jyH9rM5
I get this error when I run my vxml app;
"ECMAScript runtime error: Compilation produced 1 syntax
errors.\n\nWhile evaluating:\n\"var acc_no = [37492414, 94190610,
23228367, 39574988, 64742440];\n\t\t\t\t\tfunction checkAccount(arr,
val) {\n\t\t\t\t\treturn arr.some(arrVal => val ===
arrVal);\n\t\t\t\t\t\t\t\t\t\t\t} \"";
Here's my vxml script where the error arises:
<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns:voxeo="http://community.voxeo.com/xmlns/vxml"
version="2.1" xml:lang="en-US"
xml:base="http://webhosting.voxeo.net/201985/www/">
<property name="inputmodes" value="dtmf"/>
<property name="termchar" value="#"/>
<property name="interdigittimeout" value="2s"/>
<property name="bargein" value="false"/>
<var name = "accountNumber"/>
<var name = "pinNumber"/>
<form id="userAuth" scope="document">
<var name="iMaxTries" expr="3"/>
<var name="iTriesAcc" expr="0"/>
<var name="iTriesPin" expr="0"/>
<var name="fatal" expr="'We are having technical difficulties validating your credentials. Try back later.'"/>
<catch event="event.pinNumber.invalid">
<!-- increment the attempt counter; if the count is exceeded, disconnect -->
<assign name="iTriesPin" expr="iTriesPin+1"/>
<if cond="iMaxTries == iTries">
<value expr="fatal"/>
<disconnect/>
<else/>
<!-- clear is unnecessary on a nomatch, but we use the same code to handle a bad filled -->
<clear namelist="pinNumber"/>
<reprompt/>
</if>
</catch>
<catch event="event.accountNumber.invalid">
<!-- increment the attempt counter; if the count is exceeded, disconnect -->
<assign name="iTriesAcc" expr="iTriesAcc+1"/>
<if cond="iMaxTries == iTries">
<value expr="fatal"/>
<disconnect/>
<else/>
<!-- clear is unnecessary on a nomatch, but we use the same code to handle a bad filled -->
<clear namelist="accountNumber"/>
<reprompt/>
</if>
</catch>
<!-- exec this on the first and second noinput/nomatch -->
<!-- each event has its own counter -->
<catch event="noinput">
I'm sorry. I didn't get you.
<reprompt />
</catch>
<!-- exec this on the third nomatch -->
<catch event="nomatch">
<throw event="event.password.invalid"/>
</catch>
<!-- silently disconnect on the third noinput -->
<catch event="noinput" count="3">
<disconnect/>
</catch>
<field name="accountNumber" type="digits?length=8">
<prompt> Please enter your account number followed by the pound key. </prompt>
<grammar mode="dtmf" version="1.0" root="pin"
tag-format="semantics/1.0">
<rule id="digit" scope="public" >
<one-of>
<item> 0 </item>
<item> 1 </item>
<item> 2 </item>
<item> 3 </item>
<item> 4 </item>
<item> 5 </item>
<item> 6 </item>
<item> 7 </item>
<item> 8 </item>
<item> 9 </item>
</one-of>
</rule>
<rule id="pin" scope="public">
<tag>out=""</tag>
<item repeat="8">
<ruleref uri="#digit"/>
<tag>out += rules.latest( );</tag>
</item>
</rule>
</grammar>
<filled>
<script> <![CDATA[
var acc_no = [37492414, 94190610, 23228367, 39574988, 64742440];
function checkAccount(arr, val) {
return arr.some(arrVal => val === arrVal);
}
]]> </script>
<block>
<var name="chk_acc" expr="checkAccount(acc_no, accountNumber);"/>
<if cond="chk_acc =='true'">
<goto next="#pinNumber"/>
<else/>
<prompt>Sorry, Account number not recognised. </prompt>
<throw event="event.accountNumber.invalid"/>
</if>
</block>
</filled>
</field>
<field name="pinNumber" type="digits?length=4">
<prompt> please enter your four digit pin followed by the pound key. </prompt>
<grammar mode="dtmf" version="1.0" root="pin"
tag-format="semantics/1.0">
<rule id="digit" scope="public" >
<one-of>
<item> 0 </item>
<item> 1 </item>
<item> 2 </item>
<item> 3 </item>
<item> 4 </item>
<item> 5 </item>
<item> 6 </item>
<item> 7 </item>
<item> 8 </item>
<item> 9 </item>
</one-of>
</rule>
<rule id="pin" scope="public">
<tag>out=""</tag>
<item repeat="4">
<ruleref uri="#digit"/>
<tag>out += rules.latest( );</tag>
</item>
</rule>
</grammar>
<filled>
<script> <![CDATA[
var pin_no = [2414, 0610,8367, 4988, 2440];
function checkPin(arr, val) {
return arr.some(arrVal => val === arrVal);
}
]]> </script>
<block>
<var name="chk_pin" expr="checkPin(pin_no, pinNumber);"/>
<if cond="chk_pin =='false'">
<prompt>Sorry, you have entered is an invalid pin. Please try again </prompt>
<throw event="event.pinNumber.invalid"/>
</if>
</block>
</filled>
</field>
<filled mode="all" namelist="accountNumber pinNumber">
<script> <![CDATA[
var accDetails = [
{acc_name: 'Lawrence Burkins', account: 37492414, pin: 2414, acc_type: 'checking', acc_bal: 2372351.74 , acc_br:'Montreal'},
{acc_name: 'Ola Macaulay', account: 94190610, pin: 0610, acc_type: 'checking', acc_bal: 908242.49 , acc_br:'Tisdale'},
{acc_name: 'Judy Cefalu', account: 23228367, pin: 8367, acc_type: 'checking', acc_bal: 15700526.57 , acc_br:'Toronto'},
{acc_name: 'Mellisa Garcia', account: 39574988, pin: 4988, acc_type: 'checking', acc_bal: 568201.26 , acc_br:'Prince Albert'},
{acc_name: '', account: 64742440, pin: 2440, acc_type: 'checking', acc_bal: 1952000.00 , acc_br:'Fort McMurray'},
];
function accDetails(details) {
return details.account === accountNumber ;
}
var newDetail = accDetails.find(accDetails);
var myDetail = [newDetail];
var myBal = myDetail.slice(0, 4);
]]> </script>
<var name = "accountBalance" expr = "myBal.toString()"/>
<prompt>
Your Checking account balance is <prosody rate="slow"><say-as type="currency"> $ + <value class="currency" expr="accountBalance"/> </say-as></prosody>
</prompt>
<goto next="after_bal.vxml"/>
</filled>
</form>
</vxml>
I am still new to vxml and ecmascript.
The ECMAScript in question seems to be syntactically correct:
var acc_no = [37492414, 94190610, 23228367, 39574988, 64742440];
function checkAccount(arr, val) {
return arr.some(arrVal => val === arrVal);
}
I would suggest breaking this piecemeal and reduce the complexity of the VXML code to debug it. Use a very basic VXML file with one simple field, put the above ECMAScript in the filled section and then see if you get the same error. If you do, move the script further up in the code (i.e not in the filled section) and again check if you get the same error.
The error statement is a bit confusing because of the linefeeds etc. Does the error message look like that when you see it the first time?