Hiding and showing fields
Explicit field omission
The setFieldOmission method
This is a method of the changesetWebform class.
Sets a fields omitted property to true. It receives a single argument, the fieldId of the field to update.
Setting omitted to true on a fields has several implications:
- the related changeset property will not be validated when the
validateFieldsmethod is run on submit, or in an action. - the fields HTML element will be removed from the DOM entirely.
- the related data property will not be included in the data which is sent with the submit action.
- if the field's
resetWhenOmittedproperty is true (Which is the default) the field will be reset. This means that any unsaved chnages to the field's changeset property will be rolled back usingchangeset.rollback()and the field will be unvalidated.
See the below example of using includeField and omitField.
import React from 'react'; import ChangesetWebform from 'react-changeset-webforms/src/components/ChangesetWebform.jsx'; const formSchema = { formSettings: { formName: 'omittingFields1', hideSubmitButton: true, }, fields: [ { fieldId: 'mealRequired', fieldLabel: 'Would you like to order a meal?', fieldType: 'radioButtonGroup', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Would you like to order a meal', }, }, ], options: ['Yes', 'No'], }, { fieldId: 'mealOption', fieldType: 'radioButtonGroup', fieldLabel: 'Please select a meal option', omitted: true, validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Meal option' }, }, ], options: ['Beef', 'Chicken', 'Vegetarian', 'Vegan'], }, ], }; export default function HiddenFieldsExampleOne() { const [changesetIsValid, setChangesetIsValid] = React.useState(false); async function onFieldValueChange(formField, changesetWebform) { if (formField.fieldId === 'mealRequired') { if (formField.fieldValue === 'Yes') { changesetWebform.setFieldOmission('mealOption', false); } else { changesetWebform.setFieldOmission('mealOption', true); } } setChangesetIsValid(!changesetWebform.hasValidationErrors && !changesetWebform.hasUnvalidatedFields); } return ( <div data-test-id="omitted-fields-example-one"> <ChangesetWebform formSchema={formSchema} onFieldValueChange={onFieldValueChange} /> {changesetIsValid && ( <button data-test-id="next-button" className="btn btn-outline-primary" type="button" > Next </button> )} </div> ); }
The setOmission method
This is a method of the formField class.
The effect is exactly the same as with using the setFieldOmission method of a changesetWebform class instance above. This method simply offers an alternative way to achieve the same thing.
import React from 'react'; import ChangesetWebform from 'react-changeset-webforms/src/components/ChangesetWebform.jsx'; const formSchema = { formSettings: { formName: 'omittingFields5', hideSubmitButton: true, }, fields: [ { fieldId: 'mealRequired', fieldLabel: 'Would you like to order a meal?', fieldType: 'radioButtonGroup', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Would you like to order a meal', }, }, ], options: ['Yes', 'No'], }, { fieldId: 'mealOption', fieldType: 'radioButtonGroup', fieldLabel: 'Please select a meal option', omitted: true, validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Meal option' }, }, ], options: ['Beef', 'Chicken', 'Vegetarian', 'Vegan'], }, ], }; export default function HiddenFieldsExampleFive() { const [changesetIsValid, setChangesetIsValid] = React.useState(false); async function onFieldValueChange(formField, changesetWebform) { const mealOptionField = changesetWebform.fields.find((field) => field.fieldId === 'mealOption'); if (formField.fieldId === 'mealRequired') { if (formField.fieldValue === 'Yes') { mealOptionField.setOmission(false); } else { mealOptionField.setOmission(true); } } setChangesetIsValid(!changesetWebform.hasValidationErrors && !changesetWebform.hasUnvalidatedFields); } return ( <div data-test-id="omitted-fields-example-five"> <ChangesetWebform formSchema={formSchema} onFieldValueChange={onFieldValueChange} /> {changesetIsValid && ( <button data-test-id="next-button" className="btn btn-outline-primary" type="button" > Next </button> )} </div> ); }
Dynamic field omission
It may not be convenient to use action handlers to forcibly show and hide fields in this scenario, so your field schema can define general conditions under which it should be shown or omitted. This is done using the omitted property.
The omitted property
In order to enable dynamic field omission, the omitted property of a form field must be an object with three required properties.
returns- Boolean. The value to set field omission to if the
whereproperty evaluates astrue. Note that if thewhereproperty evaluates asfalse, the field omission will be set to the opposite of thereturnsvalue.
- Boolean. The value to set field omission to if the
where- either
anyConditionsTrueorallConditionsTrue. IfallConditionsTrue, thenwherewill evaluate totrueif every condition in the conditions array evaluates totrue. IfanyConditionsTruethenwherewill evaluate totrueif at least one condition in the conditions array evaluates totrue.
- either
conditions- an array of objects each specifiying a
fieldIdandvalueEquals. The condition evaluates to true if the current value of the related field matches that ofvalueEquals.
- an array of objects each specifiying a
fieldId: 'chooseSeat',omitted: {returns: false,where: 'allConditionsTrue',conditions: [ {fieldId: 'isMember',valueEquals: 'Yes', }, {fieldId: 'hasTicket'valueEquals: 'Yes'} ], },Let's consider the omitted property in relation to the snippet above.
First, returns is false, and where is allConditionsTrue.
- This means that if every item in the
conditionsarray evaluates totrue, the field with afieldIdofchooseSeatwill not be omitted.- This only occurs if:
- the field with a
fieldIdofisMemberhas a value of Yes, and - the field with a
fieldIdofhasTickethas a value of Yes.
- the field with a
- This only occurs if:
- This also means that if any item in the
conditionsarray evaluates tofalsethe field with afieldIdofchooseSeatwill be omitted.- This occurs if:
- the field with a
fieldIdofisMemberdoes not have a value of Yes, or - the field with a
fieldIdofhasTicketdoes not have a value of Yes.
- the field with a
- This occurs if:
The live example below shows the mealOption field being omitted or not, based on the value of the mealRequired field.
import React from 'react'; import ChangesetWebform from 'react-changeset-webforms/src/components/ChangesetWebform.jsx'; const formSchema = { formSettings: { formName: 'omittingFields2', hideSubmitButton: true, }, fields: [ { fieldId: 'mealRequired', fieldLabel: 'Would you like to order a meal?', fieldType: 'radioButtonGroup', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Would you like to order a meal', }, }, ], options: ['Yes', 'No'], }, { fieldId: 'mealOption', fieldType: 'radioButtonGroup', fieldLabel: 'Please select a meal option', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Meal option' }, }, ], options: ['Beef', 'Chicken', 'Vegetarian', 'Vegan'], omitted: { returns: false, where: 'anyConditionsTrue', conditions: [ { fieldId: 'mealRequired', valueEquals: 'Yes', }, ], }, }, ], }; export default function HiddenFieldsExampleTwo() { const [changesetIsValid, setChangesetIsValid] = React.useState(false); async function onFieldValueChange(_formField, changesetWebform) { setChangesetIsValid(!changesetWebform.hasValidationErrors && !changesetWebform.hasUnvalidatedFields); } return ( <div data-test-id="omitted-fields-example-two"> <ChangesetWebform formSchema={formSchema} onFieldValueChange={onFieldValueChange} /> {changesetIsValid && ( <button data-test-id="next-button" className="btn btn-outline-primary" type="button" > Next </button> )} </div> ); }
Extending the dynamic field omission API
By default, objects in the conditions array can only include valueEquals along with fieldId as properties.
If you need extend this API to include comparisions other than exact string match, you can do so by passing a hash of additional methods to the ChangesetWebform component as the @dynamicIncludeExcludeConditions property.
Each method receives value and condition as arguments, and should return a truthy value.
valueis the current value of the field with thefieldIdspecified it the condition.conditionis the relevant condition specified.
Now, any conditions in then dynamicOmission property of your field schema can use the names of any of these methods as a key, with the value to compare to.
In the example below, we add and invoke the valueDoesNotEqual method.
import React from 'react'; import ChangesetWebform from 'react-changeset-webforms/src/components/ChangesetWebform.jsx'; const dynamicIncludeExcludeConditions = { valueDoesNotEqual: (value, condition) => value !== condition.valueDoesNotEqual, }; const formSchema = { formSettings: { formName: 'omittingFields3', hideSubmitButton: true, }, fields: [ { fieldId: 'mealRequired', fieldLabel: 'Would you like to order a meal?', fieldType: 'radioButtonGroup', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Would you like to order a meal', }, }, ], options: ['Yes', 'No'], }, { fieldId: 'mealOption', fieldType: 'radioButtonGroup', fieldLabel: 'Please select a meal option', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Meal option' }, }, ], options: ['Beef', 'Chicken', 'Vegetarian', 'Vegan'], omitted: { returns: false, where: 'anyConditionsTrue', conditions: [ { fieldId: 'mealRequired', valueDoesNotEqual: 'No', }, ], }, }, ], }; export default function HiddenFieldsExampleThree() { const [changesetIsValid, setChangesetIsValid] = React.useState(false); async function onFieldValueChange(_formField, changesetWebform) { setChangesetIsValid(!changesetWebform.hasValidationErrors && !changesetWebform.hasUnvalidatedFields); } return ( <div data-test-id="omitted-fields-example-three"> <ChangesetWebform formSchema={formSchema} onFieldValueChange={onFieldValueChange} dynamicIncludeExcludeConditions={dynamicIncludeExcludeConditions} /> {changesetIsValid && ( <button data-test-id="next-button" className="btn btn-outline-primary" type="button" > Next </button> )} </div> ); }
Nested dynamic omission rules
Conditions can also be nested.
Any item in the conditions array of a ruleset can itself be a ruleset.
The example below shows howe the second condition on the anyConditionsTrue ruleset is itself an allConditionsTrue ruleset.
import ChangesetWebform from 'react-changeset-webforms/src/components/ChangesetWebform.jsx'; const formSchema = { formSettings: { formName: 'omittingFields4', hideSubmitButton: true, }, fields: [ { fieldId: 'isMember', fieldLabel: 'Are you a member?', fieldType: 'radioButtonGroup', validatesOn: ['$inherited', 'insertWithValue'], validationRules: [ { validationMethod: 'validatePresence', arguments: { presence: true, description: 'Are you a member', }, }, ], options: ['Yes', 'No'], }, { fieldId: 'mains', fieldType: 'input', inputType: 'number', min: 1, max: 3, fieldLabel: 'How many main meals would you like to order?', validationRules: [ { validationMethod: 'validateFormat', arguments: { type: 'number' }, }, ], }, { fieldId: 'sides', fieldType: 'input', inputType: 'number', min: 1, max: 3, fieldLabel: 'How many side dishes would you like to order?', validationRules: [ { validationMethod: 'validateFormat', arguments: { type: 'number' }, }, ], }, { fieldId: 'freeDrink', fieldType: 'radioButtonGroup', fieldLabel: `You've qualified for a free drink! You can select one of the following:`, options: ['Orange juice', 'Water', 'Chocolate milk'], omitted: { returns: false, where: 'anyConditionsTrue', conditions: [ { fieldId: 'isMember', valueEquals: 'Yes', }, { returns: true, where: 'allConditionsTrue', conditions: [ { fieldId: 'mains', valueEquals: '3', }, { fieldId: 'sides', valueEquals: '3', }, ], }, ], }, }, ], }; export default function HiddenFieldsExampleFour() { return ( <div data-test-id="omitted-fields-example-four"> Free drink for members and orders including 3 mains and 3 side dishes! <ChangesetWebform formSchema={formSchema} /> </div> ); }