Activate xforms button when instance is valid, inactivate otherwise - orbeon

I have an xforms instance that I have a number of binds set up for so I can warn a user about input errors.
When he is done he needs to be able to submit the data from the instance. I would like to toggle 'active' on the button depending on whether or not the instance is valid.
What's the best way to 'attack' this problem? I'm currently using a group around the button that basically repeats what the model bindings already said which feels redundant and is error prone because of out of sync logic.
Also: this instance has 3 bindings, but I have others with 30-40 bindings.
Current code:
<xforms:group ref=".[instance('new-terminology-association')[matches(#code,'^\S+$')][matches(#codeSystem,'^[0-2](\.(0|[1-9][0-9]*))*$')][string-length(#displayName)>0]]">
<fr:button>
<xforms:label ref="$resources/create-association"/>
<xforms:action ev:event="DOMActivate">
...
</xforms:action>
</fr:button>
</xforms:group>

You could use the xxf:valid() function, pointing to the nodes you want to be valid. You can also point that function to a "parent" node, and ask it to check everything "under" that node is valid.
I think that function does what you're looking for, but because field values are only sent when users stab out of the field, this can create a somewhat unexpected user experience. For instance, imagine that the last field of your form, showing just before your button, is required. The user focuses on that field, and types a value. At this point the button is still disabled, since the value hasn't been sent to the server yet. Now the user hits tab, the value is sent to the server, the button would become enabled when the Ajax responses received, but since the button wasn't enabled at the time tab was pressed, the focus goes on something other than the button, which is somewhat unexpected. So, this is something to keep in mind.

There is absolutely no way in Orbeon 4.7 to make the button/trigger respond directly to xxf:valid. This looks like a bug to me. Workaround code: add observer for xxforms-valid and xxforms-invalid that set true|false in a new instance. Add readonly binding based on ".='false'" for that instance and use ref="instance" on the trigger. On a busy form with lots of buttons that is a bit of a waste, but it'll have to make due. Thanks for you help, appreciated!
<xf:instance id="button-control">
<button btn-term-add="false"/>
</xf:instance>
<xf:action ev:event="xxforms-invalid" ev:observer="new-terminology-association">
<xf:setvalue ref="instance('button-control')/#btn-term-add" >false</xf:setvalue>
</xf:action>
<xf:action ev:event="xxforms-valid" ev:observer="new-terminology-association">
<xf:setvalue ref="instance('button-control')/#btn-term-add">true</xf:setvalue>
</xf:action>
<xf:bind nodeset="instance('button-control')">
<xf:bind ref="#btn-term-add" readonly=".='false'"/>
</xf:bind>
....
<xf:trigger ref="instance('button-control')/#btn-term-add">
....
</xf:trigger>

Related

How to update an instance value with another document

We are using XSLTForms and XSLT to display a page.
I have an instance on a page whose value is set as a document.
<xf:instance id="myDetails">
<xsl:copy-of select="$detailDocument" />
</xf:instance>
It works fine and the instance value is set correctly. However, later I need to update the value of this instance with another document. I tried something like follows but didn't work:
<xf:setvalue ref="instance('myDetails')"><xsl:copy-of select="$updatedDetailDocument" /></xf:setvalue>
This just makes the instance empty even though I know updatedDetailDocument is not empty. Does xf:setvalue even support setting instances ? Or is there any other way of doing the same ?
<xf:setvalue> is used to set text within an XML attribute or XML element. In order to set a tree or subtree of XML, you would need the <xf:insert> action instead.
You don't say how you are getting $updatedDetailDocument, but since this is dynamic you probably need to retrieve that updated document using <xf:submission>, in which case you won't need <xf:insert> because <xf:submission> can directly update your instance with replace="instance".

Considering an element only if its a child of another element

For example :list-view and list-item
I use them as:
<list-view>
<list-item><list-item>
<list-item><list-item>
<list-item><list-item>
</list-view>
But how do i make <list-item> element valid if and only if its a child of <list-view>?
is there a formal way to do this in polymer?
to make it more clear sometimes i create polymer elements that are intended to be used inside and only inside another polymer element
So, the short answer here is no. I think as a general rule, the element shouldn't have to know anything about its parent elements. You can have a parent element that only displays <list-item> children, but does it add anything to have a <list-item> that won't appear outside of a <list-view>?
To take an example from vanilla HTML, an <option> only really makes sense inside a <select>, <optgroup> or <datalist> element, but you can put one in elsewhere and the browser doesn't complain. Likewise, if I include a <source> tag outside of a <video>, I don't see a console message (at least on Chrome and Firefox).
If you want to do something special in this case, you can check the parent node in the attached callback. Something like:
attached: function() {
if (this.parentNode.localName !== 'list-view') {
...
} else {
...
}
Of course, if you do this, and next week you come up with an awesome <grid-list-view> that could re-use your original <list-item>, you need to go back and change your list item. So I would use this pattern with caution and only if you see a tangible benefit to restricting where your component can be used.

Trouble saving selected values in selectManyListbox

We're having some problems with saving the selected values in a h:selectManyListbox.
What happens is that only the last value selected gets saved.I've placed a breakpoint in the components setValue(List) method.
When just selecting/deselecting everything seems fine, setValue is called, the list of strings it receives as argument is filled with one or more strings.
But, when pressing our "Save" button the setValue method gets called an additional time, this time with the list argument consisting of only ONE element, effectivly overwriting any previous values!
This additional call to setValue() occurs before even reaching our save button code.
Our system setup is Liferay 6.1.1 bundle with Tomcat7 with jsf 2.1.
Anyone has a clue what the problem might be ?
have you tried to declare in your backbean a list like String[] selectedValues?
for example:
<h:selectManyListbox value="#{backBean.selectedValues}">
<f:selectItem itemValue="" itemLabel="" />
.....
</h:selectManyListbox>
ZTB.

insert into instance in a repeat

my situation is the following:
I have a nodeset, through which I iterate and populate a table with some of the data
One of the fields, I do want to sum up
The problem:
Unfortunately I cannot use the sum method for the calculation as the nodeset is custom function that accesses data from other forms. And that seems to mess up things.
My idea of a solution:
I thought, I could create an instance and in each iteration add the value to it. Then I simply could access that data and do whatever calculation required. But I cannot get the xforms:insert to work.
A simplified version looks like this:
<xforms:repeat nodeset="(xxforms:si-source-forms('other_form'))">
<!-- table here -->
<xforms:insert
nodeset="instance('fr-form-instance')//positionen/position"
origin="instance('neue-position')"/>
</xforms:repeat>
The 'neue-position' instance contains bindings to the values in the source form:
<xforms:bind id="neue-position-binds" nodeset="instance('neue-position')">
<xforms:bind id="neue-position-bind" nodeset="position">
<xforms:bind id="neue-position-summe-bind" nodeset="summe" name="summe" type="xforms:string" required="true" xxforms:default="xxforms:si-source-forms('other_form')//gesamtbetrag_ausgabe" />
</xforms:bind>
</xforms:bind>
It does not work as expected though, so obviously something is wrong. I'd appreciate any hints.
About your first code snippet:
Your <xforms:insert> won't have any effet. You're in the view, and an action only runs if it is attached to an event listener. Without an ev:listener on the <xforms:insert> (or on an action around that insert), it just won't run.
About doing a sum over nodes not in an instance:
Assuming there is just one "sum" over the data returns by your custom function, you could write code along those lines:
Store the sequence of nodes returned by the function in an variable <xf:var name="others" ref="xxforms:si-source-forms('other_form')"/>
Use that variable in the repeat: <xf:repeat ref="$others"> (BTW, now XForms is standardizing the use of ref everywhere, in place of nodeset).
Do your calculation: <xf:var name="my-sum" ref="sum($others/path/to/values)"/>.
Finally, I imagine that you want to do something with $my-sum, maybe show it with an <xf:output>.

What is the correct code pattern to fetch data base entries?

In two other questions (here and here) BalusC makes a straight up declaration:
Getters are solely there to access bean properties, not to do some business logic. There you have the bean constructor, initialization blocks or event methods for. All are executed only once during bean's life and that's exactly what you want.
Well gee -- this just invalidated a gazillion lines of code I have already written. Ok, then, what is the correct way of implementing a backing bean that fills a data table? I understand his point and the concept, but not the practice. My question is twofold:
Why is the way I am doing it wrong?
How do I fix it?
I use PrimeFaces p:dataTable a lot, and it's value attribute resolves to a collection. For reasons I don't go into here, I do not use PrimeFaces' lazy table loading feature. Instead I implement my own filter/sort controls, but they trigger AJAX events, which then results in the table being filled with records fetched from the data base.
The table is marked up like this:
<p:panel id="mqTable">
<h:outputText value="Sort/Filter: #{maintCategory.tableQueryParameters}" />
<p:dataTable
id="mqDataTable"
rows="#{maintCategory.pageSize}"
value="#{maintCategory.dataModel}"
selection="#{maintCategory.selected}"
var="cat"
selectionMode="single"
emptyMessage="No Categories Found">
Now the INCREDIBLY BAD UN-JSFish (or so I just found out) getter for dataModel goes like this:
public ATMDataModel getDataModel() {
TableQueryParameters p = getTableQueryParameters();
if (p.isChangePending()) clearDataModel();
p.setChangePending(false);
if (dataModel != null) return dataModel;
List<ET> list = getDAO().runQuery(p);
if (p.isNeedResultSize()) p.setResultSize(getDAO().runQueryCount(p));
dataModel = new ATMDataModel(list);
return dataModel;
}
A few explanations.
This is from an abstract super-class where ET is the "Entity Type." All my CRUDs use this same routine.
The class ATMDataModel is a wrapper for the list which implements SelectableListModel. The row selection logic in PrimeFaces requires this. (It is a pain that appeared in PF 3 but it makes row selection work more reliably.)
The class TableQueryParameters is something I wrote to encapsulate the current state of the table on the user's screen. It includes what sort parameters, what filter parameters, what page we are on, etc. Because this needs to be preserved, the backing bean is ViewAccesScoped (via MyFaces CODI) and the TableQueryParameters is a property within it.
The TableQueryParameters are updated in response via AJAX events, which also update the form causing getDataModel to be called. The method isChangePending goes true when anything changes. So the getDataModel method uses this to generate only one fetch from the DAO between changes no matter how many times it is called.
BUT if the TableQueryParameters do change, I have to call runQuery with those parameters to fetch the new set of records the user wants to see. If I don't call it in getDataModel where do I call it?
Please advise.
You're basically lazily loading the data in the getter. You're not hitting the DB on every getter call within the same request (or view) scope. This is affordable. I don't use CODI, but I'd imagine that the getTableQueryParameters() call is also particularly cheap and nothing to worry about.
As to the concrete question, you'd normally do the DB/business job in an action(listener) method which is attached to the UICommand component and/or the ajax event tag.
For example (works also as <p:commandButton action> though)
<p:ajax listener="#{bean.deleteSelectedRow}" />
...
<p:ajax listener="#{bean.saveRowDetail}" />
with
public void deleteSelectedRow() {
someService.delete(selectedRow);
dataModel = loadDataModel();
}
public void saveRowDetail() {
someService.save(selectedRow);
dataModel = loadDataModel();
}
Depending on the meaning of p.isChangePending(), I think you could also get rid of it this way, it look like that you were setting it in the action(listener) methods.

Resources