Using predefined fields
Now that we know how to ask our guests for their name, let's ask them a few more things! We can expect a lot of different people coming to our IT conference - they could be developers, testers, product owners, project managers, businessmen and so on. Perhaps we want to calculate how many representatives of various professions will visit us? Or maybe (if there are enough people) we want to prepare special, personalized events? It would be also a decent idea to somehow gather their visions and expectations about the conference or any additional valuable information for the organizer.
Let's modify our schema by adding two new properties: profession
and additionalInfo
in order to have the additional fields rendered:
import Ajv, { JSONSchemaType } from 'ajv';
type FormData = {
firstName: string;
lastName: string;
workExperience: number;
profession: string;
additionalInfo: string;
};
const schema: JSONSchemaType<FormData> = {
title: 'Guest',
type: 'object',
properties: {
firstName: { type: 'string' },
lastName: { type: 'string' },
workExperience: {
description: 'Work experience in years',
type: 'integer',
minimum: 0,
maximum: 100,
},
profession: { type: 'string' },
additionalInfo: { type: 'string' },
},
required: ['firstName', 'lastName'],
};
After the schema change, we must not forget to add the fields to our form.
We will add the profession
field above the workExperience
field and the additionalInfo
at the bottom:
import React from 'react';
import {
AutoField,
AutoForm,
ErrorField,
SubmitField,
} from 'uniforms-semantic';
import { bridge as schema } from './GuestSchema';
export function GuestFormProfessionAdditionalInfo() {
return (
<AutoForm schema={schema} onSubmit={console.log}>
<h4>IT meeting guest questionnaire</h4>
<AutoField name="lastName" />
<ErrorField name="lastName">
<span>You have to provide your last name!</span>
</ErrorField>
<AutoField name="firstName" />
<ErrorField
name="firstName"
errorMessage="You have to provide your first name!"
/>
<span>Do you want to share your work experience with us?</span>
<AutoField name="workExperience" />
<ErrorField
name="workExperience"
errorMessage="Your work experience cannot be lesser than 0 or greater than 100 years!"
/>
<AutoField name="profession" />
<AutoField name="additionalInfo" />
<SubmitField />
</AutoForm>
);
}
Let's stop for a while and think of what we have. Obviously, we there is a form generated, but it's not quite what we've expected:
- The
profession
field is rendered a an ordinary text input, where anyone can type literally anything. It would be far better if it would be displayed as a selectable list of options. - The
additionalInfo
property is supposed to store lots of text, so it's preffered to be displayed as a text area.
We can handle it in two ways:
- By replacing the
AutoField
inside theAutoForm
with a desired field. - By manipulating the schema using the
uniforms
key.
1. Replacing the AutoField
uniforms provide a set of predefined out-of-the-box fields that we can use instead of the AutoField
.
The perfect one for a profession
property is a SelectField
. All we have to do to have it rendered is to provide the list of available options.
The same thing applies to the additionalInfo
- in order to see it as a text area we can use the LongTextField
component.
Let's see our changes in action:
import React from 'react';
import {
AutoForm,
AutoField,
ErrorField,
LongTextField,
SelectField,
SubmitField,
} from 'uniforms-semantic';
import { bridge as schema } from './GuestSchema';
const professions = [
{
label: 'Developer',
value: 'developer',
},
{
label: 'Tester',
value: 'tester',
},
{
label: 'Product owner',
value: 'product-owner',
},
{
label: 'Project manager',
value: 'project-manager',
},
{
label: 'Businessman',
value: 'businessman',
},
];
export function GuestFormPredefinedFields() {
return (
<AutoForm schema={schema} onSubmit={console.log}>
<h4>IT meeting guest questionnaire</h4>
<AutoField name="lastName" />
<ErrorField name="lastName">
<span>You have to provide your last name!</span>
</ErrorField>
<AutoField name="firstName" />
<ErrorField
name="firstName"
errorMessage="You have to provide your first name!"
/>
<span>Do you want to share your work experience with us?</span>
<AutoField name="workExperience" />
<ErrorField
name="workExperience"
errorMessage="Your work experience cannot be lesser than 0 or greater than 100 years!"
/>
<SelectField name="profession" options={professions} />
<LongTextField name="additionalInfo" />
<SubmitField />
</AutoForm>
);
}
As you can see, now we have a beautiful select field and a text area!
2. Manipulating the schema
Now that we know how to use predefined fields within the AutoForm
, let's see how to do it in a more fancy way,
without the necessity to replace the AutoField
. That means modifying our schema and shows true magic of the uniforms.
Before touching the schema, let's have a look at the React form first:
import React from 'react';
import {
AutoForm,
AutoField,
ErrorField,
SubmitField,
} from 'uniforms-semantic';
import { bridge as schema } from './GuestSchema';
export function GuestFormWithFieldsInSchema() {
return (
<AutoForm schema={schema} onSubmit={console.log}>
<h4>IT meeting guest questionnaire</h4>
<AutoField name="lastName" />
<ErrorField name="lastName">
<span>You have to provide your last name!</span>
</ErrorField>
<AutoField name="firstName" />
<ErrorField
name="firstName"
errorMessage="You have to provide your first name!"
/>
<span>Do you want to share your work experience with us?</span>
<AutoField name="workExperience" />
<ErrorField
name="workExperience"
errorMessage="Your work experience cannot be lesser than 0 or greater than 100 years!"
/>
<AutoField name="profession" />
<AutoField name="additionalInfo" />
<SubmitField />
</AutoForm>
);
}
As you can see, both SelectField
and LongTextField
have been removed in favor of the AutoField
.
Now we can focus on modifying the schema.
Instead of having to type the React component by ourselves inside our form, we can declare it inside the schema, thanks to the uniforms
property, as it's demonstrated below:
import Ajv, { JSONSchemaType } from 'ajv';
type FormData = {
firstName: string;
lastName: string;
workExperience: number;
profession: string;
additionalInfo: string;
};
const schema: JSONSchemaType<FormData> = {
title: 'Guest',
type: 'object',
properties: {
firstName: { type: 'string' },
lastName: { type: 'string' },
workExperience: {
description: 'Work experience in years',
type: 'integer',
minimum: 0,
maximum: 100,
},
profession: {
type: 'string',
options: [
{
label: 'Developer',
value: 'developer',
},
{
label: 'Tester',
value: 'tester',
},
{
label: 'Product owner',
value: 'product-owner',
},
{
label: 'Project manager',
value: 'project-manager',
},
{
label: 'Businessman',
value: 'businessman',
},
],
},
additionalInfo: {
type: 'string',
uniforms: { component: LongTextField },
},
},
required: ['firstName', 'lastName'],
};
What's changed is that now we've included the available options
inside the profession
property definition -
uniforms are smart enough to deduce result field type, thanks to the AutoField
algorithm.
Remember to register the uniforms
and options
keyword (see Unknown keywords for more details).
// Required by Ajv strict mode
ajv.addVocabulary(['options', 'uniforms']);
When it comes to the additionalInfo
, there is a slightly different approach applied - we can explicitly tell the AutoForm
what field should be rendered.
Just don't forget to import the desired one!
The resulting form remains very the same: