© Jennifer Harder 2017

Jennifer Harder, Enhancing Adobe Acrobat DC Forms with JavaScript, https://doi.org/10.1007/978-1-4842-2893-7_12

12. Working with Dropdown Menus

Jennifer Harder

(1)Delta, British Columbia, Canada

So far you have looked at ways to improve text boxes, buttons, radio buttons, and check boxes with JavaScript. However, there are other types of fields that can be dynamic as well. In this chapter, you are going to take a look at incorporating JavaScript with dropdown menus so that information will be accurately entered or calculated in order to avoid clients entering incorrect information.

Note

If you want to work along in this lesson or review the final result, download the Chapter 12 files from www.apress.com/9781484228920 . The file with the label “Start” is the file without the code and the file with the label “End” is the final result. You will also find folders with original MS Word and PDF files if you would like to edit them and a folder containing the original scripts if you would like to add them to your own PDF forms.

If you are creating your form from an original PDF, refer to the “Forms Review” section in Chapter 1.

Remember that to view the properties of a field you must select the Prepare Form tool; only then can you right-click or double-click a field to review its properties.

Current Skills Request Form

In a small company , an employer may want to ask their employees to submit a form to HR that informs them of the employee’s current skill set. In this example, an employee selects the name of their department from the dropdown menu, and their name and some of their information is automatically entered in. If their department is not on the list, they can still type that information in themselves. See the Request Form End PDF. Refer here to Figure 12-1.

A449379_1_En_12_Fig1_HTML.jpg
Figure 12-1. The Current Skills Request Form project

This automatic action has two parts: a document JavaScript and a script attached to the dropdown menu. Refer here to Figures 12-2 and 12-3.

A449379_1_En_12_Fig2_HTML.jpg
Figure 12-2. Enter the JavaScript tool and choose the Document JavaScripts tool
A449379_1_En_12_Fig3_HTML.jpg
Figure 12-3. Inside the document JavaScripts dialog box with the script SetSelectValues entered with the Add button

The Document JavaScript I’ve created is called SetSelectValues:

//Global Document SetSelectValues Script
// Place all prepopulation data into a single data structure
var DeptInfo = { Accounting:{ contact: "Kathy Jones",
                              title: "Chief Officer",
                              email: "[email protected]",
                              deptnum: "tmc1434",
                              deptphone: "999-999-9922"},
                Engineering:{ contact: "Frank R. Smith",
                              title: "Senior Specialist",
                              email: "[email protected]",
                              deptnum: "tmc1435",
                              deptphone: "999-999-9921" },
       "Graphic Department":{ contact: "Nancy Smith",
                              title: "Artwork Planner",
                              email: "[email protected]",
                              deptnum: "tmc1436",
                              deptphone: "999-999-9923" },
                  ITSupport:{ contact: "Troy  Carson",
                              title: "Official Coder",
                              email: "[email protected]",
                              deptnum: "tmc1437",
                              deptphone: "999-999-9927" },
                  Marketing:{ contact: "Janice Walker",
                              title: "Marketing Advisor ",
                              email: "[email protected]",
                              deptnum: "tmc1438",
                              deptphone: "999-999-9925" },
         "Mine Site Safety":{ contact: "Rick James",
                              title: "Safety Officer",
                              email: "[email protected]",
                              deptnum: "tmc1439",
                              deptphone: "999-999-9924"},
                 };
function SetSelectValues(cDeptName)
{
  this.getField("DeptContact").value = DeptInfo[cDeptName].contact;
  this.getField("DeptTitle").value = DeptInfo[cDeptName].title;
  this.getField("DeptEmail").value = DeptInfo[cDeptName].email;
  this.getField("DeptNumber").value = DeptInfo[cDeptName].deptnum;
  this.getField("DeptPhone").value = DeptInfo[cDeptName].deptphone;
}

SetSelectValues is a function that is calling upon an array variable called DeptInfo. Remember that an array is a complex variable that can hold multiple pieces of information. In the array above there are six departments:

  • Accounting

  • Engineering

  • Graphic Department

  • Marketing

  • IT Support

  • Mine Site Safety

Each department has five text fields that must be filled in:

  • Contact

  • Title

  • Email

  • Department Number

  • Department Phone

In a sense, this has become an array within an array, or a nested array.

Note

This type of array is, as mentioned in Chapter 9, called an associative array. A better term is an associative syntax, in the case of JavaScript when referring to these object literals with in the square brackets []. Rather than using indexes or numbers 0-3 to call upon a variable, it uses words to call up information or object literals/variables. It is also nested in that it can hold another object literal within an object literal. Later in this chapter you will see another example that is also nested.

The last part the function SetSelectValues must now draw in each of these parts from the associative syntax so that when a selection from the dropdown menu is chosen the correct information is filtered in the correct order.

Notice how each field is calling for an item called cDeptName; however, you don’t see it anywhere in the global function as a variable or part of the associative syntax. What is this? cDeptName refers to the dropdown field DepartmentNames. Refer to Figure 12-4.

A449379_1_En_12_Fig4_HTML.jpg
Figure 12-4. The dropdown and text fields in Prepare Form Tool view

Check “Allow user to enter custom text” in the Dropdown Options tab . This way if a department is not on the list, the user can enter the information themselves. Also check “Commit selected values immediately” to allow the text to fill the other fields as soon as the dropdown selection is released. It is optional to check the “Check spelling” box. Refer to Figure 12-5.

A449379_1_En_12_Fig5_HTML.jpg
Figure 12-5. The Dropdown Properties Options tab that is referenced in the document JavaScript

Now go to the Format tab. Refer to Figure 12-6.

A449379_1_En_12_Fig6_HTML.jpg
Figure 12-6. The Format tab with a custom setting and a custom keystroke script

Enter the following JavaScript into the Format tab’s custom keystroke script section:

// Custom Format Script
if( event.willCommit ){
   if(event.value == " "){
     this.resetForm(["DeptContact","DeptTitle","DeptEmail","DeptNumber","DeptPhone"]);
}
   else{
     SetSelectValues(event.value);
}
}

Now you have created this field and applied JavaScript to the custom keystroke script area (See Figure 12-6). The script “will commit” because this was also checked in the Options tab (See Figure 12-5) and do one of two things:

  • If you set the dropdown menu to blank, all the text fields will become blank or “ ” and it will reset these form fields.

  • Otherwise (else) it will make a connection with the function SetSelectValue found within the document JavaScript so that the information or values will flow through to the text fields.

Without this trigger to the function, the dropdown field would not be able to place any data in the dropdown list . Note that if the selection is set back to blank information in the dropdown menu, then the fields will reset themselves immediately because it is part of the willCommit event. And as mentioned earlier, a person who could not find their information in the list could enter their own data in the blank area.

Note

No JavaScript code was applied to the text fields.

Parts Order Form

Sometimes a client has to search through multiple items and then through multiple subitems in order to find the exact information or item they are looking for. For instance, in a job setting, there may be more than one type of screw, in retail more than one size of T-shirt or more than one color. In just about any job, you can find items that have several variations, and those variations might have different prices depending upon their size and material.

The parts order form is a simplified example of this scenario. See the Parts Order Form End PDF. Refer here to Figure 12-7.

A449379_1_En_12_Fig7_HTML.jpg
Figure 12-7. Dropdown menus in the parts order form project

This time the form contains two connecting dropdown menus : Name and Description. Refer to Figure 12-8.

A449379_1_En_12_Fig8_HTML.jpg
Figure 12-8. The two connecting dropdown menus found in the current project

Once the user chooses a name, they can choose a description. Once the description is chosen, a price is then set for the part. Refer here to Figure 12-9.

A449379_1_En_12_Fig9_HTML.jpg
Figure 12-9. The Description dropdown menu changes the Price field

All the customer has to do is decide how many parts he wants to buy, and the order form prices start to fill in. Refer here to Figure 12-10.

A449379_1_En_12_Fig10_HTML.jpg
Figure 12-10. When a client enters a quantity, this changes the total price

Let’s look at this in depth. Once again you will need to create some global document JavaScript . Refer here to Figure 12-11.

A449379_1_En_12_Fig11_HTML.jpg
Figure 12-11. Document JavaScripts that will affect various fields in the project

As you can see, with the addition of another dropdown, more JavaScript is required.

The first thing you need to do is create your associative syntax/object literal array and the function addItems. Each associative syntax oArray is separated with a comma.

// Global Document addItems
var oArray = {
    Drills: [ ["-","None"], ["Small",19.95], ["Medium",29.95], ["Large",39.95], ["Extra Large",44.95]],
    Lubricants: [ ["-","None"], ["Oil",69.95], ["Grease",49.95], ["Super Smooth",79.95],["Averge",139.95]],
    Gears:  [ ["-","None"], ["Small",149.95], ["Medium",159.95], ["Large",219.95], ["Very Large",339.95]],
    Mining:[ ["-","None"], ["Hard Hat",39.95], ["Pick",29.95], ["Axe",19.95],["Shovel",49.95]]
};
function SetDescriptionEntries(){
if(event.willCommit){
    var cRowName = event.target.name.split(".").shift();
    var list = oArray[event.value];
    if( (list != null) && (list.length > 0) ){
    this.getField(cRowName + ".Description").setItems(list);
    }
    else{
    this.getField(cRowName + ".Description").clearItems();
    }
    this.getField(cRowName + ".Each").value = 0;
  }
 }
Note

If for some reason on another page you need to add a separate set of dropdown menus, you must create another document JavaScript like addItems2 and then change the associative syntax/object’s name and the function’s name to avoid a clash.

In the previous single dropdown example, you had to take the data from the item in the dropdown and copy it to other text fields. This time you have to copy the subitems to another dropdown menu called description. Again, if no value is found in the name dropdown, the description value remains blank and no choice is available. In this example, for array (oArray), the associative syntax is divided into four item names:

  • Drills

  • Lubricants

  • Gears

  • Mining

Each part of the associative syntax name divides into another dropdown for a subitem description and a text field that contains the price/each item. You will see shortly how this function to separate the price is called upon.

Then you need to add SetEachValue in the document JavaScript:

function SetEachValue(){
    if(!event.willCommit){
    var cRowName = event.target.name.split(".").shift();
    var nSelection = 0;
    if(!isNaN(event.changeEx))
    nSelection = event.changeEx;
    this.getField(cRowName + ".Each").value = nSelection;
    }
}

The fields have like Row1.Item, Row2.item, and so on. Using this type of order or schema allows the document JavaScript to easily call each item through the function. Because this type of form is so complex, using ordered and meaningful names is crucial.

If the event will not commit, this refers to the exclamation point (!) override, then the value is 0. This takes care of any missing numbers in the object array. If a non-number (NaN) like a word was entered instead, the value will remain 0. Otherwise, the function SetDescriptionEnteries will enter the value found in the associative syntax for price/each item.

This is the setting for the price of each item that was chosen from the description dropdown menu . The description and price are split here into their respective fields using the split method. You can see this because of the methods called .split() and .shift() . The shift method removes the first item in the associative example ["Oil",69.95] and leaves only the price that will now go into the price of each item field.

There are other methods that you can use to add or remove elements or object literals from the associative syntax. Here are a few explained for your reference:

  • pop(): Removes the last element from the array and returns the remaining elements. Example: ["Oil",69.95] = Oil

  • push(): Adds a new element to the end of an array and gives a new length count

  • unshift(): Adds new elements to the beginning an array and gives a new length count

  • splice(): Adds/removes elements from an array anywhere

For more information on methods and what else they can do, visit these links:

I created the function SetEachValue here in the Document JavaScript because I will be using it multiple times and I want to be able to call on it as many times as I like depending on how many dropdowns I have. Also, I do not have to rewrite this code for each field, which could lead to errors.

The last function is used to calculate the item total ( calculateRowTotal) of the row. This is not the Subtotal or Final Total fields, which can only be found out when you have finished adding up multiple items. Refer to the Parts Order Form End PDF.

// Global Document calculateRowTotal
function CalculateRowTotal()
{
    var cRowName = event.target.name.split(".").shift();
    event.value = this.getField(cRowName + ".Each").value * this.getField(cRowName + ".Qty").value;
}

This function once again uses the shift method . However, only the price is calculated against the quantity field so that the Item Row Total price is now calculated.

Like the SetEachValue() function, this will be used multiple times so you create it the Document JavaScripts tool. Whenever you have a function that you think you will use many times, it’s good to set it up in the Document JavaScripts tool.

Now you will take a look at the first row of your parts order form to see where it all fits. You will skip the Label field since it is not part of the project.

The first dropdown, Row1.item, is set up like Figure 12-12 so that it can reference the associative syntax/object called oArray. In the Options tab, you do not put anything in the value Export Value; this needs to remain blank.

A449379_1_En_12_Fig12_HTML.jpg
Figure 12-12. Dropdown properties in the Options tab in the first dropdown menu

In the Format tab’s Custom field , you call upon the SetDescriptionEntries function, which is found in the Document JavaScript addItems. This references the associative syntax oArray and lets the next dropdown fields of Row1.Description and Row1.each fill with information. Refer to Figure 12-13.

A449379_1_En_12_Fig13_HTML.jpg
Figure 12-13. The custom keystroke script in the first dropdown menu
// Format Custom Keystroke for Name
SetDescriptionEntries();

For all the other item dropdowns, you repeat these steps in the Option and Format tabs. To see how you use this same function in the other dropdown fields, refer to Figure 12-14.

A449379_1_En_12_Fig14_HTML.jpg
Figure 12-14. Dropdown fields requiring same options and format settings

The description dropdown Row1.Description is left blank like this and you do not preset in its Options tab. JavaScript will do that work for you. Refer to Figure 12-15.

A449379_1_En_12_Fig15_HTML.jpg
Figure 12-15. Dropdown properties in the Options tab in the second dropdown menu

The SetEachValue function is now called upon in the Format tab’s Custom field to get the value into the price of each field and can be added to the other description fields. See Figure 12-17. Remember, if the price was left out of the associative syntax or was text, the price will come in as 0 or $0.00. Refer to Figure 12-16.

A449379_1_En_12_Fig16_HTML.jpg
Figure 12-16. The custom keystroke script for the second dropdown menu
A449379_1_En_12_Fig17_HTML.jpg
Figure 12-17. Dropdown fields requiring same options and format settings
// Format Custom Keystroke for Description
SetEachValue();

This code is entered into each description drop-down menu. Refer to Figure 12-17.

The price of each field Row1.Each has no JavaScript in it. Its settings come from the SetEachValue function in the previous dropdown menu. However, it has been formatted to Number with a currency symbol . Refer to Figure 12-18.

A449379_1_En_12_Fig18_HTML.jpg
Figure 12-18. The text field properties in the Format tab are set to Number

The same is true of the quantity column , Row1.Qty. It contains no JavaScript. It just needs to be formatted to Number with no currency symbol and the decimal place set to 0. This field will be referenced shortly. Refer to Figure 12-19.

A449379_1_En_12_Fig19_HTML.jpg
Figure 12-19. The text field properties in the Format tab are set to Number without a currency symbol

The final area, Item Total (formatted to Number with currency symbol), now multiplies the information from the Price of Each and Quantity fields. Again, the calculation function was reference from the document JavaScript section and can be applied to multiple fields. Refer to Figure 12-20.

A449379_1_En_12_Fig20_HTML.jpg
Figure 12-20. Text field properties in the Calculate tab’s custom calculation script
// Calculate Script Items
CalculateRowTotal();

Set the itemTotal fields to read-only in the General tab.

Now make sure that your fields, SubTotal, Discount, Tax, Shipping and Total, are all formatted to Number with a currency symbol. SubTotal and Total should be set to read-only in the General tab so that the client does not overwrite a number. Refer to Figure 12-21.

A449379_1_En_12_Fig21_HTML.jpg
Figure 12-21. Final text fields that need to be calculated in the project

In past Invoice forms, you used Value or Simplified Field Notation. Here you might be tempted to use these options to add up your subtotal and final total. Refer here to Figure 12-22.

A449379_1_En_12_Fig22_HTML.jpg
Figure 12-22. Some field calculations do not require JavaScript

While the above examples do work, I found them to be a bit buggy and they did not refresh well. We don’t want users of our form to get frustrated and confused as to why their inputs are not working smoothly. In the end, what I discovered was that you need to add or subtract everything with JavaScript and then the form will run smoothly. In the Subtotal field , I placed the following script into the Calculate tab’s Custom Calculation Script field. Refer to Figure 12-23.

A449379_1_En_12_Fig23_HTML.jpg
Figure 12-23. Text field properties in the Calculate tab for the SubTotal field
//Subtotal Script
var a = this.getField("Row1.itemTotal");
var b = this.getField("Row2.itemTotal");
var c = this.getField("Row3.itemTotal");
var d = this.getField("Row4.itemTotal");
var f = this.getField("Row5.itemTotal");
event.value =a.value + b.value + c.value + d.value + f.value;

As you can see, it is like an example you tried in earlier lessons such as Chapter 6. Refer to Figure 12-24.

A449379_1_En_12_Fig24_HTML.jpg
Figure 12-24. Text field properties in the Calculate tab for the Total field

Then for the Grand Total or Total Field, I used this script:

//Total Script
var g = this.getField("SubTotal");
var h = this.getField("Discount");
var j = this.getField("Tax");
var k = this.getField("Shipping");
event.value =g.value - h.value + j.value + k.value;

This worked the best for me for this example.

Final Thoughts

When making a complex form with multiple dropdowns, it’s best to plan it out on paper or a drawing program first and decide what you would like to do. You may need an associative syntax with object literals or a global function, but write it all out first.

Once you’ve built the form, similar to what I have done, make sure that number areas are formatted to Number correctly, and then add the JavaScript. Test the form or maybe get others to test it. It’s important to work out all the bugs before your customers use it. The focus here is to avoid client errors and frustration and to get the results you want when working with fields in a form.

Load a Lengthy Single Dropdown or List Menu

While not as complex as the examples above, occasionally you may need to load only a single dropdown menu with 30 or more items that don’t affect other fields or menus in the form. See the Load Single Menu folder.

Having to enter this into a dropdown or list menu can be labor intensive : you must select the Options tab, enter a name, enter a value, and then click the Add button for each item. Also, you may want the same information in multiple menus that change often (see the Time Sheet PDF example). To save time for lengthy single menus, I have included extra files where I have put all my code for the menu into the global Document JavaScript area. I call this function LoadOptions. You can view the code on the following pages and will find it in the supplied text file as well. As always, make sure that your fields have the same name as what is written in the JavaScript so that the connection will work correctly. Refer to Figure 12-25.

A449379_1_En_12_Fig25_HTML.jpg
Figure 12-25. The LoadOptions script

The following code is added to the Document JavaScripts tool :

// define array of entries and export values for dropdown array
// define array of states and abbreviations
var aMinerals = new Array(["Select Mineral", ""],
                    ["Diamond", "DI"],
                    ["Emerald", "EM"],
                    ["Garnet", "GA"],
                    ["Opal", "OP"],
                    ["Ruby", "RB"],
                    ["Sapphire", "SP"]);
// function to load a combo/list box with an array of values
function LoadOptions(oField, aValues) {
    var bResult = false;
// load array of values into field object
    if(oField.type != "combobox" && oField.type != "listbox") {
    app.alert("Load Options function requires a combobox or listbox", 0, 0);
    bResult = false;
}
    else{
    oField.setItems(aValues); // set values
    bResult = true;
}
return bResult;
} // end LoadOptions
// load the data - comment out after updating when only doing array changes
LoadOptions(this.getField('Dropdown1'), aMinerals);
LoadOptions(this.getField('ListBox1'), aMinerals);

The above example works for dropdown or list box menus. If you are not using a list box, you can comment that line out or add more LoadOptions if other dropdowns require the same object literals that may change often. Remember to give all dropdown fields a distinct name.

Summary

In this chapter, while working with dropdown menus and text boxes , you encountered some complex JavaScript. You learned how nested associative/object literals work and how you can extract information from those associative syntax functions. You also saw the benefit of document JavaScript and how writing the script in one location saved you time so you didn’t have to write multiple edits in several locations, only reference the script or other fields.

In the next chapter, you are going to take a more detailed look at probably one of the most underused fields: the list box. You've just seen as with dropdown menus that you can load text into them using document JavaScript. However, they're not as compact as a dropdown, so what else can you do with them?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset