AutocompleteCustomDropdown.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <template>
  2. <div class="dropdown" :class="{'open': open}">
  3. <div class="input-group">
  4. <input type="text" class="form-control" v-model="searchText" @input="searchChanged">
  5. <div class="form-group-append">
  6. <a class="toggle input-group-text bg-primary text-light" @mousedown.prevent @click="setOpen(!open)">
  7. <span class="arrow-up">▲</span>
  8. <span class="arrow-down">▼</span>
  9. </a>
  10. </div>
  11. </div>
  12. <ul class="suggestion-list list-unstyled">
  13. <li v-for="(suggestion,index) in matches" :key="index"
  14. @mousedown.prevent
  15. @click="suggestionSelected(suggestion)"
  16. >
  17. {{ suggestion[1] }}
  18. </li>
  19. </ul>
  20. </div>
  21. </template>
  22. <script>
  23. export default {
  24. props: {
  25. value: null,
  26. // An object with the following format
  27. // {
  28. // key: value,
  29. // key: value,
  30. // }
  31. options: {
  32. type: Object,
  33. required: true
  34. },
  35. allowCustom: {
  36. type: Boolean,
  37. default: false
  38. }
  39. },
  40. data () {
  41. return {
  42. searchText: '',
  43. selectedOption: null,
  44. open: false,
  45. // allows for checking whether button was clicked or text was typed.
  46. // If button was clicked, we show all options without having to clear
  47. // out any searchText that was autofilled when page loaded.
  48. isSearching: false
  49. }
  50. },
  51. computed: {
  52. matches () {
  53. // If button was clicked, show all options, if typing, filter
  54. // options based on searchText
  55. return Object.entries(this.options).filter((option) => {
  56. var optionText = option[1].toUpperCase()
  57. if (this.isSearching) {
  58. return optionText.match(this.searchText.toUpperCase())
  59. }
  60. else {
  61. return optionText.match('')
  62. }
  63. })
  64. }
  65. },
  66. methods: {
  67. // if button is clicked, show all options
  68. setOpen (isOpen) {
  69. this.open = isOpen
  70. this.isSearching = false
  71. },
  72. // if typing in the box, start filtering options
  73. searchChanged () {
  74. if (!this.open) {
  75. this.open = true
  76. }
  77. if (!this.isSearching) {
  78. this.isSearching = true
  79. }
  80. // If custom values are allowed, emits {id: 'custom', name: searchtext}
  81. if (this.allowCustom) {
  82. this.$emit('input', {'id': 'custom', 'name': this.searchText})
  83. }
  84. },
  85. // emits { id: suggestion_id, name: suggestion_name} to v-model when
  86. // a suggestion is selected
  87. suggestionSelected (suggestion) {
  88. this.open = false
  89. this.searchText = suggestion[1]
  90. this.$emit('input', { 'id': suggestion[0], 'name': suggestion[1]})
  91. },
  92. updateComponentWithValue(newValue) {
  93. if (this.allowCustom) {
  94. this.searchText = this.value.name
  95. }
  96. else {
  97. if (newValue.id > -1) {
  98. // Find the matching text for the supplied option value
  99. for (var id in this.options) {
  100. if (this.options.hasOwnProperty(id)) {
  101. if (this.options[id] === this.options[newValue.id]) {
  102. this.searchText = this.options[id]
  103. }
  104. }
  105. }
  106. }
  107. }
  108. }
  109. },
  110. mounted () {
  111. this.updateComponentWithValue(this.value)
  112. },
  113. watch: {
  114. value: function (newValue) {
  115. this.updateComponentWithValue(newValue)
  116. }
  117. }
  118. }
  119. </script>