Skip to main content

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>
);
}
SemanticMUIBootstrap4Bootstrap5AntDUnstyled

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:

  1. By replacing the AutoField inside the AutoForm with a desired field.
  2. 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>
);
}
SemanticMUIBootstrap4Bootstrap5AntDUnstyled

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.

caution

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:

SemanticMUIBootstrap4Bootstrap5AntDUnstyled