Отсутствует необходимая опора: «значение» в привязке модели/значения для пользовательского компонента ввода в Vue

#vue.js #data-binding #custom-controls #vuejs3 #vue-composition-api

Вопрос:

Я следовал этому руководству, пытаясь создать пользовательский компонент формы в Vue 3 (api композиции, script setup режим).

Когда я загружаю страницу, содержащую мой компонент, я получаю предупреждение консоли, подобное этому:

[Предупреждение Vue]: Отсутствует необходимая опора: «значение» в <ключ управления коммутатором=0 имя=»вопрос-8″ модель=не определена … >

Мой компонент (CSS опущен):

 <template>
  <input ref="switchElement"
         v-bind="$attrs"
         class="gui-switch"
         @input="value = !value; emit('update:modelValue', value)"
         type="checkbox"
         role="switch"
         :value="value" />
</template>

<script setup lang="ts">
import { defineEmit, defineProps, onMounted, ref } from "vue"


const props = defineProps<{
  value: boolean | undefined,
}>()

const emit = defineEmit<{
  (e: "update:modelValue", value: boolean | undefined): void,
}>()

const switchElement = ref<HTMLInputElement>()

onMounted(() => switchElement.value!.indeterminate = true)
</script>
 

Страница, содержащая его, использует его так:

 <!-- v-for question in questions -->
<switch-control :name="`question-${question.id}`"
             :model="feedbackData[`question-${question.id}`]"
             :id="`question-${question.id}`" />
 

Я пробовал различные вещи, такие как изменение имени создаваемого события на input или использование v-model вместо :model этого, но мне еще не удалось это исправить, и я не знаю, что еще можно попробовать.

ИЗМЕНИТЬ: Редактирование компонента для использования modelValue таким образом:

 <template>
  <input ref="switchElement"
         v-bind="$attrs"
         class="gui-switch"
         @input="modelValue = !modelValue; emit('update:modelValue', modelValue)"
         type="checkbox"
         role="switch"
         :value="modelValue" />
</template>

<script setup lang="ts">
import { defineEmit, defineProps, onMounted, ref } from "vue"


const props = defineProps<{
  modelValue: boolean | undefined,
}>()

const emit = defineEmit<{
  (e: "update:modelValue", value: boolean | undefined): void,
}>()

const switchElement = ref<HTMLInputElement>()

onMounted(() => switchElement.value!.indeterminate = true)
</script>
 

Parent:

 <!-- v-for question in questions -->
<switch-control :name="`question-${question.id}`"
             v-model="feedbackData[`question-${question.id}`]"
             :id="`question-${question.id}`" />
 

Leads to an outright error:

 [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next
at <SwitchControl key=0 name="question-8" modelValue=undefined  ... > 

Uncaught (in promise) TypeError: invalid 'instanceof' operand type
    assertType runtime-core.esm-bundler.js:1877
    validateProp runtime-core.esm-bundler.js:1841
    validateProps runtime-core.esm-bundler.js:1817
    initProps runtime-core.esm-bundler.js:1548
    setupComponent runtime-core.esm-bundler.js:6500
    mountComponent runtime-core.esm-bundler.js:4206
    processComponent runtime-core.esm-bundler.js:4182
    patch runtime-core.esm-bundler.js:3791
    mountChildren runtime-core.esm-bundler.js:3975
 

EDIT2:
I managed to zoom in on whereabouts the problem is, but I still can’t quite figure out what’s happening.

I changed the component so that @input is now @input="emit('update:modelValue', !modelValue)" . I’ll include the relevant parts of the <script> of the page that includes it:

 import SwitchControl from "@/components/SwitchControl.vue"
import type { FeedbackQuestion } from "@/utils/api/story"
import { defineProps, ref } from "vue"


const props = defineProps<{
  questions: {id: number}[],
}>()

const defaultModelValues = {
  // These are hard-coded for debugging, ideally I'd want it to work with an empty initial object
  "question-8": null,
  "question-11": null,
}
const feedbackData = ref<Record<string, any>>(defaultModelValues)
 

Теперь симптомы:

  • Когда код выглядит так, как описано выше , с определением prop и emit boolean | undefined , я получаю следующую ошибку, и весь цикл for не отображается:
 [Vue warn]: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next 
  at <SwitchControl modelValue=null onUpdate:modelValue=fn<onUpdateModelValue> name="question-8"  ... >

Uncaught (in promise) TypeError: invalid 'instanceof' operand type
 
  • Если вместо этого я аннотирую опору и излучаю как просто boolean , элементы загружаются, и я получаю только предупреждение (см. Ниже). Если я затем попытаюсь изменить значение, нажав на элемент, я продолжу получать одно и то же предупреждение, и значение вообще не изменится, вместо чередования true и false, как и следовало ожидать. value Атрибут в HTML, если я его проверю, ведет себя правильно (изначально»», затем чередуется между «истиной» и «ложью»).
 [Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected Boolean, got Null  
  at <SwitchControl modelValue=null onUpdate:modelValue=fn<onUpdateModelValue> name="question-8"  ... >
 

Комментарии:

1. Я не могу воспроизвести проблему в этой демонстрации . Также обратите внимание, что реквизиты доступны только для чтения, поэтому ваш @input обработчик не должен пытаться переключаться modelValue .

2. @tony19 Пожалуйста, посмотрите ПРАВКУ 2 к вопросу, может быть, это намекнет на то, что я не рассматриваю. Спасибо, что нашли время до сих пор.

3. Я не могу воспроизвести проблему EDIT2 в этой демонстрации . Можете ли вы поделиться ссылкой на репродукцию?

4. Я создал эту демонстрационную версию, максимально точно скопировав структуру моего реального приложения, но она по-прежнему не выдает ошибок, даже если это делает мое приложение. У вас есть какие-либо предложения по поводу того, что может вызвать проблему? У меня заканчиваются вещи, которые я мог бы попробовать. Константа-это ошибка Uncaught (in promise) TypeError: invalid 'instanceof' operand type , но я не вызываю никаких обещаний, и трассировка стека нигде не указывает на мой собственный код, только на esm-пакет.

5. Ошибка исчезнет , только если я изменю modelValue тип для удаления undefined , но затем я получу предупреждение, потому что prop получил неопределенное значение вместо логического. Затем я должен жестко закодировать исходные значения модели, чтобы дать, и если я попытаюсь динамически вывести их из реквизитов следующим образом: feedbackData.value = Object.fromEntries(props.questions.map((q) => [ вопрос-${q.id} , false])) Я снова получаю предупреждения: результирующий объект регистрируется как полностью пустой, как props.questions и он сам. Если я зарегистрирую переданный объект, :questions он будет содержать ожидаемый массив вопросов…

Ответ №1:

В дочернем компоненте вы должны определить value как modelValue :

  <input ref="switchElement"
         ...
         :value="modelValue" />
</template>

.....
const props = defineProps<{
  modelValue : boolean | undefined,
}>()
 

и в родительском использовании v-model вместо :model :

   v-model="feedbackData[`question-${question.id}`]"
 

Комментарии:

1. Похоже, что это на правильном пути, но внесение этих изменений приводит к ошибке, из-за которой компонент и его родительский элемент теперь вообще не загружаются. Пожалуйста, проверьте правку исходного вопроса.