ulteam-react FormApp
FormApp component
Generate your form with controls from office-ui-fabric-react package. Configure form fields and component aggregates all field values in one plain object.
Demo
Try component on the demo
Sample
import { PrimaryButton } from 'office-ui-fabric-react/lib/components/Button/PrimaryButton/PrimaryButton';
import { IPersonaProps } from 'office-ui-fabric-react/lib/components/Persona/Persona.types';
import { ITag } from 'office-ui-fabric-react/lib/components/pickers/TagPicker/TagPicker.types';
import * as React from 'react';
import { FormApp, FormFields, IErrorField, IFormConfig, IFormField } from '../../../..';
import { INumberFieldProps } from '../../../../form/data/FieldTypes';
import { IFormFieldDisabled, IFormFieldHidden } from '../../../../form/data/Interfaces';
import { ITestFormPageState } from './ITestFormPageState';
import { Toggle } from 'office-ui-fabric-react/lib/components/Toggle/Toggle';
import { Icon } from 'office-ui-fabric-react/lib/components/Icon/Icon';
export interface ISampleFields {
title?: string;
numberField?: number;
doubleNumberField?: number;
datePickerField?: Date;
booleanField: boolean;
name?: string;
choiceField?: number;
peoplePickerField?: IPersonaProps[];
pickerField?: ITag[];
conditionTextField?: string;
}
export class TestFormPage extends React.Component<{}, ITestFormPageState> {
private currentFormFields: any = {};
constructor(props: {}, state: ITestFormPageState) {
super(props);
this.currentFormFields = this.getDefaultFormFields();
this.state = {
addCustomLabel: false,
disablePicker: true,
formFields: this.getDefaultFormFields()
};
}
public shouldComponentUpdate(nextProps: {}, nextState: ITestFormPageState) {
return nextState.addCustomLabel !== this.state.addCustomLabel ||
nextState.disablePicker !== this.state.disablePicker ||
nextState.formFields !== this.state.formFields;
}
public render() {
console.log('state', this.state);
return (
<div className="form">
<PrimaryButton
text="Clear field values"
onClick={this.handleClearFieldClick}
/>
<div>
<h1>Labels on the top</h1>
<FormApp key={1}
config={this.getFormConfig()}
customClass="custom-class"
fieldValues={this.state.formFields}
handleFieldValues={this.handleChangeFieldValues}
checkRequired={false} />
</div>
<p></p>
<Toggle label="Add custom label html to 'Title' field" onText="On" offText="Off"
onChange={this.handleAddCustomTable}
defaultChecked={this.state.addCustomLabel}
/>
<div>
<h1>Labels on the left</h1>
<FormApp key={2}
config={this.getFormConfig(this.state.addCustomLabel)}
fieldValues={this.state.formFields}
handleFieldValues={this.handleChangeFieldValues}
labelsOnTheLeft={true}
checkRequired={false} />
</div>
<p></p>
<div>
<h1>Required fieds</h1>
<FormApp key={3}
config={this.getFormConfig()}
fieldValues={this.state.formFields}
handleFieldValues={this.handleChangeFieldValues}
checkRequired={true} />
</div>
</div>
);
}
private addLabelHtml = (config: IFormField<any>, fieldValue?: any, formId?: number | undefined): JSX.Element | undefined => {
return (
<Icon iconName="Info" title="Custom label html" style={ {fontSize: 16} } />
)
}
private getDefaultFormFields(): ISampleFields {
return {
booleanField: false,
choiceField: 10,
doubleNumberField: 2,
datePickerField: new Date(2000, 1, 1),
conditionTextField: '',
name: 'test name',
numberField: 1,
peoplePickerField: [{
id: '1',
text: 'User name 1',
secondaryText: 'Job title 1'
}],
pickerField: [{
key: 'Key 2',
name: 'Value 2'
}],
title: 'test title'
};
}
public async getPeopleList(): Promise<IPersonaProps[]> {
const result: IPersonaProps[] = [];
await this.delay(1000);
for (let i = 0; i < 20; i++) {
const item: IPersonaProps = {
id: i.toString(),
text: `User name ${i}`,
secondaryText: `Job title ${i}`
};
result.push(item);
}
return result;
}
public onChangeNumberField = (
config: IFormField<INumberFieldProps>,
newValue: any,
prevValue: any,
prevFormFieldValues: ISampleFields
): ISampleFields => {
let result: ISampleFields = prevFormFieldValues;
result.numberField = newValue;
if (!isNaN(newValue)) {
result.doubleNumberField = newValue * 2;
}
return result;
}
public disabledPickerField = (
config: IFormField<any>,
newValue: boolean,
prevValue: boolean,
newFormFieldValues: ISampleFields,
disabledMap: IFormFieldDisabled[]): IFormFieldDisabled[] => {
disabledMap
.filter(x => x.name == 'pickerField')[0]
.disabled = !newValue;
return disabledMap;
}
public hideConditionTextField = (
config: IFormField<any>,
newValue: boolean,
prevValue: boolean,
newFormFieldValues: ISampleFields,
hiddenMap: IFormFieldHidden[]): IFormFieldHidden[] => {
hiddenMap
.filter(x => x.name == 'conditionTextField')[0]
.hidden = !newValue;
return hiddenMap;
}
public numberValidation = (config: IFormField<any>, newValue: any): string | undefined => {
return isNaN(newValue) ?
'Value should be a number.' :
undefined;
}
public numberFieldError = (config: IFormField<any>, newValue: any, newFormFieldValues: any, errorField: IErrorField): IErrorField => {
if (newFormFieldValues && newFormFieldValues.booleanField === true && newValue === '') {
errorField.isError = true;
errorField.message = "Hey!! I'm empty!!"
}
return errorField;
}
public getFormConfig(addCustomLabel?: boolean): IFormConfig {
return {
groups: [{
expand: true,
hideHeader: true,
title: 'Common',
fields: [
FormFields.TextField('title', {
label: 'Title',
onRenderLabel: addCustomLabel === true ? this.addLabelHtml : undefined,
labelsOnTheLeftClassName: 'labelsOnTheLeft-class',
customClass: 'customClass-class'
},
{
required: true,
description: 'field description',
errorMessage: 'Required field!!!',
// value: defaultValues.title
}),
FormFields.NumberField('numberField', {
label: 'Source number',
// hidden: this.currentFormFields.title !== 'test',
errorOnChange: this.numberFieldError,
onChange: this.onChangeNumberField,
validationErrorOnChange: this.numberValidation
},
{
useCommaDelimiter: true,
autoValidation: true,
round: 2
}),
FormFields.NumberField('doubleNumberField', {
label: 'Double number',
// disabled: true,
// validationErrorOnChange: this.numberValidation
},
{
autoValidation: true,
round: 0
// value: defaultValues.doubleNumberField
}),
FormFields.PeoplePickerField('peoplePickerField',
{
label: 'People picker'
},
{
itemLimit: 1,
onResolveSuggestions: this.peoplePickerOnResolve,
resolveDelay: 300,
required: true,
errorMessage: 'Required field!!!',
suggestionProps: {
noResultsFoundText: 'No items',
loadingText: 'Searching...',
suggestionsHeaderText: 'Searching results'
}
}),
FormFields.DatePickerField('datePickerField',
{
label: 'Date picker label'
},
{
description: 'description',
errorMessage: 'Required field!!!',
placeholder: 'Placeholder text...',
required: true,
officeUIProps: {
isMonthPickerVisible: false
}
}),
FormFields.BooleanField('booleanField', {
label: 'your confirmation',
hideOnChange: this.hideConditionTextField,
disabledOnChange: this.disabledPickerField
},
{
// value: defaultValues.booleanField
}),
FormFields.TextField('conditionTextField', {
label: 'Condition Text Field',
hidden: true
},
{
// value: defaultValues.conditionTextField
}),
FormFields.PickerField('pickerField', {
disabled: this.state.disablePicker,
label: 'Picker field',
customClass: 'customClass'
},
{
itemLimit: 1,
noResultsFoundText: 'No results',
suggestionsHeaderText: 'Results',
errorMessage: 'Required field!!!',
placeholder: 'Type for start search',
required: true,
resolveDelay: 300,
openItemsOnEmptyFocus: true,
// value: [{
// key: 'Key 2',
// name: 'Value 2'
// }],
options: [
{
key: 'Key 1',
name: 'Value 1'
},
{
key: 'Key 2',
name: 'Value 2'
},
{
key: 'Key 3',
name: 'Value 3'
}
]
}),
FormFields.ChoiceField('choiceField', {
label: 'Choice'
},
{
// value: defaultValues.choiceField,
required: true,
errorMessage: 'Required field!!!',
placeholder: 'Select a value',
options: [
{
key: 1,
text: 'Text 1'
},
{
key: 10,
text: 'Text 10'
},
{
key: 3,
text: 'Text 3'
}
]
}),
FormFields.ChoiceField('multiChoiceField', {
label: 'Multi Choice'
},
{
placeholder: 'Select a value',
multiSelect: true,
options: [
{
key: 1,
selected: true,
text: 'Text 1'
},
{
key: 10,
text: 'Text 10'
},
{
key: 3,
text: 'Text 3'
}
]
})
]
},
{
title: 'Others',
fields: [
FormFields.TextField('name', {
label: 'Name'
},
{
// value: defaultValues.name,
multiline: true,
multilineAutoHeight: true,
multilineResizable: true,
multilineRows: 3,
errorMessage: 'Required field!!!',
required: true
})
]
}
]};
}
private handleAddCustomTable = (event: React.MouseEvent<HTMLElement, MouseEvent>, checked?: boolean | undefined) => {
this.setState({ addCustomLabel: checked });
}
private handleChangeFieldValues = (formFields: any, errorFields: IErrorField[]) => {
// const fields: ISampleFields = formFields as ISampleFields;
this.currentFormFields = formFields;
this.setState({
formFields
});
console.log(this.currentFormFields);
console.log('errorFields', errorFields);
}
private handleClearFieldClick = () => {
const emptyValues: ISampleFields = {
booleanField: false
}
this.setState({ formFields: emptyValues });
}
private peoplePickerOnResolve = (
filterText: string,
currentPersonas: IPersonaProps[],
limitResults?: number
): IPersonaProps[] | Promise<IPersonaProps[]> => {
if (filterText.length > 2) {
return this.getPeopleList();
}
else {
return [];
}
}
public async delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
}
IFormAppProps
Property Name | Required | Type | Comments |
---|---|---|---|
asTableRow | Optional |
boolean | Table row form view |
checkRequired | boolean | Add error message if required value is empty | |
config | IFormConfig | Form field configuration | |
currentFieldId | Optional |
string | Current editing field. Used for UlTable optimization |
customClass | Optional |
string | Add custom class to main div |
errorType | Optional |
FormFieldErrorType | Field error type |
fieldNavByArrows | Optional |
boolean | Add field navigation by keyboard arrows and enter buttons. Using in UlTable component |
fieldValues | Optional |
any | Form fields as key value pairs. If value is undefined then form render with values from form field configuration (IFormAppProps.config) |
formId | Optional |
number | Form id. Its value will be return in handleFieldValues function |
handleFieldNav | Optional |
function | Fires if in control press arrow or enter buttons |
handleFieldOnClick | Optional |
function | Fires if user clicked on any field control |
handleFieldValues | function | Fires if any field value has changed | |
handleOnSelection | Optional |
function | WARNING: It’s prop using in UlTable. Handle on selection row in UlTable |
isFixed | Optional |
boolean | Indicate than form is fixed column in UlTable |
isSelected | Optional |
boolean | WARNING: it’s prop using only in UlTable component. Whether form row is selected or not. |
isSelectionMode | Optional |
boolean | WARNING: it’s prop using only in UlTable component. Set selection mode: single or multiple |
isShimmer | Optional |
boolean | Show Office UI Shimmer component instead of field |
labelsOnTheLeft | Optional |
boolean | If true then control label will be moved from the top of the control to the left. |
onComponentMount | Optional |
function | Get form metadata with error fields after mount Use this if you need to know info about error fields directly after mount component |
readView | Optional |
boolean | Form on read mode |