我正在尝试修改https://vuetifyjs.com/en/components/autocompletes#example-scoped-slots处的示例代码,以允许任意内容与芯片之间的任何自动完成项都不匹配(以便用户可以在类似于slack和facebook的消息中标记其他用户)
因此,例如,用户可以键入“ Sandra”,然后选择“ sandra adams”,然后键入“ foo”,然后键入另一个空格,然后再键入“ John”,并且自动完成会再次弹出并允许用户选择“约翰·史密斯”。
我已经遍历了文档中的所有属性,但似乎对此内置版本不提供支持。
在显示自动完成选项时,我尝试使用自定义过滤来忽略消息的不相关部分,但是当自动完成选项失去焦点时,自动完成似乎会删除非芯片内容,并且我看不到允许我防止此行为的属性。
不确定是否要使用autcomplete,或者我最好是通过破解组合框来满足此要求,因为此示例似乎更接近我正在尝试做的事情https://vuetifyjs.com/en/components/combobox#example-no-data,但是我相信我会失去自动完成功能附带的Ajax功能。
答案 0 :(得分:1)
您可以通过将自动完成功能的异步搜索与组合框结合在一起来实现。
例如:
new Vue({
el: '#app',
data: () => ({
activator: null,
attach: null,
colors: ['green', 'purple', 'indigo', 'cyan', 'teal', 'orange'],
editing: null,
descriptionLimit: 60,
index: -1,
nonce: 1,
menu: false,
count: 0,
model: [],
x: 0,
search: null,
entries: [],
y: 0
}),
computed: {
fields () {
if (!this.model) return []
return Object.keys(this.model).map(key => {
return {
key,
value: this.model[key] || 'n/a'
}
})
},
items () {
return this.entries.map(entry => {
const Description = entry.Description.length > this.descriptionLimit
? entry.Description.slice(0, this.descriptionLimit) + '...'
: entry.Description
return Object.assign({}, entry, { Description })
})
}
},
watch: {
search (val, prev) {
// Lazily load input items
axios.get('https://api.publicapis.org/entries')
.then(res => {
console.log(res.data)
const { count, entries } = res.data
this.count = count
this.entries = entries
})
.catch(err => {
console.log(err)
})
.finally(() => (this.isLoading = false))
/*if (val.length === prev.length) return
this.model = val.map(v => {
if (typeof v === 'string') {
v = {
text: v,
color: this.colors[this.nonce - 1]
}
this.items.push(v)
this.nonce++
}
return v
})*/
},
model (val, prev) {
if (val.length === prev.length) return
this.model = val.map(v => {
if (typeof v === 'string') {
v = {
Description: v
}
this.items.push(v)
this.nonce++
}
return v
})
}
},
methods: {
edit (index, item) {
if (!this.editing) {
this.editing = item
this.index = index
} else {
this.editing = null
this.index = -1
}
},
filter (item, queryText, itemText) {
const hasValue = val => val != null ? val : ''
const text = hasValue(itemText)
const query = hasValue(queryText)
return text.toString()
.toLowerCase()
.indexOf(query.toString().toLowerCase()) > -1
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js" integrity="sha256-mpnrJ5DpEZZkwkE1ZgkEQQJW/46CSEh/STrZKOB/qoM=" crossorigin="anonymous"></script>
<div id="app">
<v-app>
<v-content>
<v-container>
<v-combobox
v-model="model"
:filter="filter"
:hide-no-data="!search"
:items="items"
:search-input.sync="search"
hide-selected
label="Search for an option"
:allow-overflow="false"
multiple
small-chips
solo
hide-selected
return-object
item-text="Description"
item-value="API"
:menu-props="{ closeOnClick: false, closeOnContentClick: false, openOnClick: false, maxHeight: 200 }"
dark
>
<template slot="no-data">
<v-list-tile>
<span class="subheading">Create</span>
<v-chip
label
small
>
{{ search }}
</v-chip>
</v-list-tile>
</template>
<template
v-if="item === Object(item)"
slot="selection"
slot-scope="{ item, parent, selected }"
>
<v-chip
:selected="selected"
label
small
>
<span class="pr-2">
{{ item.Description }}
</span>
<v-icon
small
@click="parent.selectItem(item)"
>close</v-icon>
</v-chip>
</template>
<template
slot="item"
slot-scope="{ index, item, parent }"
>
<v-list-tile-content>
<v-text-field
v-if="editing === item.Description"
v-model="editing"
autofocus
flat
hide-details
solo
@keyup.enter="edit(index, item)"
></v-text-field>
<v-chip
v-else
dark
label
small
>
{{ item.Description }}
</v-chip>
</v-list-tile-content>
</template>
</v-combobox>
</v-container>
</v-content>
</v-app>
</div>
答案 1 :(得分:0)
所以我最终构建了一个与vuetify兼容的无渲染组件,因为它通过默认插槽并找到了贡品支持的任何类型的标签(文本区域,带有文本类型的输入或contenteditable),并允许您放置任意的vue,这些vue将通过作用域限定的插槽用于构建贡品菜单项。
将来可能会尝试将其包装为一个小的NPM包,以供任何希望以声明性方式以比vue-tribute允许的方式更灵活的方式将tribute.js用于vue的人使用,但是现在这是我的概念证明 >
InputWithMentions.vue
<script>
import Tribute from "tributejs"
// eslint-disable-next-line
import * as css from "tributejs/dist/tribute.css"
import Vue from "vue"
export default {
mounted() {
let menuItemSlot = this.$scopedSlots.default
let tribute = new Tribute({
menuItemTemplate: item =>
{
let menuItemComponent =
new Vue({
render: function (createElement) {
return createElement('div', menuItemSlot({ menuItem: item }))
}
})
menuItemComponent.$mount()
return menuItemComponent.$el.outerHTML
},
values: [
{key: 'Phil Heartman', value: 'pheartman'},
{key: 'Gordon Ramsey', value: 'gramsey'}
]})
tribute.attach(this.$slots.default[0].elm.querySelectorAll('textarea, input[type=text], [contenteditable]'))
},
render(createElement) {
return createElement('div', this.$slots.default)
}
}
</script>
User.vue
<InputWithMentions>
<v-textarea
box
label="Label"
auto-grow
value="The Woodman set to work at once, and so sharp was his axe that the tree was soon chopped nearly through.">
</v-textarea>
<template slot-scope="{ menuItem }">
<v-avatar size="20" color="grey lighten-4">
<img src="https://vuetifyjs.com/apple-touch-icon-180x180.png" alt="avatar">
</v-avatar>
{{ menuItem.string }}
</template>
</InputWithMentions>