如何访问 Vue 3 + Typescript + Composition 中嵌套 v-for 的引用 API

How to access refs for a nested v-for in Vue 3 + Typescript + Composition API

我正在尝试使用 Typescript 将一些 Vue2 转换为 Vue3 组合 API。 Refs 的工作方式似乎不同,我不确定如何让它在 Vue3 中工作。在 Vue 2 中,它非常简单 this.$refs[docForm.value.fields[i].name][0].focus()

我不能只使用文档中描述的方法,您只需将 ref 放在 v-for 上并通过索引访问它,因为当我需要访问它时,我只会知道字段名称,而不是索引顺序(长话短说)。

<div v-for="(p, i) in docForm.pages">
    <div v-for="(f, i) in p.fields">
        <input type="text" :name="f.name" :ref="f.name" />
    </div>
</div>
const goToNextEmptyField = (): void => {
    if (docForm.value) {
        for (var i = 0; i < docForm.value.fields.length; i++) {
            if (docForm.value.fields[i].value == null || docForm.value.fields[i].value == '') {
               refs[docForm.value.fields[i].name][0].focus() //THIS LINE NEEDS FIXING - WORKED IN Vue2
               return
            }
        }
    }
}

docForm 对象的结构是这样的(简化):

export interface DocForm {
    pages: DocFormPageModel[]
    fields: DocFieldModel[]
}

export interface DocFormPageModel {
    fields: DocFormField[]
    ...
}

export interface DocFieldModel {
    name: string
    ....
}

在 Vue 3 中你需要声明引用。

试试这个:

import { ref } from 'vue'

const inputs = ref([]);
// This is a guess at TS implementation
// const inputs : Ref<HTML element[]> = ref([]);

const goToNextEmptyField = (): void => {
    if (docForm.value) {
        for (var i = 0; i < docForm.value.fields.length; i++) {
            if (docForm.value.fields[i].value == null || docForm.value.fields[i].value == '') {
               inputs.value[docForm.value.fields[i].name][0].focus()
               return
            }
        }
    }
}

这是在初始化一个反应对象,该对象在检测到 refs 时填充。

另请注意,您始终需要在脚本(而不是模板)中通过 value 属性 引用 ref

这在 Vue 文档中有解释。 https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for

确保您按下了“合成 API”切换开关!

Danial Storey 让我走上了正确的道路,这是可行的解决方案:

<script setup lang="ts">
import { ref, onMounted } from 'vue'
  
interface DocForm {
    pages: DocFormPageModel[]
    fields: DocFieldModel[]
}

interface DocFormPageModel {
    fields: DocFormField[]
}

interface DocFieldModel {
    name: string
    value: string
}

const docForm = ref<DocForm>(
  {pages: [
    { fields: [
      { name: "test1", value: "1", masterFieldIndex: 0 },
      { name: "test2", value: "", masterFieldIndex: 1 },
      { name: "test3", value: "", masterFieldIndex: 2 },
    ]}
  ], fields: [
    { name: "test1", value: "1", masterFieldIndex: 0 },
    { name: "test2", value: "", masterFieldIndex: 1 },
    { name: "test3", value: "", masterFieldIndex: 2 },
  ]}
)

const pageRefs = ref<HtmlElement[]>([])
const fieldRefs = ref<HtmlElement[]>([])

const goToNextEmptyField = (): void => {
    if (docForm.value) {
        for (var i = 0; i < docForm.value.fields.length; i++) {
            if (docForm.value.fields[i].value == null || docForm.value.fields[i].value == '') {
               var inputToFocus = fieldRefs.value.filter((f)=>{ 
                   return f.children[0].name == docForm.value.fields[i].name
               })
               inputToFocus[0].children[docForm.value.fields[i].name].focus()
               return
            }
        }
    }
}
</script>

<template>
    <div v-for="p in docForm.pages">
      <div v-for="f in p.fields" ref="fieldRefs">
        <input type="text" v-model="docForm.fields[f.masterFieldIndex].value" :name="f.name" />
      </div>
    </div><br />
  <button @click="goToNextEmptyField">
    Go to on First Empty Field
  </button>
</template>

您可以将上面的代码here粘贴到运行中。