Skip to content

Commit

Permalink
fix: handle reactive field names and value swaps
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Nov 5, 2020
1 parent 539f753 commit cf8051d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 13 deletions.
3 changes: 2 additions & 1 deletion packages/vee-validate/src/Field.ts
Expand Up @@ -34,6 +34,7 @@ export const Field = defineComponent({
},
setup(props, ctx) {
const rules = toRef(props, 'rules');
const name = toRef(props, 'name');

const {
errors,
Expand All @@ -48,7 +49,7 @@ export const Field = defineComponent({
reset,
meta,
checked,
} = useField(props.name, rules, {
} = useField(name, rules, {
validateOnMount: props.validateOnMount,
bails: props.bails,
type: ctx.attrs.type as string,
Expand Down
24 changes: 14 additions & 10 deletions packages/vee-validate/src/useField.ts
Expand Up @@ -39,12 +39,15 @@ interface FieldOptions {

type RuleExpression = MaybeReactive<string | Record<string, any> | GenericValidateFunction>;

let ID_COUNTER = 0;

/**
* Creates a field composite.
*/
export function useField(name: string, rules: RuleExpression, opts?: Partial<FieldOptions>) {
export function useField(name: MaybeReactive<string>, rules: RuleExpression, opts?: Partial<FieldOptions>) {
const fid = ID_COUNTER >= Number.MAX_SAFE_INTEGER ? 0 : ++ID_COUNTER;
const { initialValue, validateOnMount, bails, type, valueProp, label, validateOnValueUpdate } = normalizeOptions(
name,
unref(name),
opts
);

Expand All @@ -58,7 +61,7 @@ export function useField(name: string, rules: RuleExpression, opts?: Partial<Fie
valueProp,
});

const nonYupSchemaRules = extractRuleFromSchema(form?.schema, name);
const nonYupSchemaRules = extractRuleFromSchema(form?.schema, unref(name));
const normalizedRules = computed(() => {
return normalizeRules(nonYupSchemaRules || unref(rules));
});
Expand All @@ -73,7 +76,7 @@ export function useField(name: string, rules: RuleExpression, opts?: Partial<Fie
bails,
});
} else {
result = (await form.validateSchema())[name];
result = (await form.validateSchema())[unref(name)];
}

meta.pending = false;
Expand Down Expand Up @@ -111,6 +114,7 @@ export function useField(name: string, rules: RuleExpression, opts?: Partial<Fie
}

const field = {
fid,
name,
value: value,
meta,
Expand Down Expand Up @@ -223,14 +227,14 @@ function useValidationState({
type,
valueProp,
}: {
name: string;
name: MaybeReactive<string>;
initValue?: any;
form?: FormContext;
type?: string;
valueProp: any;
}) {
const errors: Ref<string[]> = ref([]);
const initialValue = getFromPath(unref(inject(FormInitialValues, undefined)), name) ?? initValue;
const initialValue = getFromPath(unref(inject(FormInitialValues, undefined)), unref(name)) ?? initValue;
const { reset: resetFlags, meta } = useMeta(initialValue);
const value = useFieldValue(initialValue, name, form);
if (hasCheckedAttr(type) && initialValue) {
Expand Down Expand Up @@ -343,21 +347,21 @@ function extractRuleFromSchema(schema: Record<string, any> | undefined, fieldNam
/**
* Manages the field value
*/
function useFieldValue(initialValue: any, path: string, form?: FormContext) {
function useFieldValue(initialValue: any, path: MaybeReactive<string>, form?: FormContext) {
// if no form is associated, use a regular ref.
if (!form) {
return ref(initialValue);
}

// set initial value
setInPath(form.values, path, initialValue);
setInPath(form.values, unref(path), initialValue);
// otherwise use a computed setter that triggers the `setFieldValue`
const value = computed({
get() {
return getFromPath(form.values, path);
return getFromPath(form.values, unref(path));
},
set(newVal: any) {
form.setFieldValue(path, newVal);
form.setFieldValue(unref(path), newVal);
},
});

Expand Down
31 changes: 29 additions & 2 deletions packages/vee-validate/src/useForm.ts
@@ -1,4 +1,4 @@
import { computed, ref, Ref, provide, reactive, onMounted, isRef, watch, unref } from 'vue';
import { computed, ref, Ref, provide, reactive, onMounted, isRef, watch, unref, nextTick } from 'vue';
import type { ObjectSchema, ValidationError } from 'yup';
import type { useField } from './useField';
import {
Expand Down Expand Up @@ -60,6 +60,9 @@ export function useForm<TValues extends Record<string, any> = Record<string, any

// a private ref for all form values
const formValues = reactive({}) as TValues;
// a lookup to keep track of values by their field ids
// this is important because later we need it if fields swap names
const valuesByFid: Record<string, any> = {};

// an aggregation of field errors in a map object
const errors = computed<{ [P in keyof TValues]?: string }>(() => {
Expand Down Expand Up @@ -128,6 +131,9 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
const idx = newVal.indexOf(value);
idx >= 0 ? newVal.splice(idx, 1) : newVal.push(value);
setInPath(formValues, field as string, newVal);
fieldInstance.forEach(fieldItem => {
valuesByFid[fieldItem.fid] = newVal;
});
return;
}

Expand All @@ -138,6 +144,9 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
}

setInPath(formValues, field as string, newValue);
if (fieldInstance) {
valuesByFid[fieldInstance.fid] = newValue;
}
}

/**
Expand Down Expand Up @@ -210,6 +219,19 @@ export function useForm<TValues extends Record<string, any> = Record<string, any

function registerField(field: FieldComposite) {
fields.value.push(field);
if (isRef(field.name)) {
// ensures when a field's name was already taken that it preserves its same value
// necessary for fields generated by loops
watch(
field.name,
newPath => {
setFieldValue(newPath, valuesByFid[field.fid]);
},
{
flush: 'post',
}
);
}
}

function unregisterField(field: FieldComposite) {
Expand All @@ -219,7 +241,12 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
}

fields.value.splice(idx, 1);
const fieldName = field.name;
const fid = field.fid;
// cleans up the field value from fid lookup
nextTick(() => {
delete valuesByFid[fid];
});
const fieldName = unref(field.name);
// in this case, this is a single field not a group (checkbox or radio)
// so remove the field value key immediately
if (field.idx === -1) {
Expand Down

0 comments on commit cf8051d

Please sign in to comment.