0

我对 Vue 还很陌生,很难弄清楚如何解决这个问题。我在一个深入的项目上已经走了很远,但是我正在努力解决应该是一个简单的功能......

简要背景/概述:

我从 API(我自己的)获取数据,该 API 获取用户创建的研究。他们可以切换到另一项研究,这会更改页面上的数据(星号表示该项目已经在研究中)。这是他们的“整体页面浏览量研究”。他们可以将页面上的项目添加到研究中。他们单击该项目,一个 Bootstrap 5 模式弹出,其中包含他们研究的下拉列表。默认情况下选择当前页面研究,但如果他们愿意,他们可以选择任何其他研究来添加它。因此,如果他们将该项目添加到另一项研究(不是他们正在查看的研究),我们假设他们可以添加它并让 API 返回一个特殊的响应 (409),它会向用户显示“已经在研究中”的消息(toast/flash message/etc)。如果它是他们当前的“页面研究”,那么如果它不存在,他们应该能够添加它,或者如果它已经在研究中,则将其删除。

我只是想提供一个背景概述,以防万一其中的某些内容会对解决方案产生影响(Vue3、Bootstrap 5、引导模式、选择下拉菜单等)。

由于复杂性和适当性,我创建了一个通用模型来复制该问题,以及 CodePen 上的演示。

问题:

我有一个下拉列表(学习选择器)和 2 个按钮(添加和删除)。如果他们从下拉列表中选择的研究不是当前查看的“页面研究”,我们假设“添加”。如果是当前查看的“页面学习”,我们需要做一个检查。它检查该“项目”是否在研究中。如果是,则显示“删除”按钮,否则显示“添加”按钮。

当我选择一个已经在所选“页面研究”中的项目时,它确实显示了“删除”按钮(首先)。然后,当我在下拉列表中切换到不同的研究时,它会交换按钮(显示“添加”)。到目前为止一切都很好......但是,当我将它交换回当前的“页面研究”时,当它应该交换到“删除”按钮时,“添加”按钮保持不变......就像它更新了一次并且忽略了我并且不再听大声笑。

我已经尝试过观察者,计算过,尝试过强制更新...我只是对 Vue 了解不够,无法使其正常工作。

带有虚拟数据的模拟应用程序代码:

<div id="app">
  Study ID: {{ currentStudyId }}
  
  <br><br>

  Selected StudyData Item: 
  <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>

  <br><br>

  <select v-model="selectedStudyModel" name="study_id" class="form-select">
    <option disabled value="">Choose a study...</option>
    <option v-for="item in studies" :value="item.id" :key="item.id">
      {{ item.title }}
    </option>
  </select>

  <button v-show="showAddButton()" type="submit" class="btn btn-primary">Add</button>

  <button v-show="showRemoveButton()" type="submit" class="btn btn-danger">Remove</button>
</div>

const TestApp = {
  data() {
    return {
      currentStudyId: 2,
      selectedStudyDataItem: 1,
      selectedStudyModel: "", // dropdown model
      studies: [
        { id: 1, title: "Study 1" },
        { id: 2, title: "Study 2" },
        { id: 3, title: "Study 3" },
        { id: 4, title: "Study 4" }
      ],
      studyData: {
        title: "My Cool Study",
        user_id: 1,
        items: [1, 3]
      }
    };
  },

  methods: {
    showAddButton() {
      // if it isnt the current study, we assume add and let api handle it
      if (this.selectedStudyModel !== this.currentStudyId) {
        return true;
      }

      // else, if it isn't in the current viewed study, we can add it
      if (!this.isInStudy(this.selectedStudyDataItem)) {
        return true;
      }

      return false;
    },
    showRemoveButton() {
      if (this.selectedStudyModel === this.currentStudyId) {
        if (this.isInStudy(this.selectedStudyDataItem)) {
          return true;
        }
      }

      return false;
    },
    isInStudy(something) {
      if (this.studyData) {
        const match = (item) => item === something;
        return this.studyData.items.some(match);
      }

      return false;
    }
  }
};

Vue.createApp(TestApp).mount("#app");

CodePen 示例

4

1 回答 1

1

由于类型转换,您遇到了问题。

  <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>

将使用 Sting 作为selectedStudyModel更改后

为了解决这个问题,您需要更新showAddButton并包含parseInt来处理

showAddButton() {
  // if it isnt the current study, we assume add and let api handle it
  if ( parseInt(this.selectedStudyModel) !== this.currentStudyId ) {
    return true;
  }

  // else, if it isn't in the current viewed study, we can add it
  if ( ! this.isInStudy(parseInt(this.selectedStudyDataItem)) ) {
    return true;
  }

  return false;
},

或者,您也可以使用:valuewhich 将其转换

  <select v-model="selectedStudyDataItem">
    <option :value="1">1</option>
    <option :value="2">2</option>
    <option :value="3">3</option>
    <option :value="4">4</option>
    <option :value="5">5</option>
    <option :value="6">6</option>
    <option :value="7">7</option>
    <option :value="8">8</option>
  </select>

这些现在将被转换为Number而不是String,但是我建议最好在脚本中明确而不依赖于模板魔术。

其他一些注意事项

  • 使用计算而不是方法,它们的结果被缓存,所以你只在需要时调用它们
  • 如果您只有 add 或 remove 作为选项,则无需计算 remove,它正好相反(您可以使用v-ifand v-else

new Vue({
  el: '#app',
    data() {
        return {
        'currentStudyId': 2,
      'selectedStudyDataItem': 1,
            'selectedStudyModel': '',       // dropdown model
      'studies': [
        {'id': 1, 'title': 'Study 1'},
        {'id': 2, 'title': 'Study 2'},
        {'id': 3, 'title': 'Study 3'},
        {'id': 4, 'title': 'Study 4'},
      ],
      'studyData': {
        'title': 'My Cool Study',
        'user_id': 1,
        'items': [
            1, 3
        ],
      },
    }
  },
  computed:{
    showAddButton() {
      // if it isnt the current study, we assume add and let api handle it
      if ( parseInt(this.selectedStudyModel) !== this.currentStudyId ) {
        return true;
      }

      // else, if it isn't in the current viewed study, we can add it
      if ( ! this.isInStudy(parseInt(this.selectedStudyDataItem)) ) {
        return true;
      }

      return false;
    },
  },
  methods: {
    isInStudy(something) {
        if ( this.studyData ) {
                const match = (item) => item === something;
                return this.studyData.items.some(match);
            }

            return false;
    }
  }
  
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  Study ID: {{ currentStudyId }}
  
  <br><br>

  Selected StudyData Item: 
  <select v-model="selectedStudyDataItem">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
  </select>

  <br><br>

  <select v-model="selectedStudyModel" name="study_id" class="form-select">
    <option disabled value="">Choose a study...</option>
    <option v-for="item in studies" :value="item.id" :key="item.id">
      {{ item.title }}
    </option>
  </select>

  <button v-if="showAddButton" type="submit" class="btn btn-primary">Add</button>

  <button v-else type="submit" class="btn btn-danger">Remove</button>

  <p>
    <strong>How to reproduce:</strong> Choose the "Study 2" option from the 2nd dropdown. The button should be "Remove" because "Selected StudyData Item [1]" (dropdown above it) is in the "studyData". 1 and 3 are in the studyData for "Study 2".
  </p>
  <p>
    Then select a "studyData Item" that isn't in the study (not 1 or 3), ie: 8. It should change the button to "Add".
  </p>
  <p>
  Now, <u>here is the problem</u>. Swap the "studyData Item" back to 1 or 3 (items in the studyData). It is supposed to change the button back to "Remove" because 1 and 3 are both in the study (study 2).. However, it just does nothing lol.. 
  </p>
  selectedStudyDataItem: {{JSON.stringify(selectedStudyDataItem)}} | {{ typeof selectedStudyDataItem }}
  <br/>
  selectedStudyModel: {{JSON.stringify(selectedStudyModel)}} | {{ typeof selectedStudyModel}}
</div>

于 2021-05-04T21:38:46.503 回答