You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js 2组件循环渲染时数据绑定异常问题求助

Vue组件数据绑定异常问题解决

问题场景

我是Vue.js新手,实现了一个包含Bootstrap Modal的scoring-button组件,通过v-for循环渲染多个实例。但仅首个组件能正确接收父组件传递的props数据,其余组件的row.id相关数据存在异常。尝试过重置Bootstrap Modal数据、v-model、$emit等方案均无效,怀疑和Bootstrap Modal有关。

问题根源

  1. Modal ID重复:每个scoring-button组件内的Modal都使用固定id="scoringModal",Bootstrap通过ID查找Modal时只会匹配第一个,导致所有按钮都触发首个组件的Modal,表现为其他组件数据异常。
  2. Radio组冲突:所有组件内的radio使用相同name="btnScore",不同组件的radio会互相干扰选中状态。
  3. Props同步问题:组件data中直接赋值props(如scoredLoadId: this.loadId),当父组件props更新时,data不会自动同步。
  4. 未定义方法:模板中调用了clearSelectedCause但组件未实现该方法。

修复步骤

1. 给Modal生成唯一ID

每个组件的Modal使用loadId拼接唯一ID,确保Bootstrap能正确找到对应组件的Modal。

2. 同步按钮的Modal目标ID

按钮的data-bs-target对应修改为唯一的Modal ID。

3. 修复Radio组

给radio的name属性加上loadId,保证每个组件的radio组独立;同时移除冗余的checked属性(v-model已能控制选中状态)。

4. 监听Props变化(可选)

如果需要props更新时同步data,添加watch监听,或者直接在模板中使用props。

5. 补充缺失方法

实现模板中调用的clearSelectedCause方法。

修改后的代码

组件JS代码

let vSelect = Vue.component("v-select", VueSelect.VueSelect);

var scoringButton = Vue.component("scoring-button", {
  props: ["loadId", "causeData"],
  template: "#scoring-template",
  components: {
    "v-select": vSelect,
  },
  data: function () {
    return {
      scoring: null,
      causeId: null,
      selectedCause: null,
    };
  },
  computed: {
    showCauseList() {
      return this.scoring === "1" || this.scoring === null;
    },
    modalId() {
      // 生成唯一Modal ID
      return `scoringModal-${this.loadId}`;
    }
  },
  methods: {
    clearSelectedCause() {
      this.selectedCause = null;
    }
  },
  // 监听props变化,同步数据(如果需要)
  watch: {
    loadId(newVal) {
      // 若有依赖loadId的data需要同步,可在此处理
    },
    causeData(newVal) {
      // 若causeList需要同步,可在此处理,或者直接在模板中用this.causeData
    }
  }
});

模板代码

<template v-cloak id="scoring-template">
  <div class="scoring-block" :id="loadId">
    <div class="scoring">
      <button
        v-if="scoring === '1'"
        title="Scoring button"
        type="button"
        class="btn btn-success scoring-btn"
        data-bs-toggle="modal"
        :data-bs-target="`#${modalId}`"
      >
        <i class="bi bi-hand-thumbs-up-fill"></i>
      </button>
      <button
        v-else-if="scoring === '2'"
        title="Scoring button"
        type="button"
        class="btn btn-danger scoring-btn"
        data-bs-toggle="modal"
        :data-bs-target="`#${modalId}`"
      >
        <i class="bi bi-hand-thumbs-down-fill"></i>
      </button>
      <button
        v-else
        title="Scoring button"
        type="button"
        class="btn btn-info scoring-btn"
        data-bs-toggle="modal"
        :data-bs-target="`#${modalId}`"
      >
        <i class="bi bi-hand-thumbs-up-fill"></i>
        <i class="bi bi-hand-thumbs-down-fill"></i>
      </button>
    </div>
    <div
      class="modal fade scoring-modal"
      :id="modalId"
      tabindex="-1"
      role="dialog"
      aria-hidden="true"
    >
      <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
          <div class="modal-header">
            <h1 class="modal-title">Load Scoring</h1>
            <button
              type="button"
              class="btn-close"
              aria-label="Close"
              data-bs-dismiss="modal"
            ></button>
          </div>

          <div class="modal-body">
            <div class="scoring-content">
              <input
                type="radio"
                class="btn-check"
                :name="`btnScore-${loadId}`"
                :id="`btnLike-${loadId}`"
                autocomplete="off"
                value="1"
                v-model="scoring"
              />
              <label
                class="btn btn-outline-success"
                :for="`btnLike-${loadId}`"
                @click="clearSelectedCause"
              >
                <i class="bi bi-hand-thumbs-up-fill"></i> Like
              </label>
              <input
                type="radio"
                class="btn-check"
                :name="`btnScore-${loadId}`"
                :id="`btnDislike-${loadId}`"
                autocomplete="off"
                value="2"
                v-model="scoring"
              />
              <label class="btn btn-outline-danger" :for="`btnDislike-${loadId}`">
                <i class="bi bi-hand-thumbs-down-fill"></i> Dislike
              </label>
            </div>
            <div class="scoring-cause">
              <v-select
                :disabled="showCauseList"
                label="name"
                :options="causeData"
                v-model="selectedCause"
                placeholder="Choose a cause option"
              ></v-select>
            </div>
          </div>
          <div class="modal-footer">
            <button
              type="button"
              class="btn btn-secondary"
              data-bs-dismiss="modal"
            >
              Close
            </button>
            <button type="button" class="btn btn-primary">
              Save changes
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

父组件使用代码(无需修改)

<div 
  v-for="row in tableData"
  class="row-container"
>
  ....
  <scoring-button 
    :id="row.LOAD_ID"
    :key="row.LOAD_ID"
    :load-id="row.LOAD_ID" 
    :cause-data="scoringCauseList"
  >
  </scoring-button>
  ....
</div>

说明

  • 移除了data中scoredLoadId和causeList,直接使用propsloadId和causeData,避免数据不同步问题。
  • 所有需要唯一标识的元素(Modal ID、radio的id和name)都通过loadId拼接,确保每个组件实例独立。
  • 补充了clearSelectedCause方法,实现点击Like时清空选中的原因。

内容的提问来源于stack exchange,提问作者stillnurs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 18:01:23