fix: add basic <wutta-autocomplete>
component
this probably needs improvement yet but we'll see
This commit is contained in:
parent
517928320b
commit
e674db86be
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
<%def name="make_wutta_components()">
|
<%def name="make_wutta_components()">
|
||||||
${self.make_wutta_request_mixin()}
|
${self.make_wutta_request_mixin()}
|
||||||
|
${self.make_wutta_autocomplete_component()}
|
||||||
${self.make_wutta_button_component()}
|
${self.make_wutta_button_component()}
|
||||||
${self.make_wutta_datepicker_component()}
|
${self.make_wutta_datepicker_component()}
|
||||||
${self.make_wutta_timepicker_component()}
|
${self.make_wutta_timepicker_component()}
|
||||||
|
@ -86,6 +87,150 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_wutta_autocomplete_component()">
|
||||||
|
<script type="text/x-template" id="wutta-autocomplete-template">
|
||||||
|
<div>
|
||||||
|
<b-autocomplete ref="autocomplete"
|
||||||
|
v-show="!value"
|
||||||
|
v-model="entry"
|
||||||
|
:data="data"
|
||||||
|
@typing="getAsyncData"
|
||||||
|
@select="selectionMade"
|
||||||
|
keep-first>
|
||||||
|
<template slot-scope="props">
|
||||||
|
{{ props.option.label }}
|
||||||
|
</template>
|
||||||
|
</b-autocomplete>
|
||||||
|
<b-button v-if="value"
|
||||||
|
@click="clearValue(true, true)">
|
||||||
|
{{ recordLabel }} (click to change)
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const WuttaAutocomplete = {
|
||||||
|
template: '#wutta-autocomplete-template',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
// callers do not specify this directly but rather by way
|
||||||
|
// of the `v-model` directive. the component will emit
|
||||||
|
// `input` events when this value changes
|
||||||
|
value: String,
|
||||||
|
|
||||||
|
// caller must specify initial display string, if the
|
||||||
|
// (v-model) value is not empty when component loads
|
||||||
|
display: String,
|
||||||
|
|
||||||
|
// the url from which search results are obtained. the
|
||||||
|
// endpoint should expect a GET with single `term` param
|
||||||
|
// in query string, and return list of objects, each with
|
||||||
|
// (at least) `value` and `label` properties.
|
||||||
|
serviceUrl: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
// user search input
|
||||||
|
entry: null,
|
||||||
|
|
||||||
|
// display label for button, when value is set
|
||||||
|
recordLabel: this.display,
|
||||||
|
|
||||||
|
// this contains the latest search results; it will
|
||||||
|
// change over time as user types. when an option is
|
||||||
|
// selected, it will be an element from this list.
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
|
||||||
|
value(val) {
|
||||||
|
// reset ourself when model value is cleared
|
||||||
|
if (!val) {
|
||||||
|
this.clearValue()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.$refs.autocomplete.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
// convenience for parent component to fetch current label
|
||||||
|
getLabel() {
|
||||||
|
return this.recordLabel
|
||||||
|
},
|
||||||
|
|
||||||
|
// fetch new search results from server. this is invoked
|
||||||
|
// when user types new input
|
||||||
|
getAsyncData(entry) {
|
||||||
|
|
||||||
|
// nb. skip search until we have at least 3 chars of input
|
||||||
|
if (entry.length < 3) {
|
||||||
|
this.data = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// search results become autocomplete options
|
||||||
|
this.$http.get(this.serviceUrl + '?term=' + encodeURIComponent(entry))
|
||||||
|
.then(({ data }) => {
|
||||||
|
this.data = data
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.data = []
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// handle selection change. this is invoked when user
|
||||||
|
// chooses an autocomplete option
|
||||||
|
selectionMade(option) {
|
||||||
|
|
||||||
|
// reset user input
|
||||||
|
this.entry = null
|
||||||
|
|
||||||
|
// nb. this method can be triggered when a selection
|
||||||
|
// is made *or cleared* - if the latter then we do not
|
||||||
|
// want to emit event for the empty value; that part
|
||||||
|
// is handled in clearValue()
|
||||||
|
if (option) {
|
||||||
|
this.recordLabel = option.label
|
||||||
|
this.$emit('input', option.value)
|
||||||
|
} else {
|
||||||
|
this.recordLabel = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// clear the component value
|
||||||
|
clearValue(emit, focus) {
|
||||||
|
|
||||||
|
// clear autocomplete selection
|
||||||
|
this.$refs.autocomplete.setSelected(null)
|
||||||
|
|
||||||
|
// maybe emit event for new value
|
||||||
|
if (emit) {
|
||||||
|
this.$emit('input', null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe set focus to autocomplete
|
||||||
|
if (focus) {
|
||||||
|
this.$nextTick(function() {
|
||||||
|
this.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
Vue.component('wutta-autocomplete', WuttaAutocomplete)
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="make_wutta_button_component()">
|
<%def name="make_wutta_button_component()">
|
||||||
<script type="text/x-template" id="wutta-button-template">
|
<script type="text/x-template" id="wutta-button-template">
|
||||||
<b-button :type="type"
|
<b-button :type="type"
|
||||||
|
|
Loading…
Reference in a new issue