×

Plug-In Forms: Examples

Well-designed Omni Automation actions often prompt and query the user to provide data, or make a selection, choice, or decision as to how the script should proceed. The Form class enables scripts to gather information from the user, by presenting dialogs containing data expressed using standard interface elements: text input fields, date pickers, option menus, and checkboxes.

The following example forms address common scripting scenarios and can be easily added to your Omni Automaton Plug-In Templates and customized to suit the individual script requirements.

NOTE: This page contains interactive script examples that are executed only with your approval. See the section on Script Security for details about allowing the execution of external scripts, like those included in this website.

TOPICS: Text InputOptions Menu/ListDate Input

 

Text Input

Plug-in Forms using instances of the Form.Field.String class (text input fields). (documentation)

Basic Text InputURL InputEnter OmniFocus TagText with ApprovalMultiple Text InputsText Input is IntegerInput is Integer within RangeAlpha-Numeric InputValid eMail Address

 

Basic Text Input

In this form example, the user is prompted to provide text input. The form’s validation function ensures that the approval button will not be enabled without the user first providing such input.

single-text-input
var textInputField = new Form.Field.String( "textInput", "Field Label", null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Form prompt:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] return ((!inputText)?false:true) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log('textValue: ',textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Single Text Input
  

var textInputField = new Form.Field.String( "textInput", "Field Label", null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Form prompt:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] return ((!inputText)?false:true) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log('textValue: ',textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

URL Input Form

This form presents a single text input field that accepts a text string that can be made into a valid URL object:

url-input-dialog
// CREATE FORM FOR GATHERING USER INPUT var inputForm = new Form() // CREATE TEXT FIELD var textField = new Form.Field.String( "textInput", null, null ) // ADD THE FIELDS TO THE FORM inputForm.addField(textField) // PRESENT THE FORM TO THE USER var formPrompt = "Enter the full URL:" var formPromise = inputForm.show(formPrompt,"Continue") // VALIDATE THE USER INPUT inputForm.validate = function(formObject){ var textValue = formObject.values["textInput"] var textStatus = (textValue && textValue.length > 0) ? true:false var schema = [ "http://", "https://", "mailto:", "omnioutliner://", "omniplan://", "omnigraffle://", "omnifocus://" ] if (textStatus){ var i; for (i = 0; i < schema.length; i++) { if (textValue.startsWith(schema[i], 0) && URL.fromString(textValue)){return true} } } return false } // PROCESSING USING THE DATA EXTRACTED FROM THE FORM formPromise.then(function(formObject){ var textValue = formObject.values["textInput"] var url = URL.fromString(textValue) console.log(url) }) // PROMISE FUNCTION CALLED UPON FORM CANCELLATION formPromise.catch(function(err){ console.log("form cancelled", err.message) })
URL Input
  

// CREATE FORM FOR GATHERING USER INPUT var inputForm = new Form() // CREATE TEXT FIELD var textField = new Form.Field.String( "textInput", null, null ) // ADD THE FIELDS TO THE FORM inputForm.addField(textField) // PRESENT THE FORM TO THE USER var formPrompt = "Enter the full URL:" var formPromise = inputForm.show(formPrompt,"Continue") // VALIDATE THE USER INPUT inputForm.validate = function(formObject){ var textValue = formObject.values["textInput"] var textStatus = (textValue && textValue.length > 0) ? true:false var schema = [ "http://", "https://", "mailto:", "omnioutliner://", "omniplan://", "omnigraffle://", "omnifocus://" ] if (textStatus){ var i; for (i = 0; i < schema.length; i++) { if (textValue.startsWith(schema[i], 0) && URL.fromString(textValue)){return true} } } return false } // PROCESSING USING THE DATA EXTRACTED FROM THE FORM formPromise.then(function(formObject){ var textValue = formObject.values["textInput"] var url = URL.fromString(textValue) // PROCESSING STATEMENTS GO HERE }) // PROMISE FUNCTION CALLED UPON FORM CANCELLATION formPromise.catch(function(err){ console.log("form cancelled", err.message) })
 

Enter an Existing OmniFocus Tag

Here’s an example form containing a single text input field in which the user enters the title of an existing tag.

enter-existing-of-tag
var tagNames = flattenedTags.map(tag => tag.name) if (tagNames.length === 0){throw Error("No tags in database")} var textInputField = new Form.Field.String( "textInput", "Tag", null ) var checkSwitchField = new Form.Field.Checkbox( "checkboxSwitch", "Create tag if not existing", false ) var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(checkSwitchField) var formPrompt = "Enter the title of an existing tag:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText = formObject.values['textInput'] var shouldCreateTag = formObject.values['checkboxSwitch'] if(!inputText){return false} if(shouldCreateTag){return true} return (tagNames.includes(inputText)) } formPromise.then(function(formObject){ var tagTitle = formObject.values['textInput'] var shouldCreateTag = formObject.values['checkboxSwitch'] var targetTag = null tags.apply(tag => { if(tag.name === tagTitle){ targetTag = tag return ApplyResult.Stop } }) if(!targetTag && shouldCreateTag){var targetTag = new Tag(tagTitle)} console.log('tag:',targetTag) // PROCESS STATEMENTS }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
OmniFocus: Enter Name of Existing Tag
  

var tagNames = flattenedTags.map(tag => tag.name) if (tagNames.length === 0){throw Error("No tags in database")} var textInputField = new Form.Field.String( "textInput", "Tag", null ) var checkSwitchField = new Form.Field.Checkbox( "checkboxSwitch", "Create tag if not existing", false ) var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(checkSwitchField) var formPrompt = "Enter the title of an existing tag:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText = formObject.values['textInput'] var shouldCreateTag = formObject.values['checkboxSwitch'] if(!inputText){return false} if(shouldCreateTag){return true} return (tagNames.includes(inputText)) } formPromise.then(function(formObject){ var tagTitle = formObject.values['textInput'] var shouldCreateTag = formObject.values['checkboxSwitch'] var targetTag = null tags.apply(tag => { if(tag.name === tagTitle){ targetTag = tag return ApplyResult.Stop } }) if(!targetTag && shouldCreateTag){var targetTag = new Tag(tagTitle)} console.log('tag:',targetTag) // PROCESS STATEMENTS }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Text Input with Mandatory Checkbox

In this example form, the dialog cannot be approved without the user providing text and selecting the provided checkbox.

text-input-with-mandatory-checkbox
var textInputField = new Form.Field.String( "textInput", "Field Label", null ) var checkSwitchField = new Form.Field.Checkbox( "checkboxSwitch", "I accept the standard terms and conditions", null ) var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(checkSwitchField) var formPrompt = "Form prompt:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText = formObject.values['textInput'] var textStatus = ((!inputText)?false:true) var checkboxStatus = formObject.values['checkboxSwitch'] return (textStatus && checkboxStatus) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log('textValue: ',textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Text Input with Mandatory Checkbox
  

var textInputField = new Form.Field.String( "textInput", "Field Label", null ) var checkSwitchField = new Form.Field.Checkbox( "checkboxSwitch", "I accept the standard terms and conditions", null ) var inputForm = new Form() inputForm.addField(textInputField) inputForm.addField(checkSwitchField) var formPrompt = "Form prompt:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText = formObject.values['textInput'] var textStatus = ((!inputText)?false:true) var checkboxStatus = formObject.values['checkboxSwitch'] return (textStatus && checkboxStatus) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log('textValue: ',textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Multiple Mandatory Text Inputs

In this example, the user must supply textual input in all of the provided fields before the approval button will be enabled. NOTE: to change a text field to “optional input” simply remove its status variable from the validation statement on line 38.

multiple-text-input
var textInputField01 = new Form.Field.String( "textInput01", "Field Label 1", null ) var textInputField02 = new Form.Field.String( "textInput02", "Field Label 2", null ) var textInputField03 = new Form.Field.String( "textInput03", "Field Label 3", null ) var inputForm = new Form() inputForm.addField(textInputField01) inputForm.addField(textInputField02) inputForm.addField(textInputField03) var formPrompt = "Form prompt:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText01 = formObject.values['textInput01'] var inputText01Status = (!inputText01)?false:true var inputText02 = formObject.values['textInput02'] var inputText02Status = (!inputText02)?false:true var inputText03 = formObject.values['textInput03'] var inputText03Status = (!inputText03)?false:true // ALL CONDITIONS MUST BE TRUE TO VALIDATE var validation = (inputText01Status && inputText02Status && inputText03Status) ? true:false return validation } formPromise.then(function(formObject){ var textValue01 = formObject.values['textInput01'] var textValue02 = formObject.values['textInput02'] var textValue03 = formObject.values['textInput03'] console.log('textInput01: ',textValue01) console.log('textInput02: ',textValue02) console.log('textInput03: ',textValue03) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Multiple Mandatory Text Inputs
  

var textInputField01 = new Form.Field.String( "textInput01", "Field Label 1", null ) var textInputField02 = new Form.Field.String( "textInput02", "Field Label 2", null ) var textInputField03 = new Form.Field.String( "textInput03", "Field Label 3", null ) var inputForm = new Form() inputForm.addField(textInputField01) inputForm.addField(textInputField02) inputForm.addField(textInputField03) var formPrompt = "Form prompt:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var inputText01 = formObject.values['textInput01'] var inputText01Status = (!inputText01)?false:true var inputText02 = formObject.values['textInput02'] var inputText02Status = (!inputText02)?false:true var inputText03 = formObject.values['textInput03'] var inputText03Status = (!inputText03)?false:true // ALL CONDITIONS MUST BE TRUE TO VALIDATE var validation = (inputText01Status && inputText02Status && inputText03Status) ? true:false return validation } formPromise.then(function(formObject){ var textValue01 = formObject.values['textInput01'] var textValue02 = formObject.values['textInput02'] var textValue03 = formObject.values['textInput03'] console.log('textInput01: ',textValue01) console.log('textInput02: ',textValue02) console.log('textInput03: ',textValue03) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Text Input is Integer

In this example, the user must enter an integer (whole number):

var textInputField = new Form.Field.String( "textInput", null, null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter a whole number:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] if (!inputText){return false} var isnum = /^[0-9]+$/i.test(inputText) return isnum } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] var intValue = parseInt(textValue) console.log('intValue:', intValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Text Input is Integer
  

var textInputField = new Form.Field.String( "textInput", null, null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter a whole number:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] if (!inputText){return false} var isnum = /^[0-9]+$/i.test(inputText) return isnum } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] var intValue = parseInt(textValue) console.log('intValue:',intValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Text Input is Integer within Range

In this example, the user must enter an integer (whole number) that falls between the specified range, before the approval button is enabled. You can set the range by changing the values of the first two statements of the form.

integer-input
var minValue = 1 var maxValue = 34 var textInputField = new Form.Field.String( "textInput", String("(" + minValue + "-" + maxValue + ")"), null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter a whole number:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] if (!inputText) {return false} var isnum = /^[0-9]+$/i.test(inputText) if (isnum){ var intValue = parseInt(inputText) return ((intValue <= maxValue && intValue >= minValue) ? true:false) } return false } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] var intValue = parseInt(textValue) console.log('intValue:', intValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Input is Integer within Range
  

var minValue = 1 var maxValue = 34 var textInputField = new Form.Field.String( "textInput", String("(" + minValue + "-" + maxValue + ")"), null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter a whole number:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ inputText = formObject.values['textInput'] if (!inputText) {return false} var isnum = /^[0-9]+$/i.test(inputText) if (isnum){ var intValue = parseInt(inputText) return ((intValue <= maxValue && intValue >= minValue) ? true:false) } return false } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] var intValue = parseInt(textValue) console.log('intValue:', intValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Text Input with Only Alpha-Numeric Characters

In this form example, the text input field will only accept alpha numeric characters (a-z A-Z and 0-9).

alpha-numeric-input
var textInputField = new Form.Field.String( "textInput", "(a-z, 0-9)", null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter an alpha-numeric string:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var textValue = formObject.values['textInput'] if(!textValue){return false} return (/^[a-z0-9]+$/i.test(textValue)) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log("textValue:",textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Require Alpha-Numeric Input
  

var textInputField = new Form.Field.String( "textInput", "(a-z, 0-9)", null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter an alpha-numeric string:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var textValue = formObject.values['textInput'] if(!textValue){return false} return (/^[a-z0-9]+$/i.test(textValue)) } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log("textValue:",textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Enter Valid eMail Address

In this form example, the approval button will only become enabled when a fully-formed email address is entered in the text input field. For example: bob123@omniapps.uk or carla486@outlinerworld.com

email-address-input
var textInputField = new Form.Field.String( "textInput", null, null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter an eMail address:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var textValue = formObject.values['textInput'] if(!textValue){return false} if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(textValue)){ return true } else { throw "ERROR: not a valid eMail address." } } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log("textValue: ",textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Enter Valid eMail Address
  

var textInputField = new Form.Field.String( "textInput", null, null ) var inputForm = new Form() inputForm.addField(textInputField) var formPrompt = "Enter an eMail address:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var textValue = formObject.values['textInput'] if(!textValue){return false} if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(textValue)){ return true } else { throw "ERROR: not a valid eMail address." } } formPromise.then(function(formObject){ var textValue = formObject.values['textInput'] console.log("textValue: ",textValue) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Options Menu/List

Plug-in Forms using instances of the Form.Field.Option class (option menus or lists). (documentation)

Basic Options Menu/List(OmniFocus) Projects with Tag(OmniFocus) Perspectives MenuSelect Date (Follow-On Menus)Filtered Menu Form

 

Basic Options Menu

The following form will display a single options menu:

basic-options-form
var menuItems = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"] var menuIndexes = [0,1,2,3,4,5,6] var menuElement = new Form.Field.Option( "menuElement", null, menuIndexes, menuItems, 0 ) var inputForm = new Form() inputForm.addField(menuElement) var formPrompt = "Choose one of the items:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var menuIndex = formObject.values['menuElement'] var chosenItem = menuItems[menuIndex] console.log('Chosen item:', chosenItem) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Basic Options Menu/List
  

var menuItems = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"] var menuIndexes = [0,1,2,3,4,5,6] var menuElement = new Form.Field.Option( "menuElement", null, menuIndexes, menuItems, 0 ) var inputForm = new Form() inputForm.addField(menuElement) var formPrompt = "Choose one of the items:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var menuIndex = formObject.values['menuElement'] var chosenItem = menuItems[menuIndex] console.log('Chosen item:', chosenItem) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

(OmniFocus) Menu/List of Projects with Specific Tag

In the following form example, a selection menu is displayed that lists projects that are tagged with a pre-specified tag.

projects-with-tag-menu

To use the form, change the name of the target tag from “Camera-Ready” to the title of the tag you wish to specify:

try { var targetTagName = "Camera-Ready" var targetTag = null tags.apply(function(tag){ if(tag.name == targetTagName){ targetTag = tag return ApplyResult.Stop } }) if (!targetTag){ var errMsg1 = `There is no tag titled: ”${targetTagName}”` throw new Error(errMsg1) } var projectsWithTag = targetTag.projects if (projectsWithTag.length === 0){ var errMsg2 = `No projects are tagged with: ”${targetTagName}”` throw new Error(errMsg2) } projectsWithTag.sort((a, b) => { var x = a.name.toLowerCase() var y = b.name.toLowerCase() if (x < y) {return -1} if (x > y) {return 1} return 0 }) var projectNames = projectsWithTag.map(project => project.name) var menuIndexes = projectNames.map((name, index) => index) var projectsMenu = new Form.Field.Option( "projectMenu", null, menuIndexes, projectNames, 0 ) var inputForm = new Form() inputForm.addField(projectsMenu) var formPrompt = `Choose a “${targetTagName}” project:` var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var projectIndex = formObject.values['projectMenu'] var chosenProject = projectsWithTag[projectIndex] console.log(chosenProject) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) }) } catch(err){ new Alert("ERROR", err.message).show() }
(OmniFocus) Projects with Specified Tag
  

try { var targetTagName = "Camera-Ready" var targetTag = null tags.apply(function(tag){ if(tag.name == targetTagName){ targetTag = tag return ApplyResult.Stop } }) if (!targetTag){ var errMsg1 = `There is no tag titled: ”${targetTagName}”` throw new Error(errMsg1) } var projectsWithTag = targetTag.projects if (projectsWithTag.length === 0){ var errMsg2 = `No projects are tagged with: ”${targetTagName}”` throw new Error(errMsg2) } projectsWithTag.sort((a, b) => { var x = a.name.toLowerCase() var y = b.name.toLowerCase() if (x < y) {return -1} if (x > y) {return 1} return 0 }) var projectNames = projectsWithTag.map(project => project.name) var menuIndexes = projectNames.map((name, index) => index) var projectsMenu = new Form.Field.Option( "projectMenu", null, menuIndexes, projectNames, 0 ) var inputForm = new Form() inputForm.addField(projectsMenu) var formPrompt = `Choose a “${targetTagName}” project:` var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var projectIndex = formObject.values['projectMenu'] var chosenProject = projectsWithTag[projectIndex] console.log(chosenProject) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) }) } catch(err){ new Alert("ERROR", err.message).show() }
 

(OmniFocus) Menu of Perspectives

This plug-in will display a form containing a list of the built-in perspectives from which the user selects the one to be displayed.

perspectives-menu

This form example demonstrates the use of any array of objects rather than an array of strings (names) as the source for the menu/list items. The result of the user’s selection is an object.

NOTE: Since the function code changes the current view of the OmniFocus window, an instance of the Timer class (lines 24-26) is used to delay the code processing until the sheet (macOS) or dialog (iPadOS, iOS) are fully dismissed.

var perspectives = Perspective.BuiltIn.all.concat(Perspective.Custom.all) var perspectiveMenu = new Form.Field.Option( "perspective", "Perspective", perspectives, null, perspectives[0] ) var inputForm = new Form() inputForm.addField(perspectiveMenu) var formPrompt = "Choose the perspective:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var chosenPerspective = formObject.values['perspective'] console.log(chosenPerspective) Timer.once(1,function(timer){ document.windows[0].perspective = chosenPerspective }) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
(OmniFocus) Show Chosen Perspective
  

var perspectives = Perspective.BuiltIn.all.concat(Perspective.Custom.all) var perspectiveMenu = new Form.Field.Option( "perspective", "Perspective", perspectives, null, perspectives[0] ) var inputForm = new Form() inputForm.addField(perspectiveMenu) var formPrompt = "Choose the perspective:" var buttonTitle = "Continue" formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var chosenPerspective = formObject.values['perspective'] Timer.once(1,function(timer){ document.windows[0].perspective = chosenPerspective }) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Select a Date (follow-on menus)

In this example of date input alternatives, the user can choose any date by selecting from a series of of dynamic follow-on menus.

A “follow-on” menu or list is one whose content changes based upon the selection of a previous menu. In this example form, the “Day” menu is a follow-on menu to the “Month” menu. See the Form Validation section further explanation about follow-on menus.

(⬇ see below ) The form on macOS:

select-date-from-menus
var locale = new Intl.Collator().resolvedOptions().locale.toLowerCase() var monthNames = [] for (i = 0; i < 12; i++) { var objDate = new Date() objDate.setDate(1) objDate.setMonth(i) monthName = objDate.toLocaleString(locale,{month:"long"}) monthNames.push(monthName) } var now = new Date() var currentYear = now.getFullYear() var yearNames = new Array() var yearIndexes = new Array() for (i = 0; i < 4; i++) { yearNames.push(String(currentYear + i)) yearIndexes.push(i) } var dayCount = new Date(currentYear, 1, 0).getDate() var dayIndexes = new Array() var dayIndexStrings = new Array() for (var i = 0; i < dayCount; i++){ dayIndexes.push(i) dayIndexStrings.push(String(i + 1)) } var inputForm = new Form() var yearMenu = new Form.Field.Option( "yearMenu", "Year", yearIndexes, yearNames, 0 ) var currentYearIndex = 0 var monthMenu = new Form.Field.Option( "monthMenu", "Month", [0,1,2,3,4,5,6,7,8,9,10,11], monthNames, 0 ) var currentMonthIndex = 0 var dayMenu = new Form.Field.Option( "dayMenu", "Day", dayIndexes, dayIndexStrings, 0 ) inputForm.addField(yearMenu) inputForm.addField(monthMenu) inputForm.addField(dayMenu) var formPrompt = "Select the date:" var buttonTitle = "OK" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ var yearMenuIndex = formObject.values["yearMenu"] var chosenYear = parseInt(yearNames[yearMenuIndex]) var monthMenuIndex = formObject.values["monthMenu"] if(monthMenuIndex != currentMonthIndex || yearMenuIndex != currentYearIndex){ inputForm.removeField(inputForm.fields[2]) currentMonthIndex = monthMenuIndex currentYearIndex = yearMenuIndex } if (formObject.fields.length == 2){ var dayCount = new Date(chosenYear, monthMenuIndex + 1, 0).getDate() var dayIndexes = new Array() var dayIndexStrings = new Array() for (var i = 0; i < dayCount; i++){ dayIndexes.push(i) dayIndexStrings.push(String(i + 1)) } var dayMenu = new Form.Field.Option( "dayMenu", "Day", dayIndexes, dayIndexStrings, 0 ) inputForm.addField(dayMenu) } return true } formPromise.then(function(formObject){ var yearMenuIndex = formObject.values["yearMenu"] var yearName = yearNames[yearMenuIndex] var monthMenuIndex = formObject.values["monthMenu"] var monthName = monthNames[monthMenuIndex] var dayMenuIndex = formObject.values["dayMenu"] var dayIndexString = dayIndexStrings[dayMenuIndex] var targetDate = new Date(monthName + " " + dayIndexString + " " + yearName) console.log(targetDate) }) formPromise.catch(function(error){ console.log("form cancelled", error.message) })
Select a Date (menus)
  

var locale = new Intl.Collator().resolvedOptions().locale.toLowerCase() var monthNames = [] for (i = 0; i < 12; i++) { var objDate = new Date() objDate.setDate(1) objDate.setMonth(i) monthName = objDate.toLocaleString(locale,{month:"long"}) monthNames.push(monthName) } var now = new Date() var currentYear = now.getFullYear() var yearNames = new Array() var yearIndexes = new Array() for (i = 0; i < 4; i++) { yearNames.push(String(currentYear + i)) yearIndexes.push(i) } var dayCount = new Date(currentYear, 1, 0).getDate() var dayIndexes = new Array() var dayIndexStrings = new Array() for (var i = 0; i < dayCount; i++){ dayIndexes.push(i) dayIndexStrings.push(String(i + 1)) } var inputForm = new Form() var yearMenu = new Form.Field.Option( "yearMenu", "Year", yearIndexes, yearNames, 0 ) var currentYearIndex = 0 var monthMenu = new Form.Field.Option( "monthMenu", "Month", [0,1,2,3,4,5,6,7,8,9,10,11], monthNames, 0 ) var currentMonthIndex = 0 var dayMenu = new Form.Field.Option( "dayMenu", "Day", dayIndexes, dayIndexStrings, 0 ) inputForm.addField(yearMenu) inputForm.addField(monthMenu) inputForm.addField(dayMenu) var formPrompt = "Select the date:" var buttonTitle = "OK" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ var yearMenuIndex = formObject.values["yearMenu"] var chosenYear = parseInt(yearNames[yearMenuIndex]) var monthMenuIndex = formObject.values["monthMenu"] if(monthMenuIndex != currentMonthIndex || yearMenuIndex != currentYearIndex){ inputForm.removeField(inputForm.fields[2]) currentMonthIndex = monthMenuIndex currentYearIndex = yearMenuIndex } if (formObject.fields.length == 2){ var dayCount = new Date(chosenYear, monthMenuIndex + 1, 0).getDate() var dayIndexes = new Array() var dayIndexStrings = new Array() for (var i = 0; i < dayCount; i++){ dayIndexes.push(i) dayIndexStrings.push(String(i + 1)) } var dayMenu = new Form.Field.Option( "dayMenu", "Day", dayIndexes, dayIndexStrings, 0 ) inputForm.addField(dayMenu) } return true } formPromise.then(function(formObject){ var yearMenuIndex = formObject.values["yearMenu"] var yearName = yearNames[yearMenuIndex] var monthMenuIndex = formObject.values["monthMenu"] var monthName = monthNames[monthMenuIndex] var dayMenuIndex = formObject.values["dayMenu"] var dayIndexString = dayIndexStrings[dayMenuIndex] var targetDate = new Date(monthName + " " + dayIndexString + " " + yearName) console.log(targetDate) }) formPromise.catch(function(error){ console.log("form cancelled", error.message) })

(⬇ see below ) The form on iPadOS:

choose-date-from-menus

Appointment Time Form

Here is an example form for choosing the next upcoming workday/time slot:

appointment interface appointment confirmation
var menu1Items = ["Monday","Tuesday","Wednesday","Thursday","Friday"] var menu1Indexes = new Array() menu1Items.forEach((item, index) => menu1Indexes.push(index)) var menu2Items = ["9:00 AM", "9:15 AM", "9:30 AM", "9:45 AM", "10:00 AM", "10:15 AM", "10:30 AM", "10:45 AM", "11:00 AM", "11:15 AM", "11:30 AM", "11:45 AM", "12:00 PM", "12:15 PM", "12:30 PM", "12:45 PM", "1:00 PM", "1:15 PM", "1:30 PM", "1:45 PM", "2:00 PM", "2:15 PM", "2:30 PM", "2:45 PM", "3:00 PM", "3:15 PM", "3:30 PM", "3:45 PM", "4:00 PM", "4:15 PM", "4:30 PM", "4:45 PM"] var menu2Indexes = new Array() menu2Items.forEach((item, index) => menu2Indexes.push(index)) var menu1Element = new Form.Field.Option( "menu1Element", null, menu1Indexes, menu1Items, 0 ) var menu2Element = new Form.Field.Option( "menu2Element", null, menu2Indexes, menu2Items, 0 ) var inputForm = new Form() inputForm.addField(menu1Element) inputForm.addField(menu2Element) var formPrompt = "Select a day and time:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var menu1Index = formObject.values['menu1Element'] var chosen1Item = menu1Items[menu1Index] var menu2Index = formObject.values['menu2Element'] var chosen2Item = menu2Items[menu2Index] chosen2Item = chosen2Item.replace(' ', '') var fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Full, Formatter.Date.Style.Short) var dateObj = fmatr.dateFromString(`${chosen1Item} @ ${chosen2Item}`) var dateStr = fmatr.stringFromDate(dateObj) // CONFIRMATION ALERT var alertTitle = "Form Result" var alertMessage = "Your appointment is for:\n\n" + dateStr new Alert(alertTitle, alertMessage).show() }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
Select Next Upcoming Appointment Time
  

var menu1Items = ["Monday","Tuesday","Wednesday","Thursday","Friday"] var menu1Indexes = new Array() menu1Items.forEach((item, index) => menu1Indexes.push(index)) var menu2Items = ["9:00 AM", "9:15 AM", "9:30 AM", "9:45 AM", "10:00 AM", "10:15 AM", "10:30 AM", "10:45 AM", "11:00 AM", "11:15 AM", "11:30 AM", "11:45 AM", "12:00 PM", "12:15 PM", "12:30 PM", "12:45 PM", "1:00 PM", "1:15 PM", "1:30 PM", "1:45 PM", "2:00 PM", "2:15 PM", "2:30 PM", "2:45 PM", "3:00 PM", "3:15 PM", "3:30 PM", "3:45 PM", "4:00 PM", "4:15 PM", "4:30 PM", "4:45 PM"] var menu2Indexes = new Array() menu2Items.forEach((item, index) => menu2Indexes.push(index)) var menu1Element = new Form.Field.Option( "menu1Element", null, menu1Indexes, menu1Items, 0 ) var menu2Element = new Form.Field.Option( "menu2Element", null, menu2Indexes, menu2Items, 0 ) var inputForm = new Form() inputForm.addField(menu1Element) inputForm.addField(menu2Element) var formPrompt = "Select a day and time:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ return true } formPromise.then(function(formObject){ var menu1Index = formObject.values['menu1Element'] var chosen1Item = menu1Items[menu1Index] var menu2Index = formObject.values['menu2Element'] var chosen2Item = menu2Items[menu2Index] chosen2Item = chosen2Item.replace(' ', '') var fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Full, Formatter.Date.Style.Short) var dateObj = fmatr.dateFromString(`${chosen1Item} @ ${chosen2Item}`) var dateStr = fmatr.stringFromDate(dateObj) // CONFIRMATION ALERT var alertTitle = "Form Result" var alertMessage = "Your appointment is for:\n\n" + dateStr new Alert(alertTitle, alertMessage).show() }) formPromise.catch(function(err){ console.error("form cancelled", err.message) })
 

Filtered Menu

In this form, the contents of the menu is determined by filtering using the entered text. Designed for use in OmniFocus, the form filters existing projects using the entered string, and presents the results in a menu:

Filtered Menu
// CREATE FORM FOR GATHERING USER INPUT var inputForm = new Form() // CREATE TEXT FIELD var textField = new Form.Field.String( "textInput", "Search", null ) // CREATE MENU var popupMenu = new Form.Field.Option( "menuItem", "Results", [], [], null ) popupMenu.allowsNull = true popupMenu.nullOptionTitle = "0 items" // ADD THE FIELDS TO THE FORM inputForm.addField(textField) inputForm.addField(popupMenu) // PRESENT THE FORM TO THE USER var currentValue = null formPrompt = "Enter a project title:" formPromise = inputForm.show(formPrompt,"Continue") // VALIDATE THE USER INPUT inputForm.validate = function(formObject){ var textInput = formObject.values["textInput"] if(textInput !== currentValue){ currentValue = textInput // remove popup menu if (inputForm.fields.length === 2){ inputForm.removeField(inputForm.fields[1]) } } if(inputForm.fields.length === 1){ // search using provided string if (!textInput){var searchResults = []} else { var searchResults = projectsMatching(textInput) } var searchResultNames = searchResults.map(item => item.name) var searchResultIDs = searchResults.map(item => item.id.primaryKey) var popupMenu = new Form.Field.Option( "menuItem", "Results", searchResultIDs, searchResultNames, null ) popupMenu.allowsNull = true popupMenu.nullOptionTitle = String(searchResults.length + " items") inputForm.addField(popupMenu) return false } if(!textInput){return false} if(inputForm.fields.length === 2){ menuValue = formObject.values["menuItem"] if(menuValue === undefined || String(menuValue) === "null"){return false} return true } } // PROCESSING USING THE DATA EXTRACTED FROM THE FORM formPromise.then(function(formObject){ let projectID = formObject.values["menuItem"] let projectObj = Project.byIdentifier(projectID) console.log(projectObj) })
Filtered Menu Form (OmniFocus)
  

// CREATE FORM FOR GATHERING USER INPUT var inputForm = new Form() // CREATE TEXT FIELD var textField = new Form.Field.String( "textInput", "Search", null ) // CREATE MENU var popupMenu = new Form.Field.Option( "menuItem", "Results", [], [], null ) popupMenu.allowsNull = true popupMenu.nullOptionTitle = "0 items" // ADD THE FIELDS TO THE FORM inputForm.addField(textField) inputForm.addField(popupMenu) // PRESENT THE FORM TO THE USER var currentValue = null formPrompt = "Enter a project title:" formPromise = inputForm.show(formPrompt,"Continue") // VALIDATE THE USER INPUT inputForm.validate = function(formObject){ var textInput = formObject.values["textInput"] if(textInput !== currentValue){ currentValue = textInput // remove popup menu if (inputForm.fields.length === 2){ inputForm.removeField(inputForm.fields[1]) } } if(inputForm.fields.length === 1){ // search using provided string if (!textInput){var searchResults = []} else { var searchResults = projectsMatching(textInput) } var searchResultNames = searchResults.map(item => item.name) var searchResultIDs = searchResults.map(item => item.id.primaryKey) var popupMenu = new Form.Field.Option( "menuItem", "Results", searchResultIDs, searchResultNames, null ) popupMenu.allowsNull = true popupMenu.nullOptionTitle = String(searchResults.length + " items") inputForm.addField(popupMenu) return false } if(!textInput){return false} if(inputForm.fields.length === 2){ menuValue = formObject.values["menuItem"] if(menuValue === undefined || String(menuValue) === "null"){return false} return true } } // PROCESSING USING THE DATA EXTRACTED FROM THE FORM formPromise.then(function(formObject){ let projectID = formObject.values["menuItem"] let projectObj = Project.byIdentifier(projectID) console.log(projectObj) })
 

Date Input

Plug-in Forms using instances of the Form.Field.Date class. (Documentation)

Date after TodayStart End Dates

 

Enter Date after Today

In the following example form, the user is prompted to provide a date after today.

date-after-today

NOTE: Date fields will accept a variety of shortcut values. Reference the page on Date Input Fields to learn all of the available date referencing options.

var today = new Date(new Date().setHours(0,0,0,0)) var tomorrow = new Date(today.setDate(today.getDate() + 1)) var dateInputField = new Form.Field.Date( "dateInput", null, null ) var inputForm = new Form() inputForm.addField(dateInputField) var formPrompt = "Enter a date/time after today:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ dateInput = formObject.values["dateInput"] return (dateInput && dateInput >= tomorrow) } formPromise.then(function(formObject){ dateInput = formObject.values["dateInput"] console.log(dateInput) }) formPromise.catch(function(error){ console.log("form cancelled", error.message) })
Date After Today
  

var today = new Date(new Date().setHours(0,0,0,0)) var tomorrow = new Date(today.setDate(today.getDate() + 1)) var dateInputField = new Form.Field.Date( "dateInput", null, null ) var inputForm = new Form() inputForm.addField(dateInputField) var formPrompt = "Enter a date/time after today:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ dateInput = formObject.values["dateInput"] return (dateInput && dateInput >= tomorrow) } formPromise.then(function(formObject){ dateInput = formObject.values["dateInput"] console.log(dateInput) }) formPromise.catch(function(error){ console.log("form cancelled", error.message) })
 

Start and End Dates

The following example presents two date input fields for entering start and end dates:

select-date-from-menus
var fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Short) var currentDateObject = fmatr.dateFromString('today') var inputForm = new Form() var startDateField = new Form.Field.Date( "startDate", "Start Date", currentDateObject ) var endDateField = new Form.Field.Date( "endDate", "End Date", null ) inputForm.addField(startDateField) inputForm.addField(endDateField) var formPrompt = "Enter the start and end dates:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ var startDateObject = formObject.values["startDate"] if (!startDateObject){return false} var startDateStatus = (startDateObject >= currentDateObject) if(!startDateStatus){throw "Start date must be midnight today or later"} var endDateObject = formObject.values["endDate"] if (!endDateObject){return false} var endDateStatus = (endDateObject > startDateObject) if(!endDateStatus){throw "End date must be greater than start date"} return (startDateStatus && endDateStatus) } formPromise.then(function(formObject){ var startDate = formObject.values['startDate'] console.log("Start:", startDate) var endDate = formObject.values['endDate'] console.log("End:", endDate) }) formPromise.catch(function(err){ console.log("form cancelled", err.message) })
Start and End Dates
  

var fmatr = Formatter.Date.withStyle(Formatter.Date.Style.Short) var currentDateObject = fmatr.dateFromString('today') var inputForm = new Form() var startDateField = new Form.Field.Date( "startDate", "Start Date", currentDateObject ) var endDateField = new Form.Field.Date( "endDate", "End Date", null ) inputForm.addField(startDateField) inputForm.addField(endDateField) var formPrompt = "Enter the start and end dates:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ var startDateObject = formObject.values["startDate"] if (!startDateObject){return false} var startDateStatus = (startDateObject >= currentDateObject) if(!startDateStatus){throw "Start date must be midnight today or later"} var endDateObject = formObject.values["endDate"] if (!endDateObject){return false} var endDateStatus = (endDateObject > startDateObject) if(!endDateStatus){throw "End date must be greater than start date"} return (startDateStatus && endDateStatus) } formPromise.then(function(formObject){ var startDate = formObject.values['startDate'] var endDate = formObject.values['endDate'] }) formPromise.catch(function(err){ console.log("form cancelled", err.message) })