Projects‎ > ‎XBL‎ > ‎

XBL Components Processing Order

I think that the order in which the XBL components is becoming an issue with the xxbl:transform feature that we've recently introduced.

Let me give a real world example...

We need to add pagination support to the datatable widget. It appears that pagination is a common feature that do not only apply to datatable but can apply to any xforms:repeat control.

It would thus be handy to define a fr:pagination widget that would rewrite the nodeset expression of its embedded xforms:repeat to limit its range and add the necessary controls and decorations to select this range.

So far, so good, such a widget should be fairly easy to write in pure XForms using the xxbl:transform feature and it could be used with something as simple as:

<fr:pagination size="10">
  <table>
    <xforms:repeat nodeset="foo">
      ...
    </xforms:repeat>
  </table>
</fr:pagination>

Of course, the expectation is also to be able to use it to paginate a datatable, and it would seem natural to write:

<fr:pagination size="10">
  <fr:datatable>
    <xforms:repeat nodeset="foo">
      <tr>
      ...
      </tr>
    </xforms:repeat>

  </fr:datatable>
</fr:pagination>


Or even

<fr:pagination size="10">
  <fr:datatable>
    <tr repeat-nodeset="foo">
      ...
    </tr>
  </fr:datatable>
</fr:pagination>


What's bad with that?

With the current implementation, when the fr:pagination widget is processed, it's bound element still contains embedded bound elements that are not processed yet. In other word, it sees fr:datatable rather the corresponding shadow tree.

The issue isn't that much that there is an additional level of imbrication (the fr:pagination widget would have to deal with that kind of cases anyway) but that the fr:datatable widget rewrites the xforms;repeat nodeset to extract if from the "repeat-nodeset" attribute if needed but also to support sortable datatables.

I'm not sure that I understand this one. If you were to see the shadow tree, it was as if you had this in the first place:

<fr:pagination size="10">
    <table>
        <xforms:repeat nodeset="foo">
            <tr>
                ...
            </tr>
        </xforms:repeat>
    </table>
</fr:pagination>

Assuming you have this (no XBL inside the <fr:pagination>), how does the <fr:pagination> handle this? Is it going use to XSLT to change the nodeset attribute on the most outer <xforms:repeat> it can find?

Yes, exactly --Eric

Without using XSLT (which if possible would be my preference), could we do this instead:

<fr:pagination nodeset="/company/emp" size="10">
    <table>
        <xforms:repeat nodeset="$current-page">
            <tr>
                ...
            </tr>
        </xforms:repeat>
    </table>
</fr:pagination>
  • The nodeset attribute is moved on <fr:pagination> (IMHO this also makes more sense from users' perspective).
  • The <fr:pagination> component defines a variable $current-page which points to the nodes in the current page. That variable is defined by the <fr:pagination> component.  
How does this sound?

Without using XSLT, the component has to define a fixed structure for its content since it generates the table and xforms:repeat elements. Now, what happens if I want to paginate an unordered list rather than a table or just add attributes to the table element?

There must be something I don't get. Without XSLT, fr:pagination doesn't need to know anything about what is inside fr:pagination. The code would look like:

<xbl:xbl>
    <xbl:binding id="fr-pagination" element="fr|paginatinon">
        <xbl:template>
            <xforms:group id="map-container" ref=".">
                <xxforms:variable name="current-page" select="..."/>
                <xbl:content includes="*"/>
                <!-- Next/prev links for pagination -->
            </xforms:group>
        </xbl:template>
    </xbl:binding>
</xbl:xbl>

It would just defines $current-page to be a nodeset including, say, the first 10 nodes and the XForms code using $current-page can generate whatever markup it wants (list, table, use other XBL components). Am I missing something?

-- Alex

With this approach (if I am not the one who misses something) the enclosed content needs to adapt the logic of its repeat nodeset to return what is relevant for the current page. What I wanted to achieve was to take all the logic of the pagination (including this selection) out of the enclosed content to be performed by the component. In other word, I'd like to be able to take any piece of XForms/XHTML with an XForms repeat wrap it into this componant to get pagination. That's what the current version of the datatable does both for pagination and sorting and I find that most convenient.

-- Eric

The enclosed content will use <xforms:repeat nodeset="$current-page"> instead of <xforms:repeat nodeset="/company/emp">. (Here using an <xforms:repeat> for the example, but it could be anything, e.g. the gridtable component instead of a simple repeat.) Is this what you are refering to?

--Alex

Yes. More exactly, in the case of the datatable component the nodeset would be generated by the component and using $current-page rather than /company/emp means that the datatable component needs to know about the pagination component in which it is included.

Ah! I think I understand the issue: the datable can't just work on the subset of the items in the current page (as some code inside an <xsl:repeat-group> would use current-group()), because the datatable handles the sorting, so what is in the current page not only depends on what the nodeset is (provided by the user), but on sorting (known to the datatable, e.g. based on user input). So there is a more complex interdependency there. After all, maybe we shouldn't try to build 2 components, and having paging done in the gridtable is just fine.

--Alex

Now, to be honest, my suggestion only fixes the issue when the updates to the xforms:repeat nodeset follows the "ancestor" axis and I am not sure that this will always be the case. If we add another component in the loop to generate search boxes, a natural order from a UI point of vew would be fr:search/fr:pagination/fr:datatable but the xforms:repeat nodeset would be pagination(search(sort())) or pagination(sort(search())) but certainly not search(pagination(sort())) as my suggestion would lead to! I guess I have to find something more clever...

--Eric

What I find very interesting with the XSLT approach is that you can be very flexible and keep standard XHTML/XForms elements inside the bound tree instead of having to fix a set of features.

--Eric

(We talked with Erik about exposing the shadow tree and we don't like it very much for many reasons – it breaks the encapsulation and would be quite complicated to implement.)

OTH, the current way is very risky since it's easy for an XSLT transformation to accidentally write templates that break inner bound elements.

Anyway, how can we fix issues like the one I mentioned and write a pagination widget that can paginate either plain tables,  datatables or any other xforms:repeat (this is just an example, I am sure that there will be many other similar use cases)?

--Eric

Answering to myself, I see a hack to fix this issue in this specif case with a convention for  XXForms variables so that the datatable knows where it can insert its sort between the pagination and the original nodset... However, this would be quite hacky and doesn't solve the issue in a generic way.

--Eric

--Alex

This problem wouldn't happen if when a widget is processed it could be sure that all the embedded references have been processed and that it sees the corresponding shadow trees rather than the bound elements.

And I don't see other options to solve that kind of issues!

WDYT?

--Eric van der Vlist

Comments