【问题标题】:how to make el-select and v-model work together when extracting a custom component提取自定义组件时如何使el-select和v-model协同工作
【发布时间】:2018-11-28 01:31:18
【问题描述】:

我正在使用el-select 构建一个选择组件。像这样的:

<template>
    //omitted code
    <el-select v-model="filterForm.client"
                    filterable
                    remote
                    placeholder="Please enter a keyword"
                    :remote-method="filterClients"
                    :loading="loading">
                <el-option
                        v-for="item in clientCandidates"
                        :key="item._id"
                        :label="item.name"
                        :value="item._id">
                </el-option>
            </el-select>
</template>
<scripts>
    export default {
        data() {
           filterForm: {
                client: ''
           },
           clientCandidates: [],
           loading: false
        },
        methods: {
            filterClients(query) {
                if (query !== '') {
                    this.loading = true;
                    setTimeout(() => {
                        this.loading = false;
                        this.clientCandidates = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
                    }, 200);
                } else {
                    this.clientCandidates = [];
                }
            }
        }
    }
</scripts>

到目前为止一切都很好,但是由于组件会出现在不同的页面中,所以我想提取一个自定义组件以避免重复。

根据guideline

v-model="fullName"

等价于

v-bind:value="fullName"
v-on:input="$emit('input', $event)"

所以我像这样提取了选择组件:

<template>
<el-select
        v-bind:value="clientId"
        v-on:input="$emit('input', $event)"
        placeholder="Filter by short name"
        filterable="true"
        remote="true"
        :remote-method="filter"
        :loading="loading">
    <el-option
            v-for="item in clients"
            :key="item._id"
            :label="item.name"
            :value="item._id">
    </el-option>
</el-select>
</template>
<scripts>
export default {
    props: {
        clientId: {
            type: String,
            required: true
        }
    },
    data() {
        return {
            clients: [],
            loading: false,
        }
    },
    methods: {
        filter(query) {
            if (query !== '') {
                this.loading = true;
                setTimeout(() => {
                    this.loading = false;
                    this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}];
                }, 200);
            } else {
                this.clients = [];
            }
        }
    }
}
</scripts>

而父组件是这样的:

<select-client v-model="filterForm.clientId"></select-client>

选择下拉菜单工作正常,但不幸的是,选择没有显示我选择的选项,在我选择一个选项后它仍然是空的。我怀疑也许我应该将v-on:input 切换为'v-on:change',但它也不起作用。

更新 我创建了一个简单的例子,你可以克隆它here,请查看el-select-as-component 分支。运行

npm install
npm run dev

您将看到一个包含 3 种选择的简单页面:
左边是一个用 raw select 编写的自定义组件,它工作正常。
中间的一个是用el-select 编写的自定义组件,下拉列表仍然是空的,但是您可以在单击Filter 按钮后在控制台中看到filterForm.elClientId。这就是我提出这个问题的原因。
右边是一个普通的el-select,它工作正常。

【问题讨论】:

标签: vuejs2 vue-component element-ui


【解决方案1】:

指导方针说v-model 等同于v-bind:valuev-on:input,但如果你仔细观察,在监听器函数中,绑定的变量是使用事件属性设置的。你在你的例子中所做的不一样,在你的监听器中你发出另一个事件。除非你抓住这个新事件,否则你的价值永远不会被设定。

另一件事是你不能修改一个道具,你应该把它当作一个只读变量。

如果你想从父组件监听到发出的事件到子组件中,你必须做这样的事情

<template>
  <el-select
    :value="selected"
    @input="dispatch"
    placeholder="Filter by short name"
    :filterable="true"
    :remote="true"
    :remote-method="filter"
    :loading="loading">
    <el-option
      v-for="item in clients"
      :key="item._id"
      :label="item.name"
      :value="item._id">
    </el-option>
  </el-select>
</template>

<script>
export default {
  name: 'SelectClient',

  data() {
    return {
      selected: '',
      clients: [],
      loading: false,
    }
  },

  methods: {
    filter(query) {
      if (query !== '') {
        this.loading = true;
        setTimeout(() => {
          this.loading = false
          this.clients = [{_id: '1', name: 'foo'}, {_id: '2', name: 'bar'}]
        }, 200)
      } else {
        this.clients = []
      }
    },

    dispatch (e) {
      this.$emit('input', e)
      this.selected = e
    }
  }
}
</script>

注意:v-model + watch 模式也可以。重要的是$emit输入事件,所以父级中的v-model会更新。

在你的父母中,你可以像这样使用这个组件:&lt;select-client v-model="clientId"/&gt;

提示:如果你想在不同的地方修改相同的数据,你应该有一个单一的事实来源,并且更喜欢vuex这样的东西。那么你的组件会是这样的

<template lang="html">
  <select
    v-model="clientId">
    <option
      disabled
      value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>

<script>
export default {
  data () {
    return {
      clientId: ''
    }
  },

  watch: {
    clientId (newValue) {
      // Do something else here if you want then commit it
      // Of course, listen for the 'setClientId' mutation in your store
      this.$store.commit('setClientId', newValue)
    }
  }
}
</script>

然后在你的其他组件中,你可以监听$store.state.clientId的值。

【讨论】:

  • 感谢您的回复。我试过你的代码,它不起作用。控制台告诉我undefined selectedOptions,我认为el-select 转换原始事件,因此event.target 不再存在。我对前端很陌生,所以我可能会弄错:-(
  • 请注意:dispatch 中的"this.selected = e" 也很重要,而且我认为&lt;el-select/&gt; 没有很好地实现,因为对于&lt;el-input/&gt;,我没有明确设置this.value=e
  • 谢谢。我正在寻找完全相同的解决方案......而且我也在使用 Element UI。 el-select 组件有点令人困惑,因为您不知道实际上是触发输入事件的 el-select 还是 el-option。事实上,我认为他们都在触发一个事件,但最好的方法是只观看顶级赛事。我看了一下 element-ui 源代码,还有一个很好的事件传播方式,但是代码看起来有点过于复杂,还有其他的东西。当我有更多时间时,也许我会多研究一下代码(又名..从不;P)
  • 非常感谢!,我正在构建一个类似的组件,突然间,我发现当您通过代码删除或设置值时,组件本身并没有刷新它filterForm.clientId = null,我的意思是变量selected。为了解决这种情况,我必须在组件本身中添加一个手表,如下所示:watch:{ 'value':function(pnew, pold){ if(pnew==null) this.selected=null; } }
猜你喜欢
  • 1970-01-01
  • 2020-02-19
  • 2017-11-07
  • 2019-12-18
  • 2018-12-14
  • 2019-06-14
  • 2019-08-15
  • 2020-09-26
  • 2022-01-06
相关资源
最近更新 更多