Tech Blog

Conditional Forms in AEK 2

Forms are one of the most advanced components in the AEK library, allowing you to combine data and frontend components with ease. But working with them isn’t always straightforward. I’ve created an example that goes beyond what the docs provide to help people get started with the Form component. You can find them near the end of this post.

I’ve used the SimpleStore tool when creating this project – it’s a personal favourite of mine. You can achieve the same results using React’s component state, you just need to shift the objects we’re using in the Store into the Screen’s local state. It’s easy to get started with as well; it’s included as a template option when creating a new React16 project. You can also add it in manually by creating a Store file, setting it up, and importing it where you need it throughout your project. The template does this for you, as well as providing a simple example of how to use it.

How It Works

Similar to how React re-renders upon a component state change, SimpleStore is setup to force a new render every time a change is detected to the store’s state. It reads the default values for each form from SimpleStore when the page is loaded – be careful not to set these to null. Once the page has loaded, any subsequent updates by the end user are pushed to the Store via the onChange handler (for either Form).

Using this, we always have a snapshot of the state of either Form at any specific point in time. We can build the structure of the second Form using the state of the first – or vice-versa! In this case we’re both basing a required flag as well as the validation method on the state of the Checkbox in the first form.

How To Use

These files are complete replacements for screen.js and store.js (found in any React16 project created with the SimpleStore template chosen). You can drop them into a new project and work from there, or you can use them as reference. Feel free to extend the base state of the form fields in store.js – you don’t have to start from just two empty objects. You can use the LoggerPlugin (provided) when running the code locally to see what the Form is doing.

screen.js

import React from "react";
import {
  Container,
  VBox,
  BannerHeader,
  BasicSegment,
  Form,
  Button
} from "@ombiel/aek-lib";

import {
  extend,
  isEmpty
} from "lodash";

import {
  store
} from "./modules/store";

export default class Screen extends React.Component {
  componentDidMount() {
    store.on("change",()=>{
      this.forceUpdate();
    });
  }

  firstFormOnChange = (e, name, value) => {
    const storeTargetPath = "forms.firstFormData";
    const formData = extend({}, store.get(storeTargetPath), {[name]: value});
    store.set(storeTargetPath, formData);
  }

  secondFormOnChange = (e, name, value) => {
    const storeTargetPath = "forms.secondFormData";
    const formData = extend({}, store.get(storeTargetPath), {[name]: value});
    store.set(storeTargetPath, formData);
  }

  validateSecondForm = () => {
    const {firstFormData} = store.get("forms");
    if (firstFormData.confirm) {
      let valid = false;
      const messages = {};
  
      const {secondFormData} = store.get("forms");
      if (isEmpty(secondFormData.exampleTwo) || isEmpty(secondFormData.textarea)) {
        valid = false;
        if (isEmpty(secondFormData.exampleTwo)) {
          messages.exampleTwo = "This can't be empty";
        }

        if (isEmpty(secondFormData.textarea)) {
          messages.textarea = "This can't be empty";
        }
      }
  
      return {valid, messages};
    }

    return null;
  }

  render() {
    const {
      firstFormData,
      secondFormData
    } = store.get("forms");

    const firstFormFields = [
      {name: "example"},
      {name: "confirm", type: "checkbox"}
    ];

    const validation = this.validateSecondForm();
    const secondFormFields = [
      {name: "exampleTwo", label: "Example Two"},
      {name: "textarea", label: "Text Area", required: firstFormData.confirm},
      [<Button type="submit" fluid variation="secondary" disabled={firstFormData.confirm && !validation.valid}>Submit</Button>]
    ];

    return (
      <Container>
        <VBox>
          <BannerHeader theme="alt" key="header" data-flex={0}>Conditional Form</BannerHeader>
          <BasicSegment>
            <Form
              data={firstFormData}
              fields={firstFormFields}
              onChange={this.firstFormOnChange}
            />
          </BasicSegment>
          <BasicSegment>
            <Form
              data={secondFormData}
              fields={secondFormFields}
              onChange={this.secondFormOnChange}
              validation={validation}
            />
          </BasicSegment>
        </VBox>
      </Container>
    );

  }

}

store.js

import {
  SimpleStore,
  loggerPlugin
} from "@ombiel/aek-lib/simple-store";

export class Store extends SimpleStore {
  constructor() {
    super({
      initialState: {
        forms: {
          firstFormData: {},
          secondFormData: {}
        }
      },
      plugins: [
        loggerPlugin()
      ],
    });
  }
}

const store = new Store();
export {store};

Further Reading

Have any questions or comments? Feel free to leave them below.

 

Leave a Reply