VCF Automation VCF Automation

Good Practices for Developing Custom Forms with VMware Cloud Foundation Automation

Custom forms are an essential part of VMware Cloud Foundation Automation (formerly Aria Automation), allowing cloud administrators to design and customize request forms that simplify cloud consumption for various lines of business. With custom forms, administrators can tailor workflows to hide unnecessary fields, create dynamic sections based on user input, and integrate external data sources for auto-population. This enables businesses to consume cloud services more intuitively and efficiently, streamlining service delivery and ensuring that VCF environments meet specific organizational needs.

Note: The best practices discussed in this blog post are applicable to VMware Cloud Foundation Automation starting from version 5.2.1.

Developing effective custom forms requires careful consideration of various factors, including validation, usability, and maintainability. In this blog post, we will explore five best practices for developing custom forms that not only enhance user experience but also streamline development and minimize errors.

From validating inputs and avoiding long chains of dependent actions to utilizing field and external validations effectively, each best practice addresses specific challenges encountered during custom form development. By following these recommendations, administrators can create custom forms that are robust, user-friendly, and adaptable to evolving business requirements.

Let’s dive into these best practices and learn how to build better custom forms that meet the needs of both users and developers.

1. Validate Inputs Before Executing Actions

When working with custom forms, one of the first things that an administrator should do when designing a custom form is to validate all inputs before executing any associated actions. Failing to perform null checks or ensuring that the necessary data is present and valid can lead to errors and unexpected behavior, which can significantly disrupt the user experience.

Loading Field Values via External Value

Administrators can select from various value sources, such as Constant, Conditional, Bind, Computed, and External Value. Custom forms provide the capability to load default values, value lists, constraints, and appearances for fields via an external-value mechanism, using a VCF Orchestrator action.

For instance, to load a default value for a field via an external value:

  1. In the Form Designer, select the field.
  2. Navigate to the Values tab.
  3. Choose External Value from the Value source dropdown.
  4. In the Select action field, search for and select the action to be executed. The return value of this action will populate the text field.
    Note: The list of available actions will only include those whose return type is compatible with the field type, ensuring that the selected action’s output can be successfully used to populate the field.

It’s imperative that the field type matches the return type of the selected action. For example, a text field’s value can only be loaded via actions that return a String, and an array field of type string’s value can only be loaded via actions returning an array of strings etc.

Handling Action Parameters

Actions may require inputs, which can affect the return value based on the provided inputs. Within the custom form, inputs can either be constants or bound to other fields. Constant value parameters are safe because an empty string is sent to the orchestrator if no value is provided. However, binding action parameters to form fields require careful handling, especially during form initialization.

During initialization, if the bound field lacks a default value, it will be empty, and the action will receive a null value for the parameter. This null value can cause the action to fail,  especially if the action tries to access properties of that null object or call methods on it. Therefore, it is essential to validate each input before using it.

Consider this example of a text field in a custom form, where the value is loaded via an external source action toUpperCase() that has one parameter bound to another field (e.g., “Text Field Without Value”):


Below is an example of a poorly written toUpperCase action that does not perform input validation:

This action will function correctly if the input is a constant or if the bound field always contains a value. However, in cases where the bound field is empty during initialization, the action will fail, as shown in the following screenshot:

The error message indicates that the method toUpperCase cannot be called on a null value. To address this issue, ensure that the parameter is properly validated. Here is an example of an improved action with input validation:


In this updated action, an if statement checks whether the input is present before calling toUpperCase. If the input is null, the action returns an empty string. This simple validation step can prevent errors and improve the robustness of your custom forms.
​​

Validating Different Types of Input Parameters

Beyond null checks, it’s crucial to validate the types and constraints of input parameters. Consider a more complex action that processes multiple parameters with various validation needs. For example, let’s create an action calculateTotalPrice that takes three parameters: itemPrice, quantity, and shippingAddress.

In this example:

  • ItemPrice: Validates that it’s a positive number.
  • Quantity: Validates that it’s a positive integer.
  • ShippingAddress: Ensures it’s a non-empty string.

If any of these validations fail, the function throws an error with a descriptive message. This comprehensive validation ensures that only valid and correctly formatted data is processed, enhancing the reliability of the form.

Validating inputs before executing actions is a fundamental best practice when developing custom forms. By ensuring that all inputs are present and valid, you can prevent errors, improve user experience, and create more reliable forms. Always guard your inputs with appropriate validation checks to avoid unexpected failures and maintain the integrity of your form’s functionality.

2. Use Required Fields Wisely

Marking too many fields as required in a custom form can lead to a frustrating user experience. It’s crucial to use required fields judiciously and only when absolutely necessary to reduce errors and improve overall form usability.

Understanding Dependencies and Constraints

In custom forms, external actions used in form fields are only executed when all dependent fields with constraints are populated with valid values. When a field fails to meet its constraints—such as required, min/max value, length, or pattern constraints—the dependent fields that trigger external actions will not be evaluated until the original field is updated with valid data. Additionally, any action whose inputs are bound to an invalid field will not be executed until all constraints are satisfied. If a valid field is later updated to an invalid state, all dependent actions cease to run.

This behavior can confuse users when filling out a form. They might notice that some fields are not auto-populated with default values or that drop-down menus lack options. If this happens, users must ensure that all dependent fields with constraints are correctly filled and valid. For example, all required dependencies must be filled in, and all constraints on the dependencies must be satisfied.

Example Scenario

Consider the following custom form example, where we have a text field “Company Name” whose value is loaded via an external source action called getDefaultCompanyName. This action has one parameter bound to the field below, “Country Code”

Here is the corresponding action getDefaultCompanyName:

The action implementation handles an empty input value by returning “BroadcomUSA” as a default value. In the custom form, “Country Code” is marked as a required field with a value list (US, CA, UK, DE, FR) but without a default value. Upon form load, the dropdown has no selected value, making it invalid because it is required. This invalid state prevents the getDefaultCompanyName action from executing, resulting in the “Company Name” field remaining empty until a value is selected in the dropdown.



In this scenario, making the “Country Code” field required is unnecessary and can cause unexpected behavior. The action getDefaultCompanyName would still return a company name even if the input is null or empty. Marking the field as required introduces an invalid state that prevents the action from running, which is counterproductive.

By using required fields wisely and ensuring proper validation, you can create more user-friendly forms that function smoothly and reduce the likelihood of user errors and confusion.

3. Provide Default Values for Required Fields

When a field is marked as required and used as an input to an action, providing a default value is beneficial for improving the user experience. Failing to do so can result in the action execution being rejected due to missing input values, leading to errors and poor user experience. Always ensure that required fields, especially those used as inputs to actions, have default values to avoid that.

Importance of Default Values for Required Fields

In many blueprints and workflows, certain fields must remain required and cannot be made optional. These fields may also serve as parameters for actions. In such cases, it is highly advisable to assign a default value to them. This ensures that the field is valid upon form initialization, enabling the associated actions to execute correctly.

Example Scenario

Let’s revisit our earlier example involving a custom form with the two fields: “Company Name” and “Country Code.” The former is populated via an action called getDefaultCompanyName, which has one parameter bound to the “Country Code” field.

In this case, the “Country Code” field is marked as required but lacks a default value. To improve form UX, we can set a default value for it. For instance, setting the default value to “USA” (one of the options in the value list) ensures that the field is valid upon form initialization. This allows the getDefaultCompanyName action to execute immediately with “USA” as the input, populating the “Company Name” field just after the form loads.

In the example above, we selected a default value from the value list because the field is required. It’s also possible to load the value list via an action and still select a constant default value to resolve the issue. The key is to not leave the default value blank for required fields.

Benefits of Providing Default Values

By setting a default value for the “Country Code” field, the form behaves as expected:

  1. Valid Initialization: The field is valid immediately upon form load, preventing any invalid state.
  2. Instant Action Execution: The getDefaultCompanyName action executes instantly, using the default value (“USA”), and populates the field.
  3. Improved User Experience: Users see populated fields right away, reducing confusion and ensuring a smooth interaction with the form.

In this example, both the form fields are populated instantly after it’s rendered.

Providing default values for required fields, especially those used as inputs to actions, is a best practice that enhances form functionality and user experience. By ensuring that required fields have default values, you can prevent action execution rejection due to missing inputs, reduce user errors, and create more user-friendly forms. Always review your forms to identify required fields that lack default values and configure them accordingly to achieve better user experience.

4. Avoid Long Chains of Dependent Actions

“Dependent actions” refer to actions within a custom form that rely on the output of previous actions to execute. In other words, the execution of these actions is contingent upon the successful completion or specific outcome of preceding actions. For example, in a custom form for processing an order, an action to validate payment details might depend on a  preceding action that gathers customer information. If the customer information is incomplete or incorrect, the payment validation action might not execute as expected.

Long chains of dependent actions in a custom form can introduce complexity and increase the likelihood of errors. Additionally, these complex dependencies can also negatively impact the performance of the form, leading to slower load times and a degraded user experience. Instead, it’s best to break down the form into smaller, more manageable parts by dividing it into pages. This approach simplifies form development, debugging, and maintenance, leading to a better user experience.

Simplify Actions Logic

The primary purpose of actions in a custom form is to calculate and represent data simply and clearly. Actions should be quick and straightforward, avoiding complex logic and lengthy operations. They should not perform extensive database or REST requests, and ideally, each action should complete within a few seconds.

Pitfalls of Long Chains of Dependent Actions

Consider a scenario where a field is populated by an external value, which serves as a parameter for an action that populates another field, and so on. This results in a long chain of dependent actions that are hard to track and debug. If any of these fields have constraints, such as being required or having specific validation rules, it can break the action execution chain, leading to bad user experience.

Best Practice Recommendations

To avoid the pitfalls of long chains of dependent actions, follow these best practices:

  1. Keep Actions Simple: Create actions that perform straightforward calculations or data presentations without relying on complex long-running logic.
  2. Organize Forms: Organize complex forms into multiple pages, each with its own set of fields and logic. This improves readability and simplifies debugging.
  3. Minimize Dependencies: Limit the number of dependencies between fields and actions to reduce complexity and improve form stability.

Avoiding long chains of dependent actions is important for developing maintainable and user-friendly custom forms. By breaking down forms into manageable sections, utilizing conditional logic, and minimizing dependencies, you will create forms that are easier to debug, and maintain. Prioritize clarity and simplicity in your form design to enhance the overall user experience and minimize potential errors.

5. Utilize Field and External Validations Effectively

When designing custom forms, it’s essential to utilize both field and external validations effectively to ensure data integrity and smooth user experience.

Field validations

Field validations are predefined properties assigned to form fields, such as min/max length, regex patterns, required fields and match constraints. These validations ensure that user inputs meet specified criteria directly within the form.

External validations

External validations, on the other hand, are custom validation routines defined in actions. They are designed to handle complex logic that cannot be achieved with field validations alone. They can affect multiple fields, provide form-wide error messages, and run on catalog item submission.

Example: Using External Validation for Password Matching

Consider the scenario where a form requires users to enter and confirm a password. To ensure that the passwords match, an external validation routine is implemented using the action validateUserPassword. This action compares the new password with the confirmed password and returns either a validation error message if they do not match or null if they match.

This external validation is added in the form designer’s validation tab, specifying the new password and confirming password fields as inputs.



Upon form submission, if the passwords do not match, the external validation routine returns an error message, and the problematic fields are highlighted, preventing form submission until the issue is resolved.

However, this same validation can be achieved using the Match constraint. The Match constraint compares both fields, and if they are not equal, it fails and presents a validation error. This illustrates that in custom forms, there are often multiple ways to achieve the same goal. It is important to consider all possible options and determine whether the chosen method is the most efficient.

In this example, adding a Match constraint is much better because the final result is the same, but it is simpler and avoids an additional REST request for external validation. It is also more efficient.

Example of External Validation: Upgrade VM Hardware

In some cases, field constraints alone are not sufficient for the required validation. Consider the more complex scenario of upgrading VM hardware, where external validation ensures that the requested changes are feasible and compliant with organizational policies and available resources.

Scenario: The form contains following fields:

  1. VM ID: Identifies the VM to be upgraded.
  2. New CPU Count: The desired number of CPUs.
  3. New Memory Size (GB): The desired memory size in gigabytes.
  4. New Disk Size (GB): The desired disk size in gigabytes.
  5. Network Bandwidth (Mbps): The desired network bandwidth.

Validation Needs:

  • Ensure the new hardware specifications do not exceed the physical limits of the host machine.
  • Confirm the VM’s current host has sufficient resources (CPU, memory, disk space, network capacity) to accommodate the upgrade.
  • Check compliance with organizational policies (e.g., maximum allowed resources per VM).
  • Verify that the user requesting the upgrade has the necessary permissions.

Action for External Validation: validateUpgradeRequest

Inputs:

  • VM ID
  • New CPU Count
  • New Memory Size (GB)
  • New Disk Size (GB)
  • Network Bandwidth (Mbps)

Logic:

1. Retrieve VM and Host Details:

  • Fetch VM details based on VM ID.
  • Fetch host details where the VM is running.

2. Resource Validation:

  • Check if the new CPU count exceeds the host’s available CPU capacity.
  • Verify that the new memory size is within the host’s available memory.
  • Ensure the new disk size can be accommodated by the host’s available storage.
  • Confirm the requested network bandwidth does not exceed the host’s network capacity.

3. Policy Compliance Check:

  • Ensure requested resources comply with organizational policies (e.g., max CPU count, max memory size).

4. Permission Check:

  • Verify the user has the required permissions to request the upgrade.

5. Return Result:

  • If all validations pass, return null.
  • If any validation fails, return an appropriate error message indicating the issue.

Implementation:

This example of external validation in an “Upgrade VM Hardware” form demonstrates how complex validation scenarios can be managed effectively. Such validations ensure that all necessary requirements are met before proceeding with the upgrade. This level of validation cannot be achieved with simple field constraints, highlighting the importance and utility of external validations in custom forms.

Enhancing Field Validations by Adding Constraints

In addition to external validations, field validations can be enhanced with constraints such as regex patterns, min/max length settings, required and match field. For example, the password field can be constrained to require at least one letter, one number, and one special character, with a minimum length of 8 characters and a maximum length of 30 characters.

If the user enters a password that does not meet these constraints, the field is highlighted and the validation error is displayed, guiding the user to correct the input.

By effectively utilizing both field and external validations, you can create custom forms that ensure data integrity, enhance user experience, and handle complex validation scenarios. Field validations cover common constraints, while external validations provide flexibility for more intricate validation logic. Together, they form a robust validation that improves form usability and reliability. Always consider the specific requirements of your form and leverage validations accordingly to achieve the desired outcomes.

Developing custom forms that are robust, user-friendly, and error-free requires adhering to best practices. By validating inputs, using required fields wisely, providing default values, avoiding long chains of dependent actions, and utilizing field and external validations effectively, you can create forms that meet user needs and perform reliably. Implementing these guidelines will not only enhance the user experience but also streamline the form development process, ensuring your custom forms are both functional and secure.