Skip to content
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

useFieldParam #4742

Open
3dyuval opened this issue May 6, 2024 · 0 comments
Open

useFieldParam #4742

3dyuval opened this issue May 6, 2024 · 0 comments

Comments

@3dyuval
Copy link

3dyuval commented May 6, 2024

Is your feature request related to a problem? Please describe.

Yes, the feature request is related to the problem of synchronizing form field values with URL parameters. Currently, in vee-validate, there is no built-in way to bind form field values to URL parameters. This can be frustrating when trying to create a form where the state needs to be preserved in the URL, such as in a filter form on a search page.

Describe the solution you'd like

The solution is a composable function useFieldParam that provides two-way binding between URL parameters and a vee-validate field. It uses the useField function from vee-validate and the useUrlSearchParams function from @vueuse/core to achieve this. This function takes the field name, validation rules, and options as arguments and returns a reactive object representing the vee-validate field, with two-way binding to the corresponding URL parameter.

Here's a rough implementation of this composable:

import { FieldContext, FieldOptions, RuleExpression, useField } from 'vee-validate';
import { useUrlSearchParams } from '@vueuse/core';
import { MaybeRef, MaybeRefOrGetter, onMounted, reactive, toValue, watch } from 'vue';
import { maybeNumberOrString } from '@/utils/numbers';

type Param = string | string[] | undefined
type ExpandedField = { init: () => void, name: string}

type UseFieldParam<TValue = unknown> = (path: MaybeRefOrGetter<string>,
                                        rules?: MaybeRef<RuleExpression<TValue>>,
                                        opts?: Partial<FieldOptions<TValue>>) => FieldContext<TValue> & ExpandedField;

/**
 * `useFieldParam` is a composable function that provides two-way binding between URL parameters and a vee-validate field.
 * It uses the `useField` function from vee-validate and the `useUrlSearchParams` function from @vueuse/core to achieve this.
 *
 * @param args - The arguments to pass to the `useField` function. These can include the field `name` which is what will be bound to searchParams, validation rules, and options.
 * @returns FieldContext<TValue> & { init: () => void }
 */
export const useFieldParam: UseFieldParam = (...args): ReturnType<UseFieldParam<unknown>> => {

  const [key, rules = [], options = {}] = args;

  const initialValue = toValue(options.initialValue);
  const name = toValue(key);

  const searchParams = useUrlSearchParams<any>('hash');

  const field = reactive(useField<Param>(
    name,
    (rules as any), {
      ...(options as any),
      initialValue: searchParams[name] || initialValue
    })
  ) as any;

  /**
   * `init`: Resets the field and url search param to initial value
   */
  field.init = () => field.setValue(initialValue as any, true);

  onMounted(() => {
    if (field.value && !searchParams[name]) {
      searchParams[name] = field.value;
    }
  });

  watch(() => field.value, (val, old) => {
    if (name !== undefined && val !== old) {
      searchParams[name] = val;
    }
  });

  watch(searchParams, (val) => {
    if (val !== field.value) {
      field.setValue(maybeNumberOrString(val[name]));
    }
  });

  return field as any;
};


Describe alternatives you've considered

An alternative solution could be to manually watch the field values and update the URL parameters accordingly, and vice versa. However, this can lead to a lot of repetitive code if there are many fields that need to be synchronized with URL parameters. The useFieldParam composable abstracts this logic away and makes it reusable, leading to cleaner and more maintainable code.

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

No branches or pull requests

1 participant