Vue 3 - 第二次下拉更改后按钮不更新
Vue 3 - Buttons not updating after second dropdown changes
我是 Vue 的新手,很难弄清楚如何解决这个问题。我在一个深入的项目上已经走得很远了,但是我正在努力思考什么应该是一个简单的功能......
简要Background/Overview:
我从 API(我自己的)获取数据,它获取用户创建的研究。他们可以切换到另一项研究,这会更改页面上的数据(星号表示该项目已经在研究中)。这是他们的“整体页面浏览研究”。他们可以将页面上的项目添加到研究中。他们单击该项目,弹出 Bootstrap 5 模态,其中包含他们的研究下拉列表。当前页面研究默认为 selected,但如果他们愿意,他们可以选择将其添加到任何其他研究中。因此,如果他们将项目添加到另一项研究(不是他们正在查看的研究),我们 假设 他们可以添加它并让 API return 一个特殊的响应 (409),其中它将向用户显示“已经在研究中”的消息 (toast/flash message/etc)。如果这是他们当前的“页面研究”,那么他们应该能够在它不存在时添加它,或者在它已经在研究中时将其删除。
我只是想提供一个背景概述,以防其中的某些内容可以对解决方案产生影响(Vue3,Bootstrap 5,bootstrap 模态,select 下拉菜单,等)。
由于复杂性和适当性,我创建了一个复制问题的通用模型,以及 CodePen 上的演示。
问题:
我有一个下拉菜单(研究 selector)和 2 个按钮(添加和删除)。如果他们从不是当前查看的“页面研究”的下拉列表中选择一项研究,我们假设“添加”。如果是当前浏览的“页面学习”,我们需要做一个检查。它检查该“项目”是否在研究中。如果是,则显示“删除”按钮,否则显示“添加”按钮。
当我选择一个已经在 selected“页面研究”中的项目时,它会显示“删除”按钮(最初)。然后,当我切换到下拉列表中的不同研究时,它会交换按钮(显示“添加”)。到目前为止一切顺利......但是,当我将它切换回当前的“页面研究”时,“添加”按钮在它应该切换到“删除”按钮时保持不变......就像它更新了一次并且忽略了我不再听了哈哈
我尝试过观察者、计算、尝试强制更新...我只是对 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");
由于类型转换,您遇到了问题。
<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;
},
或者,您也可以使用 :value
来转换它
<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-if
和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>
我是 Vue 的新手,很难弄清楚如何解决这个问题。我在一个深入的项目上已经走得很远了,但是我正在努力思考什么应该是一个简单的功能......
简要Background/Overview:
我从 API(我自己的)获取数据,它获取用户创建的研究。他们可以切换到另一项研究,这会更改页面上的数据(星号表示该项目已经在研究中)。这是他们的“整体页面浏览研究”。他们可以将页面上的项目添加到研究中。他们单击该项目,弹出 Bootstrap 5 模态,其中包含他们的研究下拉列表。当前页面研究默认为 selected,但如果他们愿意,他们可以选择将其添加到任何其他研究中。因此,如果他们将项目添加到另一项研究(不是他们正在查看的研究),我们 假设 他们可以添加它并让 API return 一个特殊的响应 (409),其中它将向用户显示“已经在研究中”的消息 (toast/flash message/etc)。如果这是他们当前的“页面研究”,那么他们应该能够在它不存在时添加它,或者在它已经在研究中时将其删除。
我只是想提供一个背景概述,以防其中的某些内容可以对解决方案产生影响(Vue3,Bootstrap 5,bootstrap 模态,select 下拉菜单,等)。
由于复杂性和适当性,我创建了一个复制问题的通用模型,以及 CodePen 上的演示。
问题:
我有一个下拉菜单(研究 selector)和 2 个按钮(添加和删除)。如果他们从不是当前查看的“页面研究”的下拉列表中选择一项研究,我们假设“添加”。如果是当前浏览的“页面学习”,我们需要做一个检查。它检查该“项目”是否在研究中。如果是,则显示“删除”按钮,否则显示“添加”按钮。
当我选择一个已经在 selected“页面研究”中的项目时,它会显示“删除”按钮(最初)。然后,当我切换到下拉列表中的不同研究时,它会交换按钮(显示“添加”)。到目前为止一切顺利......但是,当我将它切换回当前的“页面研究”时,“添加”按钮在它应该切换到“删除”按钮时保持不变......就像它更新了一次并且忽略了我不再听了哈哈
我尝试过观察者、计算、尝试强制更新...我只是对 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");
由于类型转换,您遇到了问题。
<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;
},
或者,您也可以使用 :value
来转换它
<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-if
和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>