×
Back to book

C#Bot Custom Form Question

How to create, enable, and use a custom form question.

When using forms there will be some cases where the existing question tiles will not support all the features that are needed. You will therefore need to add a custom question tile to the application and define your own logic inside of it.

Building a Custom Text Area Question

In this tutorial we are going to be building a custom text area question for a form.

Adding the Type for the Question Tile

The first thing we need to do is to add the new type into the JSON schema for the form data. This is located in clientside/src/Forms/Schema/Question.d.ts. In this file there should be the following code block

export type QuestionType = 'text'
| 'number'
| 'checkbox'
| 'statement'
| 'date'
| 'datetime'
| 'list'
| 'radiobutton'
// % protected region % [Add extra question types here] off begin
// % protected region % [Add extra question types here] end
;

All that needs to be done here is to enable the protected region and put in the type of our new tile.

// % protected region % [Add extra question types here] on begin
| 'textarea'
// % protected region % [Add extra question types here] end

This type definition file is used to define the JSON object for the form schema. Adding this type will ensure the compile time types can reference the new question tile.

Registering the Question Tile

For the application to be able to view the question tile it will need to be registered for the application to read.

In clientside/src/Forms/Questions/QuestionUtils there shall be the following block of code

// % protected region % [Add any further imports here] off begin
// % protected region % [Add any further imports here] end

export const questions: Array<typeof QuestionComponent> = [
    TextQuestionTile,
    NumberQuestionTile,
    CheckboxQuestionTile,
    FormStatementTile,
    DateTimeQuestionTile,
    RadioQuestionTile,
    ListStatementTile,
    DateQuestionTile,
    // % protected region % [Add any extra question types here] off begin
    // % protected region % [Add any extra question types here] end
];

Enable both of the protected regions and place the following contents in each of them

// % protected region % [Add any further imports here] on begin
import { TextAreaQuestionTile } from 'Forms/Questions/Tiles/TextAreaQuestionTile';
// % protected region % [Add any further imports here] end

and

// % protected region % [Add any extra question types here] on begin
TextAreaQuestionTile,
// % protected region % [Add any extra question types here] end

This list is used as the central registry for the question tiles available in the application and adding to this list is needed for the forms runtime to be able to use the question.

Building the Question Tile

All of the questions inside of C#Bot are stored in the clientside/src/Forms/Questions/Tiles directory. You will need to create a file in this directory named TextAreaQuestionTile.tsx.

All question tiles have the following structure

import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { QuestionType, ValidatorType } from 'Forms/Schema/Question';
import { IQuestionTile, QuestionTileOptionsProps } from '../QuestionTile';
import { QuestionComponent, QuestionComponentProps } from 'Forms/Questions/QuestionComponent';

/**
 * This component is what is rendered in the options menu for the form builder.
 */
@observer
export class TextAreaQuestionTileOptions extends Component<QuestionTileOptionsProps> {
    public render() {
        const { question, schema } = this.props;

        // The tile options component is used to provide the default options, such as question name, validators ect.
        // You can add extra things to this render function to have more options, or remove the tile options component
        // entirely to provide your entirely custom configuration.
        return <div>Your question options here</div>;
    }
}

/**
 * The props for this question tile are extended from the generic question component props. If you have custom data that
 * you configure in your options component you can type it here to access it's values through a typed interface. 
 */
export interface ITextAreaQuestionTileProps<T> extends QuestionComponentProps<T> {

}

/**
 * The question tile itself. It is important that the component extends the QuestionComponent class and implements
 * IQuestionTile. The generic argument that is provided is the type of the model prop, which is the object that is
 * edited when changing data in the form.
 */
@observer
export class TextAreaQuestionTile<T> extends QuestionComponent<T, ITextAreaQuestionTileProps<T>> implements IQuestionTile {
    /** The name of the question that is displayed on the user interface */
    static displayName = 'Text Area';

    /** This is the name of the question that is used internally by the system */
    static questionType: QuestionType = 'textarea';

    /** The options menu to display in the sidebar. This class is defined above */
    static optionsMenu = TextAreaQuestionTileOptions;

    /**
     * The content that is displayed to the user that is filling out the form.
     */
    public render() {
        return <div>Your question content here</div>;
    }
}

Once this code is placed in the file all that needs to be done is render the question content of the new custom question.

It is important to note the static properties that are defined in the class. These are used by the forms runtime to determine how to render your question tile.

/** The name of the question that is displayed on the user interface */
static displayName = 'Text Area';

/** This is the name of the question that is used internally by the system */
static questionType: QuestionType = 'textarea';

/** The options menu to display in the sidebar. This class is defined above */
static optionsMenu = TextAreaQuestionTileOptions;

Now we are going to implement the actual functionality of the question tile. First ensure that you add the following imports to the top of the file.

import { hasRequiredValidator } from 'Forms/Validators/ValidationUtils';
import { TextArea } from 'Views/Components/TextArea/TextArea';

At the bottom of the file, in the render function of the TextAreaQuestionTile class, replace it with the following content:

public render() {
    const { title, model, id, isReadOnly, validators, toolTip, className, checkValidation } = this.props;

    return (
        <TextArea
            model={model}
            modelProperty={id}
            label={title}
            isReadOnly={isReadOnly}
            isRequired={hasRequiredValidator(validators)}
            tooltip={toolTip}
            onAfterChange={checkValidation}
            className={className}
        />
    );
}

This will render the text area that will be displayed on the form. To explain the sections that are being received in the component

  • model - The observable object that contains the submission data for the form.
  • id - The id of the question. This is the key on the model prop that needs to have the answer for the question stored in it.
  • title - The title of the question.
  • isReadOnly - If the form is in read only mode. This is important since the form designer is simply a form in read only mode.
  • validators - A potentially undefined array of form validators. More on this below.
  • toolTip - A tooltip for the question.
  • className - The class name for the question.
  • checkValidation - A function to be called after changes to the question to check the validators.

Validation

Question tiles can also support validation of their contents. These validators can be configured by the user constructing the form.

First we will need to import the following helper function

import { ValidateText } from 'Forms/Validators/ValidateText';

Now we can add the following fields

/**
 * The types of validators that can be used for this field.
 * These options must be covered in the validateFunction that is defined above.
 */
static validatorOptions: { display: string, value: ValidatorType }[] = [
    { display: 'Required', value: 'required' },
    { display: 'Email', value: 'email' },
    { display: 'Phone', value: 'phone' },
    { display: 'Custom', value: 'custom' },
];

/** The function used to validate the options. This must handle all validator options. */
static validateFunction = ValidateText;

The ValidateText function that we imported is currently being used for the text box question already and we are not reimplementing it for the sake of brevity. However, any function that takes a validator and a json object and returns either a string or undefined can be used.

Show Conditions

Show conditions are used for conditionally displaying question tiles based on some condition. For instance if you have a checkbox you can conditionally show or hide other fields based on weather checkbox is ticked or not.

To add the ability for other questions to show or hide themselves based off the value in our text area, first we will need to add the following import.

import CompareText from 'Forms/Conditions/CompareText';

Now we can add the following fields to our question tile.

/**
 * The options that are used for the show conditions.
 * These options must be covered
 */
static conditionOptions = [
    { display: 'Equal', value: 'equal' },
    { display: 'Not equal', value: 'notEqual' },
    { display: 'Contains', value: 'contains' },
];

/**
 * The function used to compare the value for the show conditions. This function is the one that is used by the
 * text box since we are using the same same condition options. However any function can be used here.
 */
static compareFunction = CompareText;

Again the CompareText function is reused from the text box question tile, however any function can be provided.

Tile Options

When a question tile is placed on the form builder, a sidebar is provided to a place for options to be displayed.

First we will need to add another import

import TileOptions from '../TileOptions';

Now we can control the options that are displayed to the user by replacing the render function of the TextAreaQuestionTileOptions with the following.

public render() {
    const { question, schema } = this.props;

    // The tile options component is used to provide the default options, such as question name, validators ect.
    // You can add extra things to this render function to have more options, or remove the tile options component
    // entirely to provide your entirely custom configuration.
    return (
        <TileOptions question={question} schema={schema} hasShowConditions hasValidators hasTooltip />
    );
}

The TileOptions component will provide us with all the stock options for a forms question tile. This includes editing the title of the question, as well as support for editing the show conditions, validators and tooltip if they are enabled.

Other custom fields can be added in this function to add further functionality. These inputs can modify the question prop to edit any field on the question definition. The schema prop is used to inspect the rest of the form in case any options need to cross reference another question.

Viewing the New Question

Now the code is done we can now view the new question tile. To do this go to the form designer for any form and try to add a new question.

The forms dropdown with the new question tile listed

As you can see there is now a text area question listed with all of the built in ones. After selecting the item in the dropdown we can see it appear on the form designer.

A preview of the forms question tile