ui icon indicating copy to clipboard operation
ui copied to clipboard

form.value.setErrors is undefined

Open hckhanh opened this issue 1 year ago • 3 comments

Environment

Version

3.12.2

Reproduction

I try to follow this example on docs:

<script setup lang="ts">
import type { FormError, FormSubmitEvent } from '#ui/types'

const state = reactive({
  email: undefined,
  password: undefined
})

const form = ref()

async function onSubmit (event: FormSubmitEvent<any>) {
  form.value.clear()
  try {
    const response = await $fetch('...')
    // ...
  } catch (err) {
    if (err.statusCode === 422) {
      form.value.setErrors(err.data.errors.map((err) => ({
        // Map validation errors to { path: string, message: string }
        message: err.message,
        path: err.path,
      })))
    }
  }
}
</script>

<template>
  <UForm ref="form" :state="state" @submit="onSubmit">
    <UFormGroup label="Email" name="email">
      <UInput v-model="state.email" />
    </UFormGroup>

    <UFormGroup label="Password" name="password">
      <UInput v-model="state.password" type="password" />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

Description

Is there anything is out of dated compare to the latest version of nuxt?

Additional context

Screenshot 2024-06-29 at 11 51 54

Logs

No response

hckhanh avatar Jun 29 '24 04:06 hckhanh

Hello! It looks like you're trying to use the function before the component is mounted:

form.value?.setErrors(/* ... */); // This will be undefined since the component is not mounted

onMounted(() => {
  form.value.setErrors(/* ... */); // This will be defined
});

See: https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs

romhml avatar Jun 29 '24 14:06 romhml

The onSubmit function is called after the component is mounted

hckhanh avatar Jun 29 '24 16:06 hckhanh

Can you send me a reproduction using Stackblitz so I can have a look? I can't reproduce the issue from the documentation's example

romhml avatar Jun 29 '24 16:06 romhml

hi, I run in the same problem. do your response have a path? My solution was to set it manually

const swwError = ref(false); //somethingWentWrongError
const form = ref();
const onSubmit = async (event) => {
  try {
    const data = await authStore.create(event.data);
    router.push("/");
  } catch (error) {
    //email exists already
    if (error.response.status === 422) {
      form.value.setErrors([
        {
          message: error.response.data.message,
          path: "email",
        },
      ]);
    } else {
    // For all other errors, a standard error message is displayed at the top of the form
      swwError.value = true;
    }
  }
    ```

gregorvoinov avatar Sep 04 '24 10:09 gregorvoinov

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Oct 10 '24 01:10 github-actions[bot]

@hckhanh , did you find a solution for this? I am encountering the same issue

daniosoriov avatar Feb 07 '25 14:02 daniosoriov

@romhml , is there any chance this can be re-opened? I can provide my code, although is basically the same problem that @hckhanh encountered. Thank you

daniosoriov avatar Feb 11 '25 12:02 daniosoriov

Is this on v2? Can you send me a reproduction using stackblitz or codesandbox? I can check it out

romhml avatar Feb 11 '25 12:02 romhml

I can't find a way to reproduce it from the original example: https://stackblitz.com/edit/nuxt-ui-vcf3cr5i?file=app.vue

romhml avatar Feb 11 '25 12:02 romhml

Thank you for replying @romhml . I will try to recreate it. This is my stack:

While I set up the stackblitz, I will share a snippet of my code:

<script setup lang="ts">

import * as v from 'valibot'
import type { Form, FormSubmitEvent } from '#ui/types'
// import { isEmailAvailable } from '~/api/user'

const PROMPT_MAX_CHARACTERS = 800

const createStorySchema = v.objectAsync({
  email: v.optionalAsync(
    v.pipeAsync(
      v.string(),
      v.trim(),
      v.email('Please enter a valid email'),
      // v.checkAsync(isEmailAvailable, 'This email is already in use')
    )
  ),
  prompt: v.pipe(v.string(), v.maxLength(PROMPT_MAX_CHARACTERS)),
})

type CreateStorySchema = v.InferOutput<typeof createStorySchema>

const form = ref<Form<CreateStorySchema>>()

const state = reactive({
  email: undefined,
  prompt: '',
})

const remainingCharacters = computed(() => {
  return PROMPT_MAX_CHARACTERS - state.prompt.length
})

const doSomething = (data: CreateStorySchema) => {
  // Do something
  console.log('data', data)
  return {
    success: false,
    errors: [{
      path: 'email',
      message: 'This is an error message',
    }]
  }
}

const onSubmit = async (event: FormSubmitEvent<CreateStorySchema>) => {
  form.value!.clear()

  // Attempt to create the story
  const result = doSomething(event.data)
  if (result.success) {
    // Do something
  } else {
    if (form.value && result.errors) {
      // It will never enter here...
      form.value.setErrors(result.errors)
    }
  }
}
</script>

<template>
  <UForm
    ref="form"
    :schema="v.safeParserAsync(createStorySchema, { abortEarly: true })"
    :state="state"
    :validate-on="[ 'submit', 'blur' ]"
    @submit="onSubmit"
  >
    <UFormGroup label="Email address" name="email" required>
      <UInput
        v-model.trim="state.email"
        placeholder="Enter your email address"
        type="email"

      />
    </UFormGroup>

    <UFormGroup
      label="Label"
      description="Description"
      name="prompt"
      required
      :help="`${remainingCharacters}/${PROMPT_MAX_CHARACTERS} characters left`"
      eager-validation
    >
      <UTextarea
        v-model.trim="state.prompt"
        placeholder="For example: This is my prompt"
        :rows="5"
        padded
        resize
        :maxlength="PROMPT_MAX_CHARACTERS"
      />
    </UFormGroup>

    <UButton type="submit">
      Submit
    </UButton>
  </UForm>
</template>

daniosoriov avatar Feb 11 '25 14:02 daniosoriov