Search This Blog

Friday, 28 February 2014

Updated: Drop-Down List Control with auto-complete (Searchable Drop-Down List)

I have made some changes to my "Drop-Down List Control with auto-complete" sample. 

To start I would like to explain the title of this sample, as it is really a textbox pretending to be the UI portion of the drop down list and a textbox pretending to be the list portion of drop down list.  I am not sure what I would have called the sample but "Drop-Down List Control with auto-complete" and "Searchable Drop-Down List" came from the title of the first two forum posts I tried to answer with this sample.  It seems we can't update the entries in a drop down list when the list is open, which is why I have tried to mimic the behaviour with these two controls.

The changes I have made are;
  • Display the list when the user enters the field, so the user knows this field is special
  • Show some "ghost text", giving instructions to the user
  • Sort the values in the list
  • Allow for strict scoping
  • Allow for keyboard users, previous the sample only worked with the mouse.

To use this in your own form

  • Copy the subform DropDownList to you own form
  • If you don't want the option to match first characters/match any characters the delete the RadioButtonList under the DropDownList subform.  You may need to update the code in the initialise event of DropDownList subform to set the default option, currently it defaults to match first characters.
  • Change the null display pattern in the Search textbox, to suit the items you are search over.  This is how the "ghost text" is implemented and currently says "'Search by country name".
  • Update the code in the change event of the Search textbox, this is where the matching is performed against the list of countries.  The format of the countries XML list is;
    • <Country>
            <Region>Oceania</Region>
            <Code>36</Code>
            <Name>Australia</Name>
        </Country>
    • So the binding expression for match first characters is;
      '$record.Country.[Lower(Left(Name,' + (xfa.event.newText.length) + ')) == "' + xfa.event.newText.toLowerCase() + '"]'
    • This matches the left characters of the Name element against the characters entered in the Search textbox, xfa.event.newText.  A case insensitive match is made by calling toLowerCase() on the xfa.event.newText and calling the FormCalc function Lower on the Name element.  I find the using FormCalc in binding expressions to be about four times faster.

The sample

For a PDF version of the sample with the countries data already imported download CountriesV2.pdf.  For the XDP template download CountriesV2.xdp and the preview data Countries.xml.

Sunday, 9 February 2014

Listing all fields in a form - The macro

Radzmar has written a macro that makes Listing all fields in a form method I described in a previous blog a lot easier.

The macro contains the XSLT so you no longer need to copy it around and the macro also resolves the namespace so the XSLT will work for all Adobe Reader target versions.

Run the macro and you will be prompted to save the XML file that can be opened into Microsoft Excel as an XML table, without having to make temporary changes to your XDP file.

Download the macro in XFAObjectList.zip which contains the JavaScript of the macro and the macro.xml file Designer uses to create the Tools ... Macros menu items.

Saturday, 1 February 2014

Adding values in a form to an email

Consider we have some text like "Please contact us or visit our website for more information" in our form.  So if our user clicks on "contact us" we open an email client and if they click on "website" we open the web page in the browser.

Before Designer ES2 we would have done this with two buttons with no background or border and using positioned layout carefully placed them in the correct spot.  The "contact us" button would have called the app.mailMsg and the "website" button would have called app.launchURL.

Since Designer ES2 we can add these as hyperlinks directly into a text control.  This means the links are correctly announced when using the accessibility screen readers,  but it also makes it harder to include values from the form in the email.  We might want to send the email to different recipients, use different subject lines or generate different body text depending on other values selected in the form.

If you look at the XML Source for the text control you will see a rich text value that looks something like;

<body xmlns="http://www.w3.org/1999/xhtml" xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
  <p style="text-decoration:none;letter-spacing:0in">
   Please
   <a href="
mailto:sample@example.com">contact us</a>
   or visit our
   <a href="
http://adobelivecycledesignercookbookbybr001.blogspot.com.au/">website</a>
   for more information
  </p>
</body>


The full syntax for the mailto link is

mailto:emailaddress?cc=emailaddress?bcc=emailaddress?subjext=text?body=text

Even though the Designer interface only supports the to email address and the subject header, the cc, bcc and body still work. We just need some code to update the mailto link.

To do this I am using E4X.

To start load the rich text value into an E4X XML object,  to do this we must first remove the XML declaration.  Note that the class name of the rich text value is "#html" and to refer to an element we need to prefix it with a "#" character, which is why we see the rather strange looking double "##".

var body = new XML(Text1.value.exData["##xHTML"].saveXML().replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>\n*/, "")); 

Declare the namespace and set the white space handling to be the same as Adobe Designer.

var xhtml = new Namespace("
http://www.w3.org/1999/xhtml");
XML.ignoreWhitespace = false;
XML.prettyPrinting = false;


Loop though all the anchor tags

for each (var anchorTag in body..xhtml::a)
{


  Look for the "mailto:" tag

 if (
anchorTag.@href.toString().indexOf("mailto:") === 0)
 {


   Generate the new "mailto" tag based on fields within the form

  attributes = [];
  
anchorTag.@href = "mailto:" + ToAddress.rawValue;
  if (!CCAddress.isNull)
  {
   attributes.push("cc="+CCAddress.rawValue);
  }
  if (!BCCAddress.isNull)
  {
   attributes.push("bcc="+BCCAddress.rawValue);
  }
  if (!Subject.isNull)
  {
   attributes.push("subject="+encodeURI(Subject.rawValue));
  }
  if (!Body.isNull)
  {
   attributes.push("body="+encodeURI(Body.rawValue));
  }
  if (attributes.length > 0)
  {
   
anchorTag.@href += "?" + attributes.join("&");
  }
  anchorTag.setChildren(anchorTag.toString());
 }
}


Load the new value back into the text control.

Text1.value.exData.loadXML(body.toXMLString(), /* Ignore root node */ false, /* Overwrite content */ true);


If you wanted to send an email to a list of addresses just separate them with a comma.

Enabling / Disabling a hyperlink

We can use a very similar approach to enable and disable a hyperlink.

To disable, we would do;

anchorTag.@style = "color:#969696";
delete anchorTag.@href;


To enable, we would do;

delete anchorTag.@style;
anchorTag.@href = "http://adobelivecycledesignercookbookbybr001.blogspot.com.au/";



To see this in action download this sample, EmailHyperlink.pdf.

Friday, 24 January 2014

Listing all fields in a form

Because the XDP file that defines a Designer form is just a XML file we can run an XSLT over it.

In this case we will produce a list of all the field objects that we can view in Microsoft Excel,  so we will also use Excel to run the XSLT.

First download the XSLT file, XSPFieldList.xslt , and place it in the same directory as the XDP file of your form.

Edit your form XDP file using Notepad or similar and add the following line after the XML declaration;

<?xml-stylesheet type="text/xsl" href="XDPFieldList.xslt"?>

So it should look something like;


There is a line in the XSPFieldList.xslt that looks like;

xmlns:xfa="http://www.xfa.org/schema/xfa-template/2.8/"

This might need to change to match the namespace in your XDP file (see the last line in the screen shot above)  depending on which version of Reader your form is targeting. The namespace ending in  xfa-template/2.8 means my form is targeting Reader 9.0 and above. For Reader 9.1 and above it would be xfa-template/3.0.

Now in Microsoft Excel open the XDP file (Excel doesn't know about XDP files so your will have to select "All Files (*.*)" in the Open dialog).  The first thing Excel will do is popup an Import XML... dialog;


This means it has found our xml-stylesheet line and wants to confirm we want to run the XSLT, so select "Open the file with the following stylesheet applied (select one):"

You will then get a "different format" message, which is fair enough as the XSLT will have changed the format so select Yes;


Then you get an Open XML dialog, which defaults to "As an XML table" which is what we want to do, so select OK.

 
 
The final dialog is the no schema message.  We haven't provided a schema so Excel will create one for us.  This dialog you can turn off.
 
 
 
 
The final result will look something like;
 


There is a lot you can add to the XSLT so think of this as a starting point, and remember to remove the xml-stylesheet line from the XDP as Designer will think the form is created in a new version and not allow you to do much except look at the XML Source.

Response Files

This approach can also be helpful in working with a response file from the collection of returned forms.  If you export the responses as XML then they can be processed by an XSLT and loaded into Excel in the same way.

If your XSLT produces an HTML document containing a HTML table then Excel will still load it into a spreadsheet, but you now can specify business names for the columns not just the field names from the form, so you can now have spaces in the column names. 

The XSLT will also allow you to do some decoding, such as turning 1 and 0 into 'Yes' and 'No' or whatever formatting you want.

UPDATE

Radzmar has written a macro to make the first couple of steps easier, so you no longer need to make a temporary change to your XDP file and the macro works out the correct namespace.   More details in a later blog Listing all fields in a form - The macro.

Friday, 17 January 2014

Adobe LiveCycle Designer Tip #3 - Checking your form bindings

The Report Palette is often forgotten hidden away at the bottom of the workspace, and the Binding tab of this palette is even more forgotten.

But this tab has several options for checking the bindings of your form, these are available when you click the palette menu button in the top right corner (circled in red in the image below).


Fields with Normal Data Binding

This is the default, but a binding option I have never really used.  All my forms are bound to a schema, so this tab has always been empty for me and for years totally ignored.


Fields with Data Binding by Reference

This is the option I need as it shows the name of the form object and the data connection SOM expression.



Fields with Global Data Binding

This lists all the fields with global binding, obviously enough.  But it also has a "Check Global Binding" button.  This will check all fields with the same name have global binding checked.

Designer seems pretty good at checking this as binding changes are made so I am not sure in what scenarios this error occurs, but if you are using global binding and it's not working this would be a good place to start.  (I only got this error because I was editing the XML Source).


Fields with No Data Binding

Gives a quick check that you have bound all the fields that you need to.


Fields with Import Data Binding

These are the fields that are passed to a web service call.


Fields with Export Data Binding

Theses are fields that are bound to the result of a web service call.


Unbound Data Connection Nodes

This is the opposite of Fields with No Data Binding, gives a quick check which nodes in the data connection have not yet been bound to a form object.

Objects with Dynamic Property Binding

This lists all the dynamic property bindings made for captions, list items and error messages.

This option also lists all the bindings that are set using the setProperty element.  This element allows most other form object properties to be set similar to dynamic property binding but are not supported by the Designer user interface. 

This is particularly handy as they can only be added using the XML Source view and can easily be forgotten. So if I wanted to set the presence of a form field using a value in the data connection I could add <setProperty target="presence" ref="$.presence"/> in the XML Source, where target is the form object property and ref is the data connection reference.

You can also use setProperty to refer to values that are not in the default data connection, values you don't want to be part of the form submission, such as <setProperty target="assist.toolTip" ref="!toolTips.value"> , note the data connection reference uses the "!" character which is the same as xfa.datasets.  so toolTips is a dataset at the same level as the default data one.

Friday, 6 December 2013

Formatting telephone numbers

The phone numbers I have to deal with are always 10 digits, but they can be either a two digit prefix, followed by two sets of four digits or a four digit prefix (starting '04') followed by two sets of three digits. For example (02) 1234 1234 or 0400 123 123.



We don't want our users to have to type the spaces and parenthesis but we do want the phone number field to display them once entered,  ignoring them when typed.  This is when we use the display patterns.


 
Because we also want our users to paste a formatted value into the field, including the spaces and parenthesis, we will start with a Text Field.  If we used a Numeric Field then we won't be able to perform a paste,  if the value contains anything but valid numeric characters then the whole paste would fail.



This means we must add some character filtering code to the change event so only digits are accepted.  Filtering characters means testing xfa.event.change when the characters are coming one at a time, as they do when we are typing or, typically, testing xfa.event.newText when we are pasting a value.



But in this case we also want to set the maximum character limit to 10, this means the maximum length of xfa.event.newText is also set to 10 characters.  This becomes a problem when the pasted value is formatted, in which case it will be either 12 or 14 characters. This is when we must use the xfa.event.fullText, which gives the full value before the truncation has occurred.



So our change event code now becomes;


xfa.event.change=(xfa.event.change.length > 1) ?
                   xfa.event.fullText.replace(/\D/g,""):xfa.event.change.replace(/\D/,"");

That is if the value causing the change is longer than one character, remove all non-digit characters, otherwise if the change is one character and that character is not a digit then ignore it.



The final part of our phone number field is to change the display pattern depending on which type of phone number has been entered, so 
our exit event code becomes;


var formatString = "";
if (!this.isNull)

{
    if (LandLinePhoneNumberRegex.test(this.rawValue))
 
    {
        formatString = "'('99')' 9999 9999";
    }
 
  else
 
    {
        if (MobilePhoneRegex.test(this.rawValue))
        {
            formatString = "9999 999 999";
        }
    }

}

this.format.picture.value = formatString;


Setting a display pattern now means we can test for an invalid phone number using;


TelephoneMobile.rawValue === TelephoneMobile.formattedValue


 
This works because the field will only be formatted when a value matches the display pattern, all other values are display as is and so the formatted value will equal the raw value.

Format strings with single quotes

If you ever want a single quote as part of the display pattern, as in a feet and inches measurement like 6'2" then you will need to use two single quotes and wrap then in single quotes.  So four altogether,

text{9''''9"}

 

Sample Form

Formatting telephone numbers.pdf

Tuesday, 19 November 2013

XML Schema minOccurs="0" handling

If you define a data connection using an XML Schema you can specify a minOccur="0" attribute to allow an element to be omitted from the resulting XML.  So an XML Schema element defined as;

<xs:element name="preferredContactMethod" type="preferredContactMethodType" minOccurs="0"/>

Will produce a data connection description of;

<preferredContactMethod dd:minOccur="0" dd:nullType="exclude"/>

And the resulting XML will not contain a preferredContactMethod element if the value is null.

It is the nullType="exclude" attribute that controls the handling of the empty element.  However, Designer only sets this attribute on simple types.  If the minOccurs="0" is on an element that contains child elements or an attribute (that is complex types) then the nullType="exclude" attribute is not added to the data connection description, which can cause the resulting XML to fail schema validation.

This macro will go though the form data description and add the missing nullType="exclude".

Here is a sample form that shows the differences, ComplexTypeNullTypeExclude.pdf.  The same fields are duplicated with the first set having the default behaviour and the second set that has been updated by this macro.

And here is the macro, AddNullTypeExcludeToComplexTypes.zip.  Extract the files to the macros directory of your Designer ES3 (or later) install and you will now get a "Add dd:nullType="exclude" to complex types" option in the Tools ... Macros menu.  (In Designer ES2 you need to extract the files to the Scripts directory of your install and the menu option will be AddNullTypeExcludeToComplexTypes.