Introduction

While working with forms is fun, there are times when users do not want to type in all of their information over and over again. This is where preFill technology comes into play and saves the day!

What is Pre-Fill?

Pre-fill is a simple API call to Vets-API that allows the form user to have their information pre filled in the form without having to type anything. This guide will show you how to setup pre-fill inside Vets-website and how to submit a Pull Request (PR) into Vets-API Pull Requests.

Vets-Website Work

To get started, navigate to your application folder and set up some information for the pre-fill to be enabled. Let’s start with the formConfig object, which usually lives in src/application/<your-app>/config/form.js, where we need to add a key in the formConfig object called prefillEnabled.

Here is an example from the LGY/COE Form.js:

const formConfig = {
  ...
  prefillEnabled: true,
  ...
}
JS

The next thing we need to do is hook the formConfig setting into our code, which we'll do by passing it to the SaveInProgress component. As you can see, the property prefillEnabled has to be set, but we're using the true value from the formConfig object above.

Here is an example from LGY/COE form: LGY/COE LoggedInContent SaveInProgressIntro

<SaveInProgressIntro
      testActionLink
      prefillEnabled={route.formConfig.prefillEnabled}
      messages={route.formConfig.savedFormMessages}
      formConfig={route.formConfig}
      pageList={route.pageList}
      downtime={route.formConfig.downtime}
      startText="Request a Certificate of Eligibility"
      headingLevel={2}
/>
JS

PreFill Transformers

In the VA Forms Library there are also some helper functions created around pre-fill that could be useful to better organize pre-fill data. One of those helpers is a prefillTransformer, which can be used to take in a pre-filled form data object, restructure it, and return the new object to be used in the form.

Here is an example from the Direct Deposit PrefillBankInformation:

/**
 * Use this function in the prefillTransformer to move all bank account
 * information into `view:originalBankAccount`. This is useful when using the
 * PaymentView component, which will display either `bankAccount` or
 * `view:originalBankAccount`.
 *
 * @param {object} data - All the pre-filled form data
 * @returns {object} - A new pre-filled form data object after transformation.
 */
export const prefillBankInformation = (
  data,
  prefilledFieldNames = defaultFieldNames,
) => {
  const newData = _.omit(
    [
      prefilledFieldNames.accountType,
      prefilledFieldNames.accountNumber,
      prefilledFieldNames.routingNumber,
      prefilledFieldNames.bankName,
    ],
    data,
  );

  const accountType = data[prefilledFieldNames.accountType];
  const accountNumber = data[prefilledFieldNames.accountNumber];
  const routingNumber = data[prefilledFieldNames.routingNumber];
  const bankName = data[prefilledFieldNames.bankName];

  if (accountType && accountNumber && routingNumber && bankName) {
    newData['view:originalBankAccount'] = viewifyFields({
      accountType,
      accountNumber,
      routingNumber,
      bankName,
    });

    // start the bank widget in 'review' mode
    newData.bankAccount = { 'view:hasPrefilledBank': true };
  }

  return newData;
};
JS

This snippet above is the working code that returns your new transformed object; let’s see that in action in the Education Benefits application (Form 1990s):

import { prefillBankInformation } from 'platform/forms-system/src/js/definitions/directDeposit';

...

export function prefillTransformer(pages, formData, metadata) {
  
  ...
  
  if (bankAccount) {
    const prefillBankInfo = prefillBankInformation({
      ...bankAccount,
      bankName: bankAccount?.bankName || 'fakeBank', // so that the check in the function doesn't fail if no bankName, omitted down below
    });
    const prefillBankAccount = _.get(
      prefillBankInfo,
      'view:originalBankAccount',
      {},
    );
    const originalBankAccount = {
      ...prefillBankAccount,
      'view:accountType': prefillBankAccount['view:accountType']?.toLowerCase(),
      'view:bankName': undefined, // need this so that prefill display messages will show correctly, remove if bankName is added
    };

    newFormData = {
      ...newFormData,
      'view:originalBankAccount': originalBankAccount,
      'view:directDeposit': {
        bankAccount: {
          ...prefillBankInfo.bankAccount,
          ...deviewifyFields(_.omit(originalBankAccount, ['view:bankName'])),
        },
      },
    };
  }

  return {
    metadata,
    formData: newFormData,
    pages,
  };
}
JS

Vets-API Work

After getting everything setup in Vets-Website, you'll need to move on to Vets-API and get a Pull Request (PR) set up in Vets-API Pull Requests.

In this tutorial, we’ll be using this PR as a working example of what needs to be added and changed: Vets-API 12739 Pre-fill CH31 #5391 PR.

App/Models/Form_Profile.rb

The first set of changes must be made in the form_profile file, where we need to add our application pre-fill to the ALL_FORMS object. You can see in this example that vre_counseling and vre_readiness were added: Vets-API Form_Profile.rb VRE_Counseling Line.

ALL_FORMS = {
    edu: %w[22-1990 22-1990N 22-1990E 22-1995 22-5490
            22-5495 22-0993 22-0994 FEEDBACK-TOOL 22-10203 22-1990S 22-1990EZ],
    evss: ['21-526EZ'],
    hca: ['1010ez'],
    pension_burial: %w[21P-530 21P-527EZ],
    dependents: ['686C-674'],
    decision_review: %w[20-0996 10182],
    mdot: ['MDOT'],
    fsr: ['5655'],
    vre_counseling: ['28-8832'],
    vre_readiness: ['28-1900'],
    coe: ['26-1880']
  }.freeze
RUBY

Inside of the same file we need to adjust the FORM_ID_TO_CLASS object and map our new pre-fill applications to their corresponding FormProfiles: Vets-API Form_Profile.rb FormProfile line.

FORM_ID_TO_CLASS = {
    '0873' => ::FormProfiles::VA0873,
    '1010EZ' => ::FormProfiles::VA1010ez,
    '10182' => ::FormProfiles::VA10182,
    '20-0996' => ::FormProfiles::VA0996,
    '21-526EZ' => ::FormProfiles::VA526ez,
    '22-1990' => ::FormProfiles::VA1990,
    '22-1990N' => ::FormProfiles::VA1990n,
    '22-1990E' => ::FormProfiles::VA1990e,
    '22-1995' => ::FormProfiles::VA1995,
    '22-5490' => ::FormProfiles::VA5490,
    '22-5495' => ::FormProfiles::VA5495,
    '21P-530' => ::FormProfiles::VA21p530,
    '21-686C' => ::FormProfiles::VA21686c,
    '686C-674' => ::FormProfiles::VA686c674,
    '40-10007' => ::FormProfiles::VA4010007,
    '21P-527EZ' => ::FormProfiles::VA21p527ez,
    '22-0993' => ::FormProfiles::VA0993,
    '22-0994' => ::FormProfiles::VA0994,
    'FEEDBACK-TOOL' => ::FormProfiles::FeedbackTool,
    'MDOT' => ::FormProfiles::MDOT,
    '22-10203' => ::FormProfiles::VA10203,
    '22-1990S' => ::FormProfiles::VA1990s,
    '5655' => ::FormProfiles::VA5655,
    '28-8832' => ::FormProfiles::VA288832,
    '28-1900' => ::FormProfiles::VA281900,
    '22-1990EZ' => ::FormProfiles::VA1990ez,
    '26-1880' => ::FormProfiles::VA261880
  }.freeze
RUBY

Now that we've set those items, we need to go make our Form_Profile page.

App/Models/Form_Profiles/va281900.rb

Following the steps above, we can now create our Vets-API Form Profiles va281900.rb FormProfile file that defines a metadata object that holds the version, the pre-fill boolean, and the returnUrl:

# frozen_string_literal: true

class FormProfiles::VA281900 < FormProfile
  def metadata
    {
      version: 0,
      prefill: true,
      returnUrl: '/veteran-information-review'
    }
  end
end
RUBY

Config/Form_Profile_mappings/28-1900.yml

We'll need a Vets-API 28-1900 Form_Profile_Mapping file for the data that's going to be pre-filled:

veteranInformation:
  fullName: [identity_information, full_name]
  ssn: [identity_information, ssn]
  dob: [identity_information, date_of_birth]
veteranAddress: [contact_information, address]
mainPhone: [contact_information, us_phone]
cellPhone: [contact_information, mobile_phone]
email: [contact_information, email]
RUBY

Config/settings.yml

In the Vets-API settings.yml file we need to add the vre_counseling and vre_readiness pre-fill boolean setting:

vre_counseling:
  prefill: true

vre_readiness:
  prefill: true

# Settings for EVSS
evss:
  prefill: true
YAML

Spec/Models/Form_Profile_Spec.rb

Finally, you need to add a test (Vets-API Form_Profile_spec.rb Test File) to ensure everything is working properly and automated tests are working as intended.

let(:v28_1900_expected) do
  {
    'veteranInformation' => {
      'fullName' => {
        'first' => user.first_name&.capitalize,
        'last' => user.last_name&.capitalize,
        'suffix' => user.suffix
      },
      'ssn' => '796111863',
      'dob' => '1809-02-12'
    },
    'veteranAddress' => {
      'street' => street_check[:street],
      'street2' => street_check[:street2],
      'city' => user.address[:city],
      'state' => user.address[:state],
      'country' => user.address[:country],
      'postal_code' => user.address[:zip][0..4]
    },
    'mainPhone' => us_phone,
    'email' => user.pciu_email
  }
end
RUBY

Also remember to add to the list of returns prefilled IDs to pass that test as well: Vets-API Returns Prefilled Ids Test.

%w[
    22-1990
    22-1990N
    22-1990E
    22-1995
    22-5490
    22-5495
    40-10007
    1010ez
    22-0993
    FEEDBACK-TOOL
    686C-674
    28-8832
    28-1900
    26-1880
  ].each do |form_id|
    it "returns prefilled #{form_id}" do
      expect_prefilled(form_id)
    end
  end
RUBY