具有功能模板引用的动态 v-for 中的空值

null value in dynamic v-for with functional template refs

情况

我正在构建自定义过滤组件。这允许用户应用在模板中显示为 v-forn 过滤器。用户可以更新输入字段中的任何值或之后删除任何过滤器。

问题

删除其中一个过滤器后,我的数组 itemRefs 得到一个 null 值作为最后一项。

代码(简体)

<script setup>
const filtersScope = $ref([])
const itemRefs = $ref([])

function addFilter () {
  filtersScope.push({ value: '' })
}

function removeFilter (idx) {
  filtersScope.splice(idx, 1)
  itemRefs.pop() // <- necessary? has no effect
  // validate and emit stuff
  console.log(itemRefs)
  // itemRefs got at least one null item
  // itemRefs = [null]
}

// assign the values from the input fields to work with it later on
function updateValue() {
  itemRefs.forEach((input, idx) => filtersScope[idx].value = input.value)
}
</script>

<template>
  <div v-for="(filter, idx) in filtersScope" :key="filter.id">
    <input 
      type="text" 
      @keyup="updateValue" 
      :ref="(input) => { itemRefs[idx] = input }" 
      :value="filter.value"
    >
    <button @click="removeFilter(idx)" v-text="'x'" />
  </div>
  <button @click="addFilter()" v-text="'add filter +'" />
</template>

>>> Working demo

重现:

  1. 添加两个过滤器
  2. itemRefs 现在将模板引用作为参考,例如:[input, input]
  3. 删除一个过滤器,itemRefs 现在看起来:[input, null]
  4. 删除最后一个过滤器,itemRefs 现在看起来像:[null]

问题

在没有 itemRefs.pop() 的情况下,删除并应用新过滤器后出现以下错误:

Uncaught TypeError: input is null

使用 pop() 方法我防止了控制台错误,但 itemRefs 中的 null 值仍然存在。

如何彻底清理模板引用?

我不知道在 $refs 中使用 $refs 是怎么回事,但它显然没有像人们预期的那样工作。

但是,您永远不需要嵌套 $refs。改变数据时,改变外部 $refs。使用 $computed 获取该数据的 simplified/focused angle/slice。

这里是a working example

<script setup>
  const filtersScope = $ref([])
  const values = $computed(() => filtersScope.map(e => e.value))

  function addFilter() {
    filtersScope.push({ value: '' })
  }

  function removeFilter(idx) {
    filtersScope.splice(idx, 1);
    console.log(values)
  }
</script>
<template>
  <div v-for="(filter, idx) in filtersScope" :key="idx">
    <input type="text" 
           v-model="filtersScope[idx].value">
    <button @click="removeFilter(idx)" v-text="'x'" />
  </div>
  <button @click="addFilter()" v-text="'add filter +'" />
</template>