react-component / form

React High Order Form Component(web & react-native)
http://react-component.github.io/form/
MIT License
1.81k stars 296 forks source link

Get all fields / touched fields #82

Open bkniffler opened 7 years ago

bkniffler commented 7 years ago

Hey, could we expose a function that returns all fields and/or all touched fields? Or maybe an option to validateFields { touchedOnly: true } to only return those that were touched?

Would be pretty cool to only save actually changed fields through a patch method to the server instead of all values, even those that didn't change.

paranoidjk commented 7 years ago

i think you can use isFieldsTouched and getFieldsValue to do this.

bkniffler commented 7 years ago

Could you please elaborate? isFieldsTouched returns true/false if something changed and getFieldsValue returns field values.

How can I get all the fields that changed? This should be really common in case that you don't want to have classic PUT/POST, but PATCH methods that will only send the changed form data.

Imagine a form with tabs and 30 fields. If only the "name" field changed and the save button is clicked, I'd like to run a method that will return ["name"] or have a possibility to implement that without needing to attach onChange on every single form item.

paranoidjk commented 7 years ago

isFieldsTouched can receive params,like a array of field names. please read api doc carefully

bkniffler commented 7 years ago

Okay, since I'm not sure if I'm making myself clear, here an example: https://www.webpackbin.com/bins/-KlSiBx4TBghlGriZJF9

Just change the field "name" to something like "Name1" and click save. It works, but only due to

const transform = (obj, arr = [], parent = '') => {
  if (!obj) return [];
  if (parent) parent = `${parent}.`;
  Object.keys(obj).forEach(key => {
    const fullKey = `${parent}${key}`;
    if (obj[key] && typeof obj[key] === 'object') {
      transform(obj[key], arr, fullKey);
    } else {
      arr.push(fullKey);
    }
  });
  return arr;
};
const onClick = (form) => () => {
  const value = form.getFieldsValue();
  // Get an array of changed fields
  const arr = transform(form.getFieldsValue()).filter(x => form.isFieldTouched(x));
  // Get the final object with only changed keys
  const final = form.getFieldsValue(arr);
  console.log(final);
};
  1. Is there a better way to achieve what I'm trying to do? 1 b. If not: Wouldn't it more sense to add "getChangedFields" to return an array of the changed fields and/or "getChangedFieldValues" that returns an object with only the changes applied (like my above example) to the form? This shouldn't be too hard since that stuff is already present in the form state, isn't it?
paranoidjk commented 7 years ago

Sorry, i was wrong,isFieldsTouched can not achieve your requirements. isFieldsTouched(names: String[]): Bool: Whether one of the inputs' values had been change.

But you can do it by use isFieldTouched to loop all your fileds.

cc @benjycui

bkniffler commented 7 years ago

Yes, thats what I'm doing right now, but it would be better if we had an easy to use function instead of what I'm doing in the example above :)

I suggest

agonbina commented 7 years ago

I also ran into this issue just now actually, I want to know if any of the values for any of the input fields within the form has changed or not. Is touched will be true as soon as there is some interaction with the input field, but that doesn't mean that the field is "dirty". Basically there is no easy way to know if one(or more/all) of your fields are dirty. This is useful when you want to enable a submit button only if the form is "dirty".

The suggested getChangedFields() method by @bkniffler would suffice for my use case. Or having an explicit form.isDirty() => boolean method which calls into getChangedFields would be even better :)

BTW this is pretty common requirement for a piece of UI when using forms. While you could do all kinds of deep equality checks to accomplish this, it feels pretty dirty. Moreover I assume that there is a map in rc-form which is already keeping track of this stuff.

benjycui commented 7 years ago

isFieldsTouched and isFieldTouched is for https://ant.design/components/form/#components-form-demo-horizontal-login

If you want to know which fields are changed, you can diff final fields and initialValue.

bkniffler commented 7 years ago

Diffing isn't nearly as good a solution as exposing the functions. You are already storing the dirty state of each field, why not expose the methods then? I really don't understand why we should redundantly diff the data. I also see a few problems with diffing (using multiple sources for initialValue, having a ton of fields that need to be diffed, diffing nested fields / deep-diffing).

Please consider exposing the functions, I suppose its only a few lines of code necessary.

Also, stating that isFieldsTouched/isFieldTouched is only for one specific use-case is strange. Why do you store the dirty state per field then instead of one dirty state for the whole form, if its only to disable the save button?

agonbina commented 7 years ago

@benjycui keep in mind that there is also a performance hit if you rely on diffing(ex on every render). Moreover doing deep equality checks of JS maps is tricky and can result in inconsistent outcomes.

benjycui commented 7 years ago

Actually, rc-form can not get all the dirty fields easily now. Because the data structure of nested fields. So, rc-form need to traverse the whole tree to find out dirty fields..

benjycui commented 7 years ago

Maybe later, but I am not going to implement this feature recently.

jwmann commented 6 years ago

I'm also having a bit of frustration with this.

Why aren't we saving a global form dirty or pristine (a la Redux Form)

Having this isFieldsTouched is nice but if a user changes a field but then reverts it, is still counts as "touched" even though the form is actually "pristine" (or not dirty).

This should be a built in feature no?

AshuDeshpande commented 5 years ago

I'm also facing the same issue.

Do we have any update on this ?

Maikel-Nait commented 5 years ago

In my case, I'm using isFieldsTouched to detect if any form field has been changed. That seems to work fine. The problem is that, when I click on a "save" submit button, I expect the function isFieldsTouched to return false (since all the form items have been submitted). But I'm always receiving true on that function, no matter if we submit the form or not. How is isFieldsTouched supposed to return false after a form is submitted ?

ahouck commented 5 years ago

This is also pertinent to my use case where I want to prevent user input loss by wrapping the antd Form with my own definition that tracks if any of the fields are dirty. It would be useful to be able to retrieve references to the fields from the form without having to know the names, or to get a general status if any of the fields are dirty.

wiltfm commented 5 years ago

As @jwmann I'm longing the same pristine feature. Any news on this?

jwmann commented 5 years ago

@wilkmoura I had opted to totally give up on using the included rc-form in favour of using Formik combined with Formik-antd that I personally worked on.

It works quite well and I actually prefer it over rc-form now.

wiltfm commented 5 years ago

thanks for the feedback @jwmann, definitely gonna look into it.

vixeven commented 4 years ago

In my case, I'm using isFieldsTouched to detect if any form field has been changed. That seems to work fine. The problem is that, when I click on a "save" submit button, I expect the function isFieldsTouched to return false (since all the form items have been submitted). But I'm always receiving true on that function, no matter if we submit the form or not. How is isFieldsTouched supposed to return false after a form is submitted ?

Any updates? Did you find any solutions?

mikeaustin commented 4 years ago

I'm looking to simply disable the submit button until the form has values and is validated. I'm looking for something like form.validateFields(), but only returning data, checking untouched fields, and not showing errors.

spencer741 commented 3 years ago

I created an issue pertaining to this very thing plus more... Ant Design - issue #29316 ... also, I don't really see any reason why most form functionality couldn't be ported ... it would make for a more flexible end product and promote clean code.

I have also always wanted to see a feature to disable all of the fields in a form, this is typical functionality for forms ... especially when you provide avalidateFields promise, which returns the values on resolve ... it would be nice to do a form.DisableAll(); while waiting for validation to complete.

Honestly, I could probably throw together a complete issue summarizing all of this stuff on the rc-form repo, linking here and other places. I would be open to personally working on the features if someone could give me a little help.

Like some others mentioned, rc-form has fallen behind on some nice features like this...

menosprezzi commented 3 years ago

+1 isDirty fn

error404as commented 2 years ago

Faced same issue - need to validate only touched fields. My workaround is:

const touchedFields = Object.keys(form.getFieldsValue()).filter((el) => form.isFieldTouched(el));
form.validateFields(touchedFields)
doraemonxxx commented 2 years ago

+2 isDirty fn

rmagon commented 2 years ago

@error404as It works if it's a simple form, but for more hierarchical forms I'm using what @bkniffler suggested a few years back :) Though I have modified it a little to support Form.List as well

import { FormInstance } from 'antd';

const transform = (obj: any, arr: any[][] = [], parent: any[] = []) => {
  if (!obj) return [];
  Object.keys(obj).forEach((key) => {
    const fullKey = parent.concat(key);
    if (obj[key] && typeof obj[key] === 'object') {
      transform(obj[key], arr, fullKey);
    } else {
      arr.push(fullKey);
    }
  });
  return arr;
};

export const useTouchedFields = (form) => () => {
  // Get an array of changed fields
  const arr = transform(form.getFieldsValue()).filter((x) => {
    return form.isFieldTouched(x);
  });
  // Get the final object with only changed keys
  const final = form.getFieldsValue(arr);
  return final;
};
clmz commented 1 year ago

There's still no getTouchedFields and/or dirty flag in 2023? Cmon... this is so basic.

chillyistkult commented 1 year ago

Yep there is no way in rc-form to figure out if values really changed compared to initialValues. And on top onValuesChange and onFieldsChange is not triggered when calling setFieldValue or setFieldsValue which makes it even harder.

What we need is a global form state that exposes touched, dirty, pristine etc. like any other form library is doing it.