Autocomplete


Overview

Autocomplete is a feature provided by many applications, and this component makes it easy to add an autocomplete field to your form. The autocomplete controls is not unlike an <xforms:select1> selection controls:
  • You use a single-node binding attribute to bind the control to the node holding the current value for the control.
  • You use a combination of <xforms:itemset> and <xforms:item> to define the values suggested to the user. Just as with <xforms:select1>, what is visible to users are item's labels and what is stored in the node to which your autocomplete is bound is the selected item's value.
  • Just like the <xforms:select1>, the autocomplete only implements a closed selection: users will only be able to select a value which exists in the itemset. The value of the node bound to the autocomplete won't be set to the value typed by users in the field, unless they select an item from the suggestion list or type text which is identical to one of the item's label.

Auto Complete field used for a list of countries


Modes

The autocomplete can work in one of three modes, which in order of increasing complexity, are referred to as: static, resource, and dynamic.

Static

  • When to use the static mode?

    • When you can reasonably determine the full list of suggested values without having to do any processing when users type in the field, such as submissions to a service you provide. You will typically use this mode when the list of suggested values isn't extremly large. Examples include a list of states, of countries, or of departments in your company.
    • When the default filtering mechanism works for you (more on this below).

  • Does the itemset really need to be static?

    No: you don't need hard code the list of values in your form, or even to know it when you generate the page. You can fetch the list from a service using an <xforms:submission>, and this list can change depending on other values entered by users in other fields of the page. But at a given point in time, that list must be complete; you can't change it depending what users are currently typing in the field as they type.

  • How does the filtering work?

    In this mode, the autocomplete field determines which values are shown in the suggestion based on what users typed in the field. It will only show items which label starts with the text entered by users. The comparison is not case sensitive. For instance, typing "ar" in a field that expects a list of countries with show "Argentina", amongst other countries, but not "Saudi Arabia".

  • What does the syntax look like?
<fr:autocomplete ref="country-name" dynamic-itemset="false">
    <xforms:label>Enter a country name: </xforms:label>
    <xforms:itemset nodeset="instance('all-countries')/country">
        <xforms:label ref="name"/>
        <xforms:value ref="name"/>
    </xforms:itemset>
</fr:autocomplete>

Resource

[NOTE: This feature was introduced after the 3.9 release, and isn't yet available in Orbeon Forms 3.9 or earlier releases.]
  • When to use the resource mode?

    When you have a service responding to an HTTP GET, that, given the current value users typed in the search field, returns an XML document with the list of suggestions.

  • What does the syntax look like?

    <fr:autocomplete ref="country-name" labelref="country-name/@label"
            resource="/xforms-controls/services/countries?country-name={$fr-search-value}"
            max-results-displayed="4">
        <xforms:label>Country code: </xforms:label>
        <xforms:itemset nodeset="/countries/country">
            <xforms:label ref="name"/>
            <xforms:value ref="us-code"/>
        </xforms:itemset>
        <xforms:alert>Value is mandatory</xforms:alert>
    </fr:autocomplete>

    • With ref, you bind the autocomplete to a node that will that the autocomplete will populate with the selected item's value.
    • [SINCE 2012-03-09] With labelref, you bind the autocomplete to a node that will that the autocomplete will populate with the selected item's label. This attribute is optional, but if you don't specify it, when loading a form where an autocomplete already has a value, then autocomplete will show empty, which most likely will be seen as incorrect behavior by users. This is because in resource mode, as well as in dynamic mode (see below), the autocomplete doesn't know about all the items, and thus can't automatically infer the label it needs to show users based on a value stored in the instance. labelref solves this problem by also storing the label in the instance.
    • The resource attribute points to an HTTP service responding with an XML document. It is interpreted as an AVT, and you can use the $fr-search-value to refer to the value users typed so far in the search field, as done in the above example.
    • The <xforms:itemset> points to the items in the XML returned by the service, and for each item to its label and value.
    • Whenever the result of the resource AVT evaluates to a different URI, a request is made by the autocomplete to that new URI to retrieve the a new itemset. Typically, your resource AVT will use $fr-search-value, and thus whenever users change the value in the search field, your service will be called. But this mechanism is general, and should your AVT, say, refer to a node in an instance that can change, then the service will we called again every time a change occurs.

Dynamic

  • When to use the dynamic mode?

    When you want the list of suggestions to change dynamically and that the resource mode doesn't offer the flexibility you need. (Say, the suggestions are not retrieved from a service, or a service that takes a POST rather than a GET.)

  • How to update the itemset?

    In most cases, you will want to update the itemset as users type, based on the value they entered. For this, listen to the event fr-search-changed on the <fr:autocomplete>, and run an action that updates the itemset. You are in control of where the data comes from, what subset of the data is to be displayed in the suggestion list based on what the user types, and when the suggestion is to be made.

    For instance, the following code reacting to the fr-search-changed event is written is a such a way that:

    • The suggestion list shows up only after users enter at least 2 characters.
    • The suggestion list is updating by running a submission which calls a service, providing the text typed by users so far.

<fr:autocomplete ref="instance('selected-countries')/dynamic"
                 labelref="instance('selected-countries')/dynamic/@label"
                 id="dynamic-autocomplete"

                 dynamic-itemset="true">

    <!-- React to user searching -->
    <xforms:action ev:event="fr-search-changed">
        <xxforms:variable name="search-value" select="event('fr-search-value')"/>
        <xxforms:variable name="make-suggestion"
                          select="string-length($search-value) >= 2"/>

        <xforms:action if="$make-suggestion">
            <!-- Update itemset -->
            <xforms:setvalue ref="instance('search-dynamic')/country-name"
                             value="$search-value"/>

            <xforms:send submission="update-countries"/>
        </xforms:action>
        <xforms:action if="not($make-suggestion)">
            <!-- Delete itemset -->
            <xforms:delete nodeset="instance('searched-countries')/country"/>
        </xforms:action>
    </xforms:action>

    <xforms:label>Country code: </xforms:label>
    <xforms:itemset nodeset="instance('searched-countries')/country">
        <xforms:label ref="name"/>
        <xforms:value ref="us-code"/>
    </xforms:itemset>
</fr:autocomplete>

For more on the labelref attribute, see the above section the resource mode.

Events

  • fr-search-changed – What did the user type in the field?

    When using the autocomplete in dynamic itemset mode, you can listen on the fr-search-changed event to be notified when the value typed in the field changes. This event is dispatched as users type in the field, just like the xforms-value-changed event would for an incremental <xforms:input>. The preceding example uses this event to update the itemset as users type in the field.

  • xforms-value-changed – When did the user make a selection?

    Just as with other XForms controls, you can listen on xforms-value-changed to be notified when the value of the node bound to the autocomplete changes. That event is also dispatched when users make a selection from the suggestion list, or when they happen to have typed a value that exactly matches one of the items in the itemset. [AS OF 2011-06-02] This event is dispatched when the autocomplete looses the focus. At that time, the node bound to the autocomplete is also updated with the value corresponding to the label typed or selected by users. If the label in the field doesn't correspond to label of any item in the itemset, both the value of the node bound to the autocomplete as well as the content of the search field are set to empty string.

Setting the content of the text field

Setting by label

You can set the content of the text field by sending the fr-set-label event to the autocomplete component, with label as context information. For instance:

<xforms:dispatch ev:event="DOMActivate" target="my-autocomplete" name="fr-set-label">
    <xxforms:context name="label" select="'Canada'"/>
</xforms:dispatch>

Just as if users had typed that label, if you are in dynamic itemset mode, this will trigger the fr-search-changed event, upon which your code can change the itemset. If you do change the itemset, and there is a single value corresponding to the label you specified, then the node to which the autocomplete is bound is set to that value.

Setting by value

When using a static itemset, you can set the value of the node you bind to the autocomplete control. This value can either be set initially (i.e. be present in the instance when the page is loaded), or can be set dynamically. When this happens, the autocomplete control will populate the search field with the label corresponding to that value, if there is one.

Don't set the value of the node bound to an autocomplete control if you are using the dynamic or resource mode. This is because when the itemset is dynamic, you load the itemset based on the search term entered by users, responding to the fr-search-changed event. If the value is set, the autocomplete can't dispatch a fr-search-changed event, since it doesn't know the label yet, and your code won't load the appropriate itemset that contains the label for the new value. In dynamic itemset mode, always use the fr-set-label event to indirectly set the value of the control. If you want the autocomplete to be populated based on a value in the instance when the form loads, on xforms-model-construct-done you need to find what the label corresponding to this value is, and dispatch an fr-set-label event with that label. [AS OF 2011-06-02] As an exception to this rule, setting the value of bound node to empty string is supported in all cases: it is understood as a "reset" and will also set the search field to empty string.

Maximum number of displayed results

By default, the autocomplete displays a maximum of 10 items. You can have the autocomplete display less of more results by:
  • Adding attribute max-results-displayed. You would typically use this attribute to provide a static value. For instance:

    <fr:autocomplete max-results-displayed="20">

  • Adding a nested element <fr:max-results-displayed>, which supports single node binding attributes (ref, model, and bind) as well as the value attribute. For instance:

    <fr:autocomplete>
         <fr:max-results-displayed value="/configuration/max-results-displayed"/>

  • By defining the property:

    <property as="xs:integer" name="oxf.xforms.xbl.fr.autocomplete.max-results-displayed" value="10"/>
When several configurations are used for an autocomplete component, the <fr:max-results-displayed> element has priority over the max-results-displayed attribute, which has priority over the global property.

Displaying all the items

For Orbeon Forms 3.9  [SINCE 2010-06-11]

The autocomplete control shows suggestions as users start typing in the field, based on what they have already typed. What if users don't know what to start typing? This is where the show-full-itemset-dropdown="true" attribute comes into play. Add it to your <fr:autocomplete>, and you'll see a button added next to the text field:


When users click on it, they will see the full list of items from which they can make a selection.


Since for show-full-itemset-dropdown="true" to work, the component needs to have access to the full itemset, not just the partially loaded item load based on what users typed so far. So you can only use show-full-itemset-dropdown="true" when you are in static itemset mode.


For Orbeon Forms 4.0 [SINCE 2011-06-02]

By default, the autocomplete control shows a button next to the autocomplete. Clicking on the button shows the suggestions users would get if they had typed the value currently in the field. For a country list, if you have Sw in the field and you click on the button, you would get Swaziland, Sweden, and Switzerland. This won't be a common scenario as users will automatically get those suggestions when typing the Sw. A more common case is users not knowing what to type in the field. In that case they can click on the button when the field is empty, and the suggestion will contain the first n possible values.


This is the out of the box behavior in static mode. It is left to you to return the first n items when in dynamic mode, when you get the fr-search-changed event with an empty label, and in resource mode when your service is called with an empty search value.

You can disable this button by adding a show-suggestions-button="false" attribute on the control. The default value of this attribute is true.


Styling

  • By default, the width of the input field is 140px. You can change the width of all the autocomplete fields with:

    .xbl-fr-autocomplete .xforms-input input { width: 30em }

    To change a specific autocomplete, add your own class on the autocomplete, say <fr:autocomplete class="my-autocomplete"> and the following CSS:

    .my-autocomplete .xforms-input input { width: 30em }

  • You can constraint the height of the section that displays results with CSS. For instance, the following will set a max height constraint of 100 pixels:

    .fr-autocomplete-container .yui-ac-content {
        overflow-x: hidden;
        overflow-y: auto;
        max-height: 100px;
        *height:expression(this.scrollHeight>100?"100px":"auto");
    }

    Then, a vertical scrollbar will show when necessary:


Possible future features