Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: unwrap initial value with useField.resetField fixes #3272 (#3274)
  • Loading branch information
logaretm committed Apr 21, 2021
1 parent 4949487 commit f6e9574
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/vee-validate/src/types.ts
Expand Up @@ -134,6 +134,7 @@ export interface FormContext<TValues extends Record<string, any> = Record<string
}>;
isSubmitting: Ref<boolean>;
handleSubmit(cb: SubmissionHandler<TValues>): (e?: Event) => Promise<void>;
setFieldInitialValue(path: string, value: unknown): void;
}

export interface PublicFormContext<TValues extends Record<string, any> = Record<string, any>>
Expand All @@ -147,6 +148,7 @@ export interface PublicFormContext<TValues extends Record<string, any> = Record<
| 'errorBag'
| 'setFieldErrorBag'
| 'stageInitialValue'
| 'setFieldInitialValue'
> {
errors: ComputedRef<FormErrors<TValues>>;
handleReset: () => void;
Expand Down
12 changes: 9 additions & 3 deletions packages/vee-validate/src/useField.ts
Expand Up @@ -335,9 +335,13 @@ function useValidationState<TValue>({
}) {
const { errors, errorMessage, setErrors } = useErrorsSource(name, form);
const formInitialValues = injectWithSelf(FormInitialValuesSymbol, undefined);
// clones the ref value to a mutable version
const initialValueRef = ref(unref(initValue)) as Ref<TValue>;

const initialValue = computed(() => {
return (getFromPath<TValue>(unref(formInitialValues), unref(name)) ?? unref(initValue)) as TValue;
return (getFromPath<TValue>(unref(formInitialValues), unref(name)) ?? unref(initialValueRef)) as TValue;
});

const value = useFieldValue(initialValue, name, form);
const meta = useMeta(initialValue, value, errors);

Expand Down Expand Up @@ -382,12 +386,14 @@ function useValidationState<TValue>({
const newValue =
state && 'value' in state
? (state.value as TValue)
: ((getFromPath<TValue>(unref(formInitialValues), fieldPath) ?? initValue) as TValue);
: ((getFromPath<TValue>(unref(formInitialValues), fieldPath) ?? unref(initValue)) as TValue);

if (form) {
form.setFieldValue(fieldPath, newValue, { force: true });
form.setFieldInitialValue(fieldPath, newValue);
} else {
value.value = newValue;
initialValueRef.value = newValue;
}

setErrors(state?.errors || []);
Expand Down Expand Up @@ -421,7 +427,7 @@ function useMeta<TValue>(initialValue: MaybeRef<TValue>, currentValue: Ref<TValu
validated: false,
initialValue: computed(() => unref(initialValue) as TValue | undefined),
dirty: computed(() => {
return !isEqual(currentValue.value, unref(initialValue));
return !isEqual(unref(currentValue), unref(initialValue));
}),
}) as FieldMeta<TValue>;

Expand Down
7 changes: 6 additions & 1 deletion packages/vee-validate/src/useForm.ts
Expand Up @@ -376,12 +376,16 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
};
};

function setFieldInitialValue(path: string, value: unknown) {
setInPath(initialValues.value, path, value);
}

/**
* Sneaky function to set initial field values
*/
function stageInitialValue(path: string, value: unknown) {
setInPath(formValues, path, value);
setInPath(initialValues.value, path, value);
setFieldInitialValue(path, value);
}

const schema = opts?.validationSchema;
Expand Down Expand Up @@ -412,6 +416,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
isSubmitting,
handleSubmit,
stageInitialValue,
setFieldInitialValue,
};

const immutableFormValues = computed<TValues>(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/vee-validate/tests/Field.spec.ts
Expand Up @@ -499,7 +499,7 @@ describe('<Field />', () => {
<span id="error">{{ errors && errors[0] }}</span>
<span id="touched">{{ meta.touched.toString() }}</span>
<span id="dirty">{{ meta.dirty.toString() }}</span>
<button @click="resetField({ value: '${resetValue}', dirty: true, touched: true, errors: ['${resetMessage}'] })">Reset</button>
<button @click="resetField({ value: '${resetValue}', touched: true, errors: ['${resetMessage}'] })">Reset</button>
</Field>
</div>
`,
Expand All @@ -521,7 +521,7 @@ describe('<Field />', () => {
await flushPromises();
expect(error.textContent).toBe(resetMessage);
expect(input.value).toBe(resetValue);
expect(dirty.textContent).toBe('true');
expect(dirty.textContent).toBe('false');
expect(touched.textContent).toBe('true');
});

Expand Down
68 changes: 68 additions & 0 deletions packages/vee-validate/tests/useField.spec.ts
Expand Up @@ -95,4 +95,72 @@ describe('useField()', () => {
await flushPromises();
expect(meta?.textContent).toBe('invalid');
});

test('dirty flag is false after reset', async () => {
mountWithHoc({
setup() {
const { value, meta, resetField } = useField('field', val => (val ? true : REQUIRED_MESSAGE));

return {
value,
meta,
resetField,
};
},
template: `
<input name="field" v-model="value" />
<span id="meta">{{ meta.dirty ? 'dirty' : 'clean' }}</span>
<button @click="resetField()">Reset</button>
`,
});

const input = document.querySelector('input') as HTMLInputElement;
const meta = document.querySelector('#meta');

await flushPromises();
expect(meta?.textContent).toBe('clean');

setValue(input, '');
await flushPromises();
expect(meta?.textContent).toBe('dirty');

// trigger reset
document.querySelector('button')?.click();
await flushPromises();
expect(meta?.textContent).toBe('clean');
});

test('dirty flag is false after reset with a new value', async () => {
mountWithHoc({
setup() {
const { value, meta, resetField } = useField('field', val => (val ? true : REQUIRED_MESSAGE));

return {
value,
meta,
resetField,
};
},
template: `
<input name="field" v-model="value" />
<span id="meta">{{ meta.dirty ? 'dirty' : 'clean' }}</span>
<button @click="resetField({ value: '12' })">Reset</button>
`,
});

const input = document.querySelector('input') as HTMLInputElement;
const meta = document.querySelector('#meta');

await flushPromises();
expect(meta?.textContent).toBe('clean');

setValue(input, '');
await flushPromises();
expect(meta?.textContent).toBe('dirty');

// trigger reset
document.querySelector('button')?.click();
await flushPromises();
expect(meta?.textContent).toBe('clean');
});
});

0 comments on commit f6e9574

Please sign in to comment.