【问题标题】:vuetify autocomplete allow unknown items between chipsvuetify 自动完成允许芯片之间的未知项目
【发布时间】:2019-05-04 07:01:33
【问题描述】:

我正在尝试修改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 功能。

【问题讨论】:

    标签: vue.js vuetify.js


    【解决方案1】:

    所以我最终构建了一个与 vuetify 兼容的无渲染组件,因为它通过默认插槽并找到贡品支持的任何类型的标签(文本区域、带有文本类型的输入或内容可编辑),并允许您放置任意 vue,用于通过作用域插槽构建贡品菜单项。

    未来可能会尝试将它作为一个小型 NPM 包包装给任何想要以一种比 vue-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>
    

    用户.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>
    

    【讨论】:

      【解决方案2】:

      您可以通过将自动完成的异步搜索与组合框相结合来实现此目的。

      例如:

      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>

      【讨论】:

      • 有什么办法可以让中间的自定义物品不是筹码吗?这个想法是在输入 @ 符号时触发此自动完成功能,否则只是让它表现得像一个普通的输入框,就像这个 github.com/fritx/vue-at 但使用 vuetify 控件和 ajax 驱动的数据。
      • 我会通过在搜索观察器中检查 val.charAt(0) === '@' 来解决这个问题。该 vue-at 存储库中的 utils 文件可能是了解其工作原理的好地方:github.com/fritx/vue-at/blob/dev/src/util.js.
      • 见官方example
      猜你喜欢
      • 2022-01-08
      • 2019-11-24
      • 1970-01-01
      • 2022-08-16
      • 1970-01-01
      • 2021-06-27
      • 2020-09-07
      • 1970-01-01
      • 2019-09-17
      相关资源
      最近更新 更多