Open bkniffler opened 7 years ago
i think you can use isFieldsTouched and getFieldsValue to do this.
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.
isFieldsTouched can receive params,like a array of field names. please read api doc carefully
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);
};
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
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
getTouchedFields(): [String]
=> Returns array of changed field-keys like ['user.name', 'comment']
getTouchedValues(): Object
=> Returns object with changes like { user: { name: 'XY' }, comment: 'xy' }
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.
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
.
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?
@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.
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..
Maybe later, but I am not going to implement this feature recently.
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?
I'm also facing the same issue.
Do we have any update on this ?
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 ?
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.
As @jwmann I'm longing the same pristine feature. Any news on this?
@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.
thanks for the feedback @jwmann, definitely gonna look into it.
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 functionisFieldsTouched
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 isisFieldsTouched
supposed to return false after a form is submitted ?
Any updates? Did you find any solutions?
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.
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...
+1 isDirty
fn
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)
+2 isDirty
fn
@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;
};
There's still no getTouchedFields and/or dirty flag in 2023? Cmon... this is so basic.
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.
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.