3

I inherited a Kendo React subclass from a colleague. The subclass name is FormDropDownList.

It is a basic combination of a Label and a DropDownList

The value of the drop-down doesn't seem to get reflected in the Form values on submit.

Any ideas why?

Test Harness:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Field, Form, FormElement } from "@progress/kendo-react-form";
import { FormDropDownList } from "./FormDropDownList";

const handleSubmit1 = (dataItem) => {
  alert(JSON.stringify(dataItem));
};

const AppComponent = () => {

  const moduleData = [
    {
        description: 'Alpha',
        module: 1
    },
    {
        description: 'Beta',
        module: 2
    }
  ];

  return (
    <Form
      onSubmit={handleSubmit1}
     
      render={(formRenderProps) => (
        <FormElement>
          Name:
          <Field name="firstName" label="First name" 
          component="input" />

          <div className={"k-form-field-wrap"}>
                  <Field label={'Module'} 
                  name={'module'} 
                  id={'module'} 
                  required={true} 
                  dataItemKey={"module"} 
                  textField={"description"} 
                  data={moduleData} 
                  dropdownType={false} 
                  component={FormDropDownList} />
              </div>


          <button type={"submit"} disabled={false}>
            Submit
          </button>
        </FormElement>
      )}
    />
  );
};

ReactDOM.render(<AppComponent />, document.querySelector("my-app"));

Custom component:

import * as React from "react";
import { FieldWrapper } from "@progress/kendo-react-form";
import { Label, Error, Hint } from "@progress/kendo-react-labels";
import { DropDownList } from "@progress/kendo-react-dropdowns";
import axios from "axios";

export const FormDropDownList = (fieldRenderProps) => {
  const {
    validationMessage,
    touched,
    label,
    id,
    valid,
    disabled,
    hint,
    wrapperStyle,
    dropdownType,
    data,
    value,
    name,
    style,
    ...others
  } = fieldRenderProps;
  const {
    onChange,
    dataItemKey,
    textField,
    extra_param,
    extra_param_name
  } = others;

  const [state, setState] = React.useState([]);
  const [currentValue, setCurrentValue] = React.useState({});

  const loadDefaultData = (props) => {
    var cData = {};
    cData = props.find((obj) => obj[dataItemKey] === value);
    setCurrentValue(cData);
  };

  const onValueChange = (event) => {
    var cData = event.target.value; // selected data Object
    setCurrentValue(cData);
  };
 
  /**
   * Populate list
   */
  React.useEffect(() => {
 
      setState(data);
      loadDefaultData(data);
  
  }, []);

  const editorRef = React.useRef(null);
  const showValidationMessage = touched && validationMessage;
  const showHint = !showValidationMessage && hint;
  const hintId = showHint ? `${id}_hint` : "";
  const errorId = showValidationMessage ? `${id}_error` : "";
  const labelId = label ? `${id}_label` : "";
  return (
    <FieldWrapper style={wrapperStyle}>
      <Label
        id={labelId}
        editorRef={editorRef}
        editorId={id}
        editorValid={valid}
        editorDisabled={disabled}
      >
        {label}
      </Label>
      <DropDownList
        onChange={onValueChange}
        ariaLabelledBy={labelId}
        ariaDescribedBy={`${hintId} ${errorId}`}
        ref={editorRef}
        valid={valid}
        id={id}
        name={name}
        value={currentValue}
        disabled={disabled}
        data={state}
        dataItemKey={dataItemKey}
        textField={textField}
        style={style}
      />
      {showHint && <Hint id={hintId}>{hint}</Hint>}
      {showValidationMessage && <Error id={errorId}>{validationMessage}</Error>}
    </FieldWrapper>
  );
};
Black
  • 5,023
  • 6
  • 63
  • 92

2 Answers2

1

The value of the drop-down doesn't seem to get reflected in the Form values on submit. Any ideas why?

You are getting the kendo-react-form onChange method here:

  const {
    onChange,
    dataItemKey,
    textField,
    extra_param,
    extra_param_name
  } = others;

The onChange declared here ( = others.onChange) is the function that updates the value for the submit. However your are sending onValueChange to the DropDownList

<DropDownList
  onChange={onValueChange}

And the onValueChange function doesn't call the onChange method from the kendo-react-form anywhere (so the form values are not changing in any way, since you are only setting a local state. This is basically why value of the drop-down doesn't get reflected in the Form values on submit)


Potential fix

You shouldn't need any state in FormDropDownList, but rather just send the default props provided by the form to the dropdown. So something like:

import * as React from "react";
import { FieldWrapper } from "@progress/kendo-react-form";
import { Label, Error, Hint } from "@progress/kendo-react-labels";
import { DropDownList } from "@progress/kendo-react-dropdowns";
import axios from "axios";

export const FormDropDownList = (fieldRenderProps) => {
  const {
    validationMessage,
    touched,
    label,
    hint,
    wrapperStyle,
    dropdownType,
    extra_param,
    extra_param_name
    ...others
  } = fieldRenderProps;

  const editorRef = React.useRef(null);
  const showValidationMessage = touched && validationMessage;
  const showHint = !showValidationMessage && hint;
  const hintId = showHint ? `${id}_hint` : "";
  const errorId = showValidationMessage ? `${id}_error` : "";
  const labelId = label ? `${id}_label` : "";
  return (
    <FieldWrapper style={wrapperStyle}>
      <Label
        id={labelId}
        editorRef={editorRef}
        editorId={id}
        editorValid={valid}
        editorDisabled={disabled}
      >
        {label}
      </Label>
      
      {/* the props that are not explicitly defined bellow 
      will be transmitted trough {...others} part (ex: value, data, etc.) */}
      <DropDownList
        ariaLabelledBy={labelId}
        ariaDescribedBy={`${hintId} ${errorId}`}
        ref={editorRef}
        {...others}
      />
      {showHint && <Hint id={hintId}>{hint}</Hint>}
      {showValidationMessage && <Error id={errorId}>{validationMessage}</Error>}
    </FieldWrapper>
  );
};

Please note the above example is for giving you a hint and it might need small adjustments (without a minimal reproducible example I was not able to fully test your code and therefore neither this one.)

However the option with {...others} should work. You can also check a reproducible example with Kendo React DropDownList and the {...others} approach here

Berci
  • 2,876
  • 1
  • 18
  • 28
0

Building on the answer provided by @Berci...

The custom component needs a way to translate between the Objects selectable in the DropDown list, and a 'primitive' Form parameter value, and this needs to be updated whenever the DropDown selection is changed.

The way to achieve this is to have a callback triggered in the DropDown change event handler:

const onValueChange = (event) => {
    var cData = event.target.value; // selected data Object
    setCurrentValue(cData);
    onFormValueChange(cData);
};


/* translate between dropdown Object value and Form parameter value */
const onFormValueChange = React.useCallback((item) => {
    // onChange callback expects argument with 'value' property
    onChange({
        value: item[dataItemKey]
    });
}, [onChange, value]);
Black
  • 5,023
  • 6
  • 63
  • 92