Vu3.x如何給v-for迴圈出來的輸入框繫結v-mode的值,以及實現父子元件傳值、雙向繫結

2022-10-11 06:00:47

觀前須知:本人演示使用的input是自己手敲的,如果使用的是element-ui等表單組建的input框請選擇性參考,不保證我的方法對你們也完全有效。

父元件程式碼:

這裡我的MiniInput是以元件形式引入的父頁面 (這裡只貼關鍵程式碼)

<template>
	<div>
    <MiniInput
 			v-for="item in titleArray"
  		:key="item.id"
 			>
       <template #miniTitle> {{ item.name }} </template>
		</MiniInput>
  </div>
</template>
<script setup lang="ts">
  import { Ref, ref } from 'vue'
  import MiniInput from '../components/MiniInput.vue'
  //用ref包裹陣列,方便後期實現雙向繫結
  const titleArray: Ref<Array<TitleArray>> = ref([
    {
      name: 'Exclude content',
      id: 1231,
      textVal: ''
    },
    {
      name: 'Federated content',
      id: 1232,
      textVal: ''
    },
    {
      name: 'Optional retrieval',
      id: 1233,
      textVal: ''
    },
    ])
</script>

子元件程式碼:

<template>
  <div class="from__input__mini">
    <span class="mini-title">
      <slot name="miniTitle"></slot>
    </span>
    <input class="mini-input" type="text" />
  </div>
</template>
<script setup lang="ts">
</script>

上述程式碼可以得到以下介面效果,這是我們只完成了表面工作(請忽略樣式)

下面開始正題:

首先,我們要打通父子元件的隔閡,先將父元件的textVal傳遞給子元件,這裡我們需要使用到v-mode語法,將我們先前定義好的陣列內的textVal屬性傳遞過去。

<!-- 下面新增了一行 v-mode -->
<MiniInput
    v-model:textVal="item.textVal"
 		v-for="item in titleArray"
  	:key="item.id"
 >
       <template #miniTitle> {{ item.name }} </template>
	</MiniInput>

隨後我們來到子元件,為接收父元件傳遞過來的值做準備

<script setup lang="ts">
  // defineProps 用於接收父元件傳遞過來的引數
  defineProps<{
    textVal: string
  }>()
</script>

此時我們就可以給子元件繫結父元件傳遞過來的引數了

<template>
  <div class="from__input__mini">
    <span class="mini-title">
      <slot name="miniTitle"></slot>
    </span>
    <input :value="textVal" class="mini-input" type="text" />
  </div>
</template>
<script setup lang="ts">
  // defineProps 用於接收父元件傳遞過來的引數
  defineProps<{
    textVal: string
  }>()
</script>

實現雙向繫結

但是此時我們會發現,到目前為止我們也僅僅只是接受了父元件傳遞過來的引數,此時我們去輸入框改變內容時,並不會同時改變父元件中的值,那麼此時我們就要想辦法實現資料流的 雙向繫結

要實現雙向資料響應,首先子元件要使用 defineEmits 接受父元件傳遞過來的 textValupdate 函數,隨後我們給輸入框新增一個input事件,目的是監聽輸入內容隨後改變父元件中的對應屬性。

<template>
  <div class="from__input__mini">
    <span class="mini-title">
      <slot name="miniTitle"></slot>
    </span>
    <input :value="textVal" @input="changeText" class="mini-input" type="text" />
  </div>
</template>
<script setup lang="ts">
  // defineProps 用於接收父元件傳遞過來的引數
  defineProps<{
    textVal: string
  }>()
  // 要實現雙向資料響應要使用 defineEmits 接受父元件傳遞過來的 textVal 的 update函數
  const emit = defineEmits(['update:textVal'])
  // 輸入框input事件
  const changeText = (e: Event) => {
    // 這裡因為ts自動型別推斷會把變數推斷為EventTarget,導致沒辦法讀取到.value屬性,所以要進行一個型別斷言
    const target = e.target as HTMLInputElement
    emit('update:textVal', target.value)
  }
</script>

這時我們就可以回到父元件中,為父元件的陣列新增一個監聽事件:

// 這裡是父元件的script
<script setup lang="ts">
  import { Ref, ref, watch } from 'vue'
  ...
  ...
  ...
  // 監聽陣列
  watch
    titleArray,
    () => {
      console.log('陣列變化了')
    },
    {
      deep: true
    }
  )
</script>

隨後進行測試

可以看到,雖然是通過迴圈生成的三個子元件(input),但是它們各自都實現了雙向資料繫結以及資料監聽,至此,效果實現,本部落格僅用於開發過程中的記錄以及覆盤,僅供參考!