Skip to content

Add computed fields ? #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
leplatrem opened this issue Mar 3, 2016 · 20 comments
Closed

Add computed fields ? #45

leplatrem opened this issue Mar 3, 2016 · 20 comments
Labels

Comments

@leplatrem
Copy link
Contributor

Would it be the scope of this library to be able to specify small routines to compute default values of fields ?

Our use-cases is to be able to set the current date/time in a (hidden) field.

@Natim
Copy link
Contributor

Natim commented Mar 3, 2016

I remember this was done in the formbuilder to compute the field id from the label.

See: https://github.com/Kinto/formbuilder/blob/8ceae7585108c3fe8d0ed64882f9d856cf2ef9e7/formbuilder/components/EditableField.js#L43-L62

@n1k0
Copy link
Collaborator

n1k0 commented Mar 3, 2016

Do you guys have a dream API in mind for this here?

@Natim
Copy link
Contributor

Natim commented Mar 3, 2016

UISchema set to something like:

{
   "slug": {"populate_from": "title"},
}

Where slug and title are JSONSchema properties maybe?

populate_from could also be slugify_from

@leplatrem
Copy link
Contributor Author

After discussion:

  • We should keep this library as minimalist and focused as possible
  • For the created date use-case, it is possible to use the onSubmit() function to alter the final object
  • For the slug use-case, the idea would be to introduce a onFieldChange hook that can alter the field value (or other fields)
      <Form schema={schema}
            uiSchema={uiSchema}
            onFieldChange=(name, value, data) => {
               switch(name) {
                  case 'title':
                    return {...data, [name]: value, slug: slugify(value)};
                  default:
                    return {...data: [name]: value}
               }
            } />

@n1k0
Copy link
Collaborator

n1k0 commented Mar 20, 2016

onFieldChange=(name, value, data) => {

To be noted, we should allow intercepting any deeply nested updates here.

@apkoponen
Copy link
Contributor

Jdorns Json-editor uses the templates for this:

https://github.com/jdorn/json-editor#templates

@n1k0
Copy link
Collaborator

n1k0 commented Apr 24, 2016

Hmm, nice though most often you're likely to want a little more than just templating... functions are probably better for advanced computations :)

@apkoponen
Copy link
Contributor

Yup, templates do have their limitations.

@maartenth
Copy link
Contributor

maartenth commented Nov 3, 2016

onFieldChange=(name, value, data) => {

Could I help with implementing this feature? I'm currently working on a project where this would be very welcome due to better integration possibilities with Redux reducers.

How do you see this in combination with the current onChange() function? Would all fields call onFieldChange instead, or both? And which one would come first?

@maartenth
Copy link
Contributor

@n1k0 Bumping this question because you probably missed my comment above. :)

@n1k0
Copy link
Collaborator

n1k0 commented Nov 14, 2016

@maartenth Form onChange should work as before, but reflect computed field values in formData.

As mentioned before, the trickiest part imo is to find a nice API for hooking into deeply nested fields. Using strings to identify these isn't an option in my world. All I can see atm is to introduce yet another new hooks prop which would be an object registering nested hook functions:

const schema = {
  type: "object",
  properties: {
    foo: {
      type: "object",
      properties: {
        bar: {type: "string"}
      }
    }
  }
} 

const hooks = {
  foo: {
    bar: (value) => value + "!"
  }
}

render(<Form schema={schema} hooks={hooks}/>)

We'll have to be careful with precedence here though. This should also work with array items as well.

Thoughts? /cc @leplatrem @olzraiti @frassinier and people interested

@maartenth
Copy link
Contributor

Thank you for your reply and your thoughts. I will try to find a more elegant API.

@knilink
Copy link
Contributor

knilink commented Mar 3, 2017

How about

//SchemaField.js  
function getFieldComponent(schema, uiSchema, fields) {
   //....
   return (uiSchema["ui:enhancer"] || a=>a)(FieldComponent);
}
import propsMapper from "recompose/propsMapper";
//...
const uiSchema = {
  title: {
    'ui:enhancer': propsMapper(({value, onChange, ...rest})=>({
      onChange: value => onChange(slugify(value)),
      value: unslugify(value),
      ...rest
    }))
  }
};

For this case, I personally prefer simply

import propsMapper from "recompose/propsMapper";
import TextWidget from "react-jsonschema-form/TextWidget";

const uiSchema = {
  title: {
    'ui:widget': propsMapper(({value, onChange, ...rest})=>({
      onChange: value => onChange(slugify(value)),
      value: unslugify(value),
      ...rest
    }))(TextWidget)
  }
};

@n1k0
Copy link
Collaborator

n1k0 commented Mar 3, 2017

It would feel odd to add data processing logic to uiSchema, which has always been intended to deal with presentation only.

Also, I'm still pretty much convinced that post-processing form data could (and should) be achieved in the form's onSubmit event handler.

@elisechant
Copy link
Contributor

@n1k0 I would agree. I normalize payload before post. Or you might be able to use a controlled component to compute inline?

I feel like these solutions are pretty heavy on perf?

@acouch
Copy link

acouch commented May 27, 2017

Anyone find a decent solution for this? If I intercept the state change similar to https://github.com/Kinto/formbuilder/blob/8ceae7585108c3fe8d0ed64882f9d856cf2ef9e7/formbuilder/components/EditableField.js#L43-L62 my slug field is always one character behind.

@OliverColeman
Copy link

@acouch I was having a similar issue (I think). The values of some fields on my form are constrained by others - eg can only add up to some maximum value. In Form.onChange I grab the formData and store it in some state. If I try modifying some of the values of the formData to satisfy the constraint before putting it into state then the other fields would always be one step behind. The solution for me was to do a deep clone of the formData property before mutating it. Potentially inefficient, but effective.

@lovato
Copy link

lovato commented Mar 2, 2019

@OliverColeman I am having the VERY SAME issue you had. And your suggestion fixed my problem. Thanks man. "would always be one step behind".

@stale
Copy link

stale bot commented Apr 15, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Please leave a comment if this is still an issue for you. Thank you.

@stale stale bot added the wontfix label Apr 15, 2022
@stale stale bot closed this as completed May 1, 2022
@Matnard
Copy link

Matnard commented May 24, 2024

This is how I got it to work.

export const CustomObjectField = (props: FieldProps) => {
  const {
    schema,
    uiSchema,
    idSchema,
    formData,
    errorSchema,
    onChange,
    onBlur,
    onFocus,
    registry,
    disabled,
    readonly,
    name,
  } = props;

  const { fields } = registry;
  const { ObjectField } = fields;

  return (
    <ObjectField
      schema={schema}
      uiSchema={uiSchema}
      idSchema={idSchema}
      formData={formData}
      errorSchema={errorSchema}
      onChange={(obj) => {
        const newObj = structuredClone(obj);

        newObj["total"] =
          newObj["a"] +
          newObj["b"] +
          newObj["c"] +
          newObj["d"];

        onChange(newObj);
      }}
      onBlur={onBlur}
      onFocus={onFocus}
      registry={registry}
      disabled={disabled}
      readonly={readonly}
      name={name}
    />
  );
};

Then in the uiSchema apply "ui:field": CustomObjectField where you need

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests