Wednesday, March 13, 2013

Dropdown populated based on values from another form

Say you created two forms with Form Builder: a contact form and a phone call form. In the phone call form, you use one of the fields to capture the name of the person you'll have a call with. Instead of using a text field, you'd like to have a dropdown, with the values in the dropdown coming from values previously captured with the contact form.

Form Builder doesn't come, out-of-the-box, with a way to link forms in the manner described above. However, this is something that you can do with Form Builder's Services & Actions. The idea goes as follows:

  1. Write a query retrieving the contact names captured with the contact form. The query will differ depending on the persistence layer you're using: it will be in XQuery if you're storing data in eXist, or SQL if you're storing data in Oracle or MySQL.
  2. Create a service that runs this query. It will be either an HTTP service (to run XQuery) or a database service (to run SQL).
  3. Add a dropdown to your form.
  4. Create an action that runs the service on form load, and use the result to populate the dropdown.

The query

For the purpose of this post, we'll assume data is stored in eXist, which is bundled with Orbeon Forms, and we'll query data in the contact form, which is one of the sample forms. Since we're querying eXist, we're using XQuery:

for $c in /form/contact return element contact {
    element label {concat($c/first-name, ' ', $c/last-name)},
    element value {tokenize(util:collection-name($c), '/')[last()]}

This query returns one <contact> element per entry in the contact form, with for each one, the person name (to be shown in the dropdown) and an id (stored in form data, so we can link a phone call with a person). The root element of data captured with forms you create with Form Builder is always <form> and in the contact form the first/last name are in a section named contact, so the query iterates over /form/contact. Also note how the query retrieves the document id in which a node $c is stored with tokenize(util:collection-name($c), '/')[last()]. With this, the query returns <contact> elements that look like:

    <label>Homer Simpson</label>
    <label>Charles Burns</label>

The service

You can pick a name for the service, say get-contacts. The URL should point to the contact form, as it is stored in eXist. With the embedded eXist, if Orbeon Forms is deployed on http://localhost:8080/orbeon then the URL pointing to the contact form will be (here the URL is split in two lines for formatting, when pasting it in Form Builder put it on one line with no spaces):


The service POSTs and XML document as follows, with the XQuery above within the <exist:text> element.

<exist:query xmlns:exist="" max="0">
  <exist:text> ... </exist:text>

The action

Finally, the action calls the service on form load, and uses the result to populate the dropdown, as shown in the screenshot on the right (click it to see a larger version). Click the test button, and you'll see how the dropdown populates with values entered previously in your contact form.

Wrapping it up

Since populating a dropdown with values from another form at this point isn't supported out-of-the-box by Form Builder, to do so you need to write a query against the database and use services and actions to run the query and populate the dropdown. While this isn't as simple as we'd like it to be, and future versions of Orbeon Forms might make this process simpler, it is already something that you can do today, and it goes to show the flexibility of the services and actions feature, which allows you to query existing data, whether it has been captured with a form you created with Form Builder or by some other mean.

1 comment: