-
Notifications
You must be signed in to change notification settings - Fork 37
Xperimental form definition syntax
Dave Methvin (USDS) edited this page Aug 27, 2018
·
1 revision
Many early users have pointed out the current schema/uiSchema setup as a high pain point for their use of the library. This page explores an alternative concise syntax that involves less redundancy.
The form chosen for a comparison was the VA Form 1990 Healthcare Benefits form.
- Live form at vets.gov site
- Source code at VA:
- form.js config (539 lines)
- JSON schema (built file, 682 lines):
{
chapter: 'Applicant Information',
label: '',
widget: 'PageDisplayGroup',
children: [
// Short (array) syntax, when widget defaults are sufficient
// [ field, widget, label, initialValue ]
[ 'veteranFullName', 'FullName', 'Full Name' ],
[ 'veteranSocialSecurityNumber', 'SSN', 'Social Security Number' ],
[ 'veteranDateOfBirth', 'AdultBirthDate', 'Date of Birth' ],
[ 'gender', 'Gender', 'Gender' ]
]
},
// Person should be at least 18 years old; used below
var MaxAdultBirthDate = new Date();
MaxAdultBirthDate.setFullYear(MaxAdultBirthDate.getFullYear()-17);
// Widgets can use already-defined widgets; this is in long (object) syntax
'AdultBirthDate': {
widget: 'Date',
label: 'Date of Birth',
validation: {
minDate: [
'1900-01-01',
'Date of birth must be after ${minDate}'
],
maxDate: [
MaxAdultBirthDate, // Converted to ISO8601 string
'Date of birth must be no later than ${maxDate}'
]
}
},
applicantInformation: _.merge(applicantInformation(fullSchema1990, {
isVeteran: true,
fields: [
'veteranFullName',
'veteranSocialSecurityNumber',
'veteranDateOfBirth',
'gender'
],
required: [
'veteranFullName',
'veteranSocialSecurityNumber',
'veteranDateOfBirth',
]
}), {
uiSchema: {
veteranDateOfBirth: {
'ui:validations': [
(errors, dob) => {
// If we have a complete date, check to make sure it’s a valid dob
if (/\d{4}-\d{2}-\d{2}/.test(dob) && moment(dob).isAfter(moment().endOf('day').subtract(17, 'years'))) {
errors.addError('You must be at least 17 to apply');
}
}
]
}
}
})
imported applicant information
return {
path: 'applicant/information',
title: 'Applicant information',
initialData: {},
uiSchema: _.assign({
'ui:order': fields,
'ui:description': applicantDescription,
[`${prefix}FullName`]: fullNameUI,
[`${prefix}DateOfBirth`]: _.assign(currentOrPastDateUI('Date of birth'),
{
'ui:errorMessages': {
pattern: 'Please provide a valid date',
futureDate: 'Please provide a valid date'
}
}
),
gender: {
'ui:widget': 'radio',
'ui:title': 'Gender',
'ui:options': {
labels: labels.gender || genderLabels
}
},
relationship: {
'ui:widget': 'radio',
'ui:title': 'What’s your relationship to the Servicemember whose benefit is being transferred to you?',
'ui:options': {
labels: labels.relationship || relationshipLabels
}
}
}, personId.uiSchema(prefix, 'view:noSSN')),
schema: {
type: 'object',
definitions: _.pick([
'fullName',
'relationship',
'ssn',
'gender',
'date',
'vaFileNumber'
], schema.definitions),
required,
properties: _.pick(fields, possibleProperties)
}
};
{
chapter: 'Benefits Eligibility',
label: '',
widget: 'PageDisplayGroup',
children: [
// Long (object) syntax
{
widget: 'Fieldset',
label: 'You may be eligible for more than 1 education benefit ...',
validation: {
errorUnless: [
'chapter33Checkbox|chapter30Checkbox|chapter1606Checkbox|chapter32Checkbox',
'You must select at least one benefit.'
]
},
children: [
[ 'chapter33Checkbox', 'Checkbox', 'Post-9/11 GI Bill (Chapter 33) ...' ],
{
widget: 'HTML',
showIf: 'chapter33Checkbox',
className: 'expand-under',
label: 'When you choose to apply for your Post-9/11 benefit, ...'
},
[ 'chapter30Checkbox', 'Checkbox','Montgomery GI Bill (MGIB-AD, Chapter 30) ...' ],
[ 'chapter1606Checkbox', 'Checkbox','Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606) ...' ],
[ 'chapter32Checkbox', 'Checkbox', 'Post-Vietnam Era Veterans’ Educational Assistance Program ...' ]
]
}
]
},
benefitsEligibility: {
title: 'Benefits eligibility',
path: 'benefits-eligibility/benefits-selection',
uiSchema: {
'ui:description': benefitsEligibilityBox,
'view:selectedBenefits': {
'ui:title': 'Select the benefit that is the best match for you.',
'ui:validations': [
validateBooleanGroup
],
'ui:errorMessages': {
atLeastOne: 'Please select at least one benefit'
},
'ui:options': {
showFieldLabel: true
},
chapter33: {
'ui:title': benefitsLabels.chapter33,
'ui:options': {
expandUnderClassNames: 'schemaform-expandUnder-indent',
}
},
'view:chapter33ExpandedContent': {
'ui:description': 'When you choose to apply for your Post-9/11 benefit, you have to relinquish (give up) 1 other benefit you may be eligible for. You’ll make this decision on the next page.',
'ui:options': {
expandUnder: 'chapter33',
}
},
chapter30: {
'ui:title': benefitsLabels.chapter30
},
chapter1606: {
'ui:title': benefitsLabels.chapter1606
},
chapter32: {
'ui:title': benefitsLabels.chapter32
}
}
},
schema: {
type: 'object',
required: ['view:selectedBenefits'],
properties: {
'view:selectedBenefits': {
type: 'object',
properties: {
chapter33,
'view:chapter33ExpandedContent': {
type: 'object',
properties: {}
},
chapter30,
chapter1606,
chapter32
}
}
}
}
},
{
chapter: 'Benefits Eligibility',
label: 'Benefits Relinquishment',
widget: 'PageDisplayGroup',
showIf: 'chapter33Checkbox',
required: [ 'benefitsRelinquished' ],
children: [
// Hidden field containing today's date (LOCAL TIME), see function above
[ 'benefitsRelinquishedDate', 'Hidden', '', () => {
const now = Date.now();
return now.getFullYear()+'-'+(now.getMonth()+1)+'-'+now.getDate();
}],
{
field: 'benefitsRelinquished',
widget: 'RadioGroup',
label: 'Because you chose to apply for your Post-9/11 benefit, ...',
children: [
{ value: 'chapter32', label: 'I\'m only eligible for the post-9/11 GI Bill' },
{ value: 'chapter30', label: 'Montgomery GI Bill (MGIB-AD, Chapter 30)' },
{ value: 'chapter1606', label: 'Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606)' },
{ value: 'chapter1607', label: 'Reserve Educational Assistance Program (REAP, Chapter 1607)' }
]
}
]
},
benefitsRelinquishment: {
title: 'Benefits relinquishment',
path: 'benefits-eligibility/benefits-relinquishment',
depends: (formData) => formData['view:selectedBenefits'].chapter33,
initialData: {
'view:benefitsRelinquishedContainer': {
benefitsRelinquishedDate: moment().format('YYYY-MM-DD')
}
},
uiSchema: {
'ui:title': 'Benefits relinquishment',
'ui:description': benefitsRelinquishmentWarning,
'view:benefitsRelinquishedContainer': {
'ui:field': BenefitsRelinquishmentField,
benefitsRelinquished: {
'ui:title': 'I choose to give up:',
'ui:widget': 'radio',
'ui:options': {
labels: benefitsRelinquishmentLabels,
}
},
benefitsRelinquishedDate: _.merge(dateUI('Effective date'), {
'ui:required': (formData) => _.get('view:benefitsRelinquishedContainer.benefitsRelinquished', formData) !== 'unknown'
})
},
'view:questionText': {
'ui:description': benefitsRelinquishedDescription
}
},
schema: {
type: 'object',
properties: {
'view:benefitsRelinquishedContainer': {
type: 'object',
required: ['benefitsRelinquished'],
properties: {
benefitsRelinquished,
benefitsRelinquishedDate
}
},
'view:questionText': {
type: 'object',
properties: {}
}
}
}
}
export const benefitsRelinquishmentLabels = {
unknown: 'I’m only eligible for the Post-9/11 GI Bill',
chapter30: 'Montgomery GI Bill (MGIB-AD, Chapter 30)',
chapter1606: 'Montgomery GI Bill Selected Reserve (MGIB-SR, Chapter 1606)',
chapter1607: 'Reserve Educational Assistance Program (REAP, Chapter 1607)'
};
- Current (Phase 2) Roadmap
- Phase 1 Information
- Release Process
- Stable: 1.2.0 (GitHub, npm)