让我先说一下前端不是我的强项。
我有一个下拉组件,所有我想要的是,当我点击重置按钮是为它恢复到占位符文本,而不是保持所选的选项。我完全不知道为什么不行。我已经看了两天的文件,尝试了很多方法都无济于事。对我来说,它显然是反应性的,因为它在其他部分是反应性的,但当我将它设置为null时,没有反应性。
下拉。
<template>
<div class="btn-group">
<li @click="toggleMenu()" class="dropdown-toggle dropdown-toggle-placeholder" v-if="isPlaceholder()">
{{placeholderText}}
<span class="caret"></span>
</li>
<li @click="toggleMenu()" class="dropdown-toggle" v-else>
{{ selectedOption }}
<span class="caret"></span>
</li>
<ul class="dropdown-menu" v-if="showMenu">
<li v-for="(option, idx) in options" :key="idx">
<a href="javascript:void(0)" @click="updateOption(option)" draggable="false">
{{ option }}
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
options: {
type: Array
},
selected: {
type: String
},
placeholder: {
type: String
},
closeOnOutsideClick: {
type: Boolean,
default: true,
},
},
data() {
return {
selectedOption: '',
showMenu: false,
placeholderText: 'Please select an item',
}
},
mounted() {
this.selectedOption = this.selected;
if (this.placeholder)
{
this.placeholderText = this.placeholder;
}
if (this.closeOnOutsideClick) {
document.addEventListener('click', this.clickHandler);
}
},
beforeUnmount() {
document.removeEventListener('click', this.clickHandler);
},
methods: {
updateOption(option) {
this.selectedOption = option;
this.showMenu = false;
this.$emit('update-option', this.selectedOption);
},
clearOption() {
this.selectedOption = null;
this.showMenu = false;
console.log("reset:", this.selectedOption);
},
toggleMenu() {
this.showMenu = !this.showMenu;
},
clickHandler(event) {
const { target } = event;
const { $el } = this;
if (!$el.contains(target)) {
this.showMenu = false;
}
},
isPlaceholder() {
return (this.selectedOption === undefined || this.selectedOption === null || this.selectedOption === '');
}
},
}
</script>
<style>
.btn-group {
min-width: 160px;
height: 40px;
position: relative;
margin: 10px 1px;
display: inline-block;
vertical-align: middle;
}
.btn-group a:hover {
text-decoration: none;
}
.dropdown-toggle {
color: #636b6f;
min-width: 160px;
padding: 10px 20px 10px 10px;
text-transform: none;
font-weight: 300;
margin-bottom: 7px;
border: 0;
background-image: linear-gradient(#009688, #009688), linear-gradient(#D2D2D2, #D2D2D2);
background-size: 0 2px, 100% 1px;
background-repeat: no-repeat;
background-position: center bottom, center calc(100% - 1px);
background-color: transparent;
transition: background 0s ease-out;
float: none;
box-shadow: none;
border-radius: 0;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
user-select: none;
}
.dropdown-toggle:hover {
background: #e1e1e1;
cursor: pointer;
}
.dropdown-menu {
display: inherit !important;
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
font-size: 14px;
text-align: left;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
background-clip: padding-box;
}
.dropdown-menu > li > a {
padding: 10px 30px;
display: block;
clear: both;
font-weight: normal;
line-height: 1.6;
color: #333333;
white-space: nowrap;
text-decoration: none;
user-select: none;
}
.dropdown-menu > li > a:hover {
background: #efefef;
color: #409FCB;
}
.dropdown-menu > li {
overflow: hidden;
width: 100%;
position: relative;
margin: 0;
}
.caret {
width: 0;
position: absolute;
top: 19px;
height: 0;
margin-left: -24px;
vertical-align: middle;
border-top: 4px dashed;
border-top: 4px solid 9;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
right: 10px;
}
li {
list-style: none;
}
li.dropdown-toggle::after {
display: none;
}
</style>
App.vue
<template>
<form @reset.prevent="onReset">
<dropdown v-if="foo.bar" class="my-dropdown"
:options="myOptions"
:selected="foo.bar.name"
@update-option="onSelected"
:placeholder="'Select a MyOption'">
</dropdown>
<button type="reset">Reset</button>
</form>
</template>
<script>
import dropdown from './components/Dropdown.vue'
export default {
name: 'App',
components: {
dropdown,
},
data() {
return {
foo: {},
myOptions: [],
};
},
methods: {
onReset() {
console.log("Resetting...");
this.foo = {};
this.foo['bar'] = {};
this.foo['bar']['name'] = '';
dropdown.methods.clearOption();
},
onSelected(selected) {
this.foo.bar.name = selected;
console.log(selected);
}
},
created() {
this.onReset();
},
mounted() {
this.myOptions = ['A', 'B'];
}
}
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.my-dropdown {
border-radius: 5px;
::v-deep(.dropdown-toggle)
{
color: tomato;
font-size: 25px;
font-weight: 800;
}
::v-deep(.dropdown-toggle-placeholder) {
color: #c4c4c4;
}
}
</style>
当我点击重置按钮并调用dropdown.methods.clearOption()
下拉菜单将this.selectedOption
设置为null
(我也尝试过空字符串等)。我所有的控制台。日志看起来很好,使它看起来工作,但是v-if="isPlaceholder()"
和v-else
不工作。选定的选项将保留在dropbox中,而不会返回到占位符文本。
我做错了什么????
假设Vue的响应性基于你自己的代码,甚至没有测试它的错误…你真大胆。我没有深入研究,因为我在你自己的代码中发现了一个非常讨厌的bug。你直接在App的实例中调用dropdown.methods.clearOption();
函数。对我来说,Vue可以很好地解释它,但JS并不像你想象的那样工作。首先,使用这样的方法,你已经丢失了this上下文。
要在Vue中解决这个问题,您需要获取对您试图调用的组件的引用。要在Vue中创建引用,你需要在App组件的模板中添加一个ref
属性:
<dropdown
ref="myDropDown"
v-if="foo.bar"
class="my-dropdown"
:options="myOptions"
:selected="foo.bar.name"
@update-option="onSelected"
:placeholder="'Select a MyOption'">
</dropdown>
然后您可以通过myDropDown
引用访问组件实例,例如:
onReset() {
// the `?.` is an optional chaining operator
// can be easily replaced with an if statement
this.$refs.myDropDown?.clearOption();
}
正如我注意到的,您在模板中使用v-if
语句,此时可能会丢失$refs.myDropDown
引用。要解决这个问题,您可以通过Vue.nextTick延迟调用该方法。
// defers clearing the dropdown options
Vue.nextTick(() => this.$refs.myDropDown?.clearOption())
无论如何,像这样编写组件是与Vue必须提供的响应性相反的。你可以随时观察props的变化,并使组件相应地表现,而不需要获取任何引用。