When I code in Obyx, I often wish to access elements that may be quite distant from the information that is available to me. An example would be given an attribute of an element, a need to access or modify the attribute of it's sibling (or further relation) directly. Let's look at a snippet. We must imagine that the rest of the markup is not known to us. Of course, in general, it is best to manipulate a snippet at it's own level of control, but sometimes controls need to access views that were generated elsewhere.
<label for="my_field"> <span>ISBN</span> <input id="my_field" value="foobar" /> </label>
So, here is a small part of a form - with a label around an input, and a span that holds the label text. I know the id of the input (my_field), but I want to affect the class attribute of the sibling element (the span) to indicate, for instance, an error as follows:The desired result
<label for="my_field"> <span class="error">ISBN</span> <input id="my_field" value="foobar" /> </label>
The thing to remember about XPath is the capacity to use "predicates" - the square brackets that act as filters over the xml nodes. So to start with, if we want to access the input itself, we can use the xpath://h:input[@id='my_field']
A common approach is to then think - ok, that's what I start from, so let's move from there...//h:input[@id='my_field']/preceding-sibling::*/@class
I don't think there's anything particularly wrong with that - but I prefer not to have to use axes where possible.
Another approach, is to think about the parent (label) as our starting point, rather than the input itself, and then we can step back down. This implies greater knowledge of the snippet - but that also means that there is less opportunity for multiple results to occur! So let's look at using the input with the my_field attribute as a predicate of the label!//h:label[h:input[@id='my_field']]/h:span/@class
There is likely some great xpath guru who can tell me which is best - but personally I prefer the latter solution! What method do you prefer?
So, assuming that the snippet we wish to amend is somewhere within the store 'view' the Obyx code for this particular change could be as follows:
<instruction> <output space="store" value="view#//h:label[h:input[@id='my_field']]/h:span/@class" /> <input value="error" /> </instruction>
Just to remind you, writing to a non-existing attribute in Obyx will generate the attribute as needed.