RXNT / react-jsonschema-form-conditionals

react-jsonschema-form-conditionals

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

help wanted: Am I using this lib correctly with my react component?

OleksiL opened this issue · comments

Hello.

I use react-jsonschema-form and need to extend it with more complex conditions, so, I decided to use this extension.

Below is my FormRenderer component where I used to render Form component where FormWithConditionals is now.

I struggle with two things:

  1. In order to use it I have to define a const within my FormRenderer component as shown in the example. So, I do this:
    FormWithConditionals = applyRules(this.jsonSchema, this.jsonLayout, this.rules_simplified, Engine)(Form);

Then I render it like this:

<this.FormWithConditionals
                formData={this.state.formData}
                className="form-renderer"
                noHtml5Validate
                liveValidate
                onChange={this.onChange}
                FieldTemplate={FieldTemplate}
                ArrayFieldTemplate={ArrayTemplate}
                showErrorList={false}
                formContext={{ formError: this.state.error }}
                // uiSchema={this.props.jsonLayout}
                // schema={this.props.jsonSchema}
                onSubmit={this.props.showSubmitButton ? this.onSubmit : undefined}
                onError={this.onError}
                widgets={Widgets}
                fields={Fields}
                omitExtraData
                liveOmit
                transformErrors={this.suppressEmptyOptionalObjectError}
                validate={this.validateEmptyRequiredObjectFields}
              >

It looks weird to me: I need to use <**this.**FormWithConditionals> in order to use it. I didn't find a way to have it in a separate file as a standalone component/wrapper and then being imported and used because I don't know how to provide the rest of regular Form props to it (onChange, liveValidation and so on..). My attempt below obviously fails because when I try to use it in FormRenderer where Form used to be it complains about all the rest of usual Form parameters. Does it mean that defining it as a const and then using with <this.FormWithConditionals is the only way to do it?

interface IFormWithConditionalsProps {
  jsonSchema: ISchema;
  jsonLayout: ILayout;
  rules: any;
}

const FormWithConditionals = (props: IFormWithConditionalsProps) => applyRules(props.jsonSchema, props.jsonLayout, props.rules, Engine)(Form);

export default FormWithConditionals
  1. My second issue is that with the approach above I cannot use shcema and layout from props. Notice that when I create the component in applyRules method I use constants for schema and uiSchema instead of props as I used to do before for Form component. I had to do it this way because when I tried to get them from props this.props.jsonSchema and this.props.jsonLayout rule engine failed at validation step. It seems that at initial rendering they are provided with empty values, and rule engine fails at rules validation because it cannot find the field which I refer in rule's remove event. When I use hardcoded constants instead of props with the same values as in props - it works becuse everything is provided at once. Could you suggest any solution for this?

Full code:

import React from 'react';

import FieldTemplate from './field-template';
import ArrayTemplate from './array-template';
import { MuiPickersUtilsProvider } from '@material-ui/pickers';
import DateFnsUtils from '@date-io/date-fns';
import { Button, Grid } from '@material-ui/core';
import { ArrowForward } from '@material-ui/icons';
import { ILayout, ISchema } from 'app/modules/content-management/spaces/space/form-wizard/form-tab/form-builder/model/schema-model';
import Widgets from 'app/modules/spaces/forms/form-renderer/fields/index';
import Fields from 'app/modules/spaces/forms/form-renderer/fields/custom-object-field/index';
import { excludeFields, orderFormData } from 'app/shared/util/form-data';
import FormHeader from 'app/modules/spaces/forms/form-header';
import {
  flattenDependencies,
  isGroupField
} from 'app/modules/content-management/spaces/space/form-wizard/form-tab/form-builder/model/field-model-util';
import { FormModel } from 'app/modules/content-management/spaces/space/form-wizard/form-tab/form-builder/model/form-model';
import { FieldModel } from 'app/modules/content-management/spaces/space/form-wizard/form-tab/form-builder/model/abstract/field-model';
import Form from 'react-jsonschema-form';
import applyRules from 'react-jsonschema-form-conditionals';
import Engine from 'json-rules-engine-simplified';
import FormWithConditionals from './form-with-conditionals';

export interface IFormRendererProps {
  jsonSchema: ISchema;
  jsonLayout: ILayout;
  showSubmitButton: boolean;
  spaceAlias: string;
  formAlias: string;
  isPreview?: boolean;
  formData: object;

  onSubmit(data): void;
}

export interface IFormRendererState {
  error: boolean;
  formData: object;
  sending: boolean;
}

interface IReactJsonSchemaError {
  name: string;
  params: {
    missingProperty?: string;
  };
  property: string;
}

class FormRenderer extends React.Component<IFormRendererProps, IFormRendererState> {
  rules_simplified = [{
    conditions: {
      'bd9d6a94-19cc-4bfd-8836-00e75dd445ad': {equal: 'aaa'}
    },
    event: {
      type: "remove",
      params: {
        field: '3c9a0dd9-97af-4dc8-b83d-0380c958f3f1'
      },
    }
  }];

  jsonSchema = {
    "type" : "object",
    "required" : [ "bd9d6a94-19cc-4bfd-8836-00e75dd445ad" ],
    "properties" : {
      "bd9d6a94-19cc-4bfd-8836-00e75dd445ad" : {
        "title" : "text field",
        "type" : "string"
      },
      "3c9a0dd9-97af-4dc8-b83d-0380c958f3f1" : {
        "title" : "text field2",
        "type" : "string"
      }
    }
  };

  jsonLayout = {
    "ui:order" : [ "bd9d6a94-19cc-4bfd-8836-00e75dd445ad", "3c9a0dd9-97af-4dc8-b83d-0380c958f3f1" ],
    "bd9d6a94-19cc-4bfd-8836-00e75dd445ad" : {
      "ui:widget" : "text",
      "ui:disabled" : false,
      "ui:options" : { }
    },
    "3c9a0dd9-97af-4dc8-b83d-0380c958f3f1" : {
      "ui:widget" : "text",
      "ui:disabled" : false,
      "ui:options" : { }
    }
  };

  FormWithConditionals = applyRules(this.jsonSchema, this.jsonLayout, this.rules_simplified, Engine)(Form);

  constructor(props: IFormRendererProps) {
    super(props);

    this.state = {
      error: false,
      sending: false,
      formData: props.formData ?? {}
    };
  }

  onError = () => {
    this.setState({ error: true });
  };

  onChange = data => this.setState({ formData: data.formData });

  onSubmit = async data => {
    this.setState({ sending: true });
    await this.props.onSubmit(orderFormData(excludeFields(data.uiSchema, data.formData), data.uiSchema['ui:order']));
  };

  render() {
    const isPreview = this.props.isPreview ?? false;
    return (
      <>
        {isPreview ? null : (
          <FormHeader formData={this.state.formData} formAlias={this.props.formAlias} spaceAlias={this.props.spaceAlias} />
        )}
        <Grid container>
          <Grid item xs={1} lg={3} />
          <Grid item xs={isPreview ? 12 : 10} lg={isPreview ? 12 : 6}>
            <MuiPickersUtilsProvider utils={DateFnsUtils}>
              <this.FormWithConditionals
                formData={this.state.formData}
                className="form-renderer"
                noHtml5Validate
                liveValidate
                onChange={this.onChange}
                FieldTemplate={FieldTemplate}
                ArrayFieldTemplate={ArrayTemplate}
                showErrorList={false}
                formContext={{ formError: this.state.error }}
                // uiSchema={this.props.jsonLayout}
                // schema={this.props.jsonSchema}
                onSubmit={this.props.showSubmitButton ? this.onSubmit : undefined}
                onError={this.onError}
                widgets={Widgets}
                fields={Fields}
                omitExtraData
                liveOmit
              >
                {this.props.showSubmitButton ? (
                  <Button aria-label="Submit" className="form-renderer-submit-btn" type="submit" disabled={this.state.sending}>
                    Send
                    <ArrowForward fontSize="large" className="ml-2" />
                  </Button>
                ) : (
                  <br />
                )}
              </this.FormWithConditionals>
            </MuiPickersUtilsProvider>
          </Grid>
        </Grid>
      </>
    );
  }
}

export default FormRenderer;