349 lines
12 KiB
Vue
349 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<v-autocomplete
|
|
v-model="selected"
|
|
return-object
|
|
:items="searchResults"
|
|
:label="label"
|
|
item-text="name"
|
|
item-value="id"
|
|
item-disabled="!active"
|
|
:error-messages="errors"
|
|
:loading="searchUnderway"
|
|
:placeholder="lt('Search by text, ..tags or both')"
|
|
:search-input.sync="searchEntry"
|
|
auto-select-first
|
|
:multiple="multiple"
|
|
:filter="customFilter"
|
|
hide-no-data
|
|
clearable
|
|
:no-filter="isTagFilter"
|
|
>
|
|
<!-- <template v-slot:no-data> hide-selected v-on:input="$emit('input', $event)" cache-items="false":no-data-text="lt('NoData')"
|
|
<v-list-item>
|
|
<v-list-item-title>
|
|
Search for your favorite
|
|
<strong>PICKLIST ITEM</strong>
|
|
some kind of hint here?
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
</template> -->
|
|
</v-autocomplete>
|
|
<!-- <div>autocomplete selected item: {{ selected }}</div>
|
|
<div>searchResults: {{ searchResults }}</div> -->
|
|
</div>
|
|
</template>
|
|
<script>
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/* eslint-disable */
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//NOTE: have to import lodash directly here as no combination was working with the window.$gz._
|
|
//it would not recognize window in the function call
|
|
import _ from "../libs/lodash.min.js";
|
|
/*
|
|
todo: double check what I want here is actually autocomplete,
|
|
becuase I'm not actually autocompleting what is typed, it's more auto-search
|
|
maybe combobox does this better?
|
|
worth it to check
|
|
todo: validation error is obscured by no-data element
|
|
- PREPEND SLOT - use the prepend slot to add custom error message box or mirror it, In addition to the help link mentioned below or maybe this is it?
|
|
- style it to look like the error slot
|
|
- maybe on validation error it should just clear the list so this will reveal the error?
|
|
- can't get this to do anything, it's very stubborn
|
|
- maybe on validation error it shows elsewhere (popup error dialog with help link?) or just goes red and a HELP icon appears to take to how to use picklist?
|
|
- also there is suffix and prefix text options to explore
|
|
- also there is separately HINT and PLACEHOLDER text, maybe HINT is what I'm after here
|
|
- NOPE: hint is useless, it just gets obscured by drop down items list just like error and is replaced by error message
|
|
todo: set actual seleted ID value from our local selected whole object so outer form just gets id
|
|
todo test multiple selection
|
|
todo: search only property that forces user to search vs just drop down and get default 100
|
|
todo: append or use slot to put at end of drop down menu a "type to search for more" when the results are from an empty query only
|
|
todo: translation keys when done
|
|
todo: option to display icon to open the record selected, (we have the type and the id and in v7 you could click on most titles to navigate to that record)
|
|
if I add that then maybe need a "new" option on edit forms because it's a two step way to get to adding a new one of whatever it is without having to go
|
|
completely out of the page and hunt around lists and shit!!!
|
|
or consider a direct NEW button right there, (this might be a winner)
|
|
or maybe combine the two ideas, if no selection or empty selection then the button acts as new, puts any entered search text into the Name field
|
|
or, if selection then it acts as open
|
|
*/
|
|
export default {
|
|
created() {
|
|
//need to add no selection object if specified
|
|
var vm = this;
|
|
//console.log("CREATED: calling getList for type ", this.ayaType);
|
|
if (vm.preFill) {
|
|
vm.searchUnderway = true;
|
|
vm.getList();
|
|
} else {
|
|
if (vm.noSelectionValid) {
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
if (vm.value == null) {
|
|
vm.value = 0;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
beforeUpdate() {
|
|
//Set the initial list items based on the record items, this only needs to be called once at init
|
|
//Not sure what this is to picklist as it came from tags
|
|
// if (!this.initialized && this.value.length > 0) {
|
|
// this.searchResults = this.value;
|
|
// this.initialized = true;
|
|
// }
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
searchResults: [],
|
|
errors: [],
|
|
selected: { name: "-", id: 0 },
|
|
searchEntry: null,
|
|
searchUnderway: false,
|
|
isTagFilter: false
|
|
//,initialized: false
|
|
};
|
|
},
|
|
props: {
|
|
// value: {
|
|
// type: Number,
|
|
// default: 0
|
|
// },
|
|
ayaType: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
includeInactive: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
multiple: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
noSelectionValid: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
preFill: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
label: { type: String, default: "" }
|
|
},
|
|
watch: {
|
|
searchEntry(val) {
|
|
var vm = this;
|
|
//clear any local errors
|
|
vm.errors = [];
|
|
// console.log("WATCH::SEARCHENTRY TRIGGERED:", val);
|
|
if (!val || vm.searchUnderway) {
|
|
return;
|
|
}
|
|
if (vm.selected != null) {
|
|
if (val == vm.selected.name) {
|
|
return;
|
|
}
|
|
}
|
|
//console.log("WATCH::SEARCHENTRY doing search now");
|
|
this.doSearch();
|
|
},
|
|
value(val) {
|
|
//this ensures the parent form gets the onchange event
|
|
//not actually sure why there are two here but it worked with the datetime picker so I replicated it here
|
|
//To answer above it appears both are necessary for proper operation
|
|
this.$emit("input", val);
|
|
this.$emit("change", val);
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
lt: function(ltkey) {
|
|
return window.$gz.translation.get(ltkey);
|
|
},
|
|
customFilter(item, queryText, itemText) {
|
|
//NOTE: I wanted this to work with tags but all it does is highlight all of each row if tag query is present
|
|
//I guess because it later on attempts to do the highlighting and can't find all the entered query
|
|
//it's not clean so I'm just going to make it only highlight if it's a non tag query for now
|
|
//and do no filtering (highlighting) at all if it's a tag query
|
|
if (queryText.includes(" ") || queryText.startsWith("..")) {
|
|
this.isTagFilter = true;
|
|
return false;
|
|
}
|
|
return item.name.indexOf(queryText) > -1;
|
|
//split out just the text search part
|
|
// var searchText = queryText;
|
|
// if (queryText.includes(" ")) {
|
|
// //get the non tag part of query if possible
|
|
// //ignore bad condition of too many terms
|
|
// var searchTerms = queryText.split(" ");
|
|
// if (searchTerms[0].includes("..")) {
|
|
// searchText = searchTerms[1];
|
|
// } else {
|
|
// searchText = searchTerms[0];
|
|
// }
|
|
// }
|
|
},
|
|
getList: function(urlParams) {
|
|
var vm = this;
|
|
// console.log("getlist: calling api.get.picklist for type ", vm.ayaType);
|
|
//default params for when called on init
|
|
if (!urlParams) {
|
|
urlParams = "?ayaType=" + vm.ayaType;
|
|
if (vm.includeInactive) {
|
|
urlParams += "&inactive=true";
|
|
}
|
|
}
|
|
window.$gz.api
|
|
.get("PickList/List" + urlParams)
|
|
.then(res => {
|
|
if (res.error) {
|
|
throw res.error;
|
|
}
|
|
|
|
vm.searchResults = res.data;
|
|
if (vm.noSelectionValid) {
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
// if (vm.value == null) {
|
|
// vm.value = 0;
|
|
// }
|
|
}
|
|
vm.searchUnderway = false;
|
|
})
|
|
.catch(err => {
|
|
window.$gz.errorHandler.handleFormError(err);
|
|
});
|
|
},
|
|
doSearch: _.debounce(function() {
|
|
//NOTE debounce with a watcher is a bit different
|
|
//https://vuejs.org/v2/guide/migration.html#debounce-Param-Attribute-for-v-model-removed
|
|
//-----------------
|
|
|
|
var vm = this;
|
|
|
|
//NOTE: empty query is perfectly valid; it means get the top 100 ordered by template order
|
|
var emptyQuery = false;
|
|
if (this.searchEntry == null || this.searchEntry == "") {
|
|
emptyQuery = true;
|
|
} else {
|
|
//Pre-process the query to validate and send conditionally
|
|
var val = this.searchEntry;
|
|
//get the discrete search terms and verify there are max two
|
|
var isATwoTermQuery = false;
|
|
var queryTerms = [];
|
|
if (val.includes(" ")) {
|
|
queryTerms = val.split(" ");
|
|
if (queryTerms.length > 2) {
|
|
//todo: put client side localized validation error message in component
|
|
vm.errors.push("LTERROR TOO MANY TERMS");
|
|
|
|
return;
|
|
}
|
|
isATwoTermQuery = true;
|
|
} else {
|
|
//one term only so push it into array
|
|
queryTerms.push(val);
|
|
//Marker term, will be weeded back out later
|
|
queryTerms.push("[?]");
|
|
}
|
|
|
|
//Now vet the terms
|
|
//Is user in mid entry of a second tag (space only?)
|
|
//will appear as an empty string post split
|
|
if (queryTerms[1] == "") {
|
|
//mid entry of a second term, just return
|
|
return;
|
|
}
|
|
|
|
//Is user in mid entry of tag query, just bounce back
|
|
if (
|
|
queryTerms[0] == "." ||
|
|
queryTerms[0] == ".." ||
|
|
queryTerms[1] == "." ||
|
|
queryTerms[1] == ".."
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (isATwoTermQuery) {
|
|
//check that both terms aren't tags
|
|
if (
|
|
window.$gz._.startsWith(queryTerms[0], "..") &&
|
|
window.$gz._.startsWith(queryTerms[1], "..")
|
|
) {
|
|
//todo: put client side localized validation error message in component
|
|
vm.errors.push(
|
|
"LTERROR if two terms one must be tag and one must be text"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
//check that both aren't non-tags
|
|
if (
|
|
!window.$gz._.startsWith(queryTerms[0], "..") &&
|
|
!window.$gz._.startsWith(queryTerms[1], "..")
|
|
) {
|
|
//todo: put client side localized validation error message in component
|
|
vm.errors.push(
|
|
"LTERROR if two terms one must be tag and one must be text"
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//build url
|
|
vm.searchUnderway = true;
|
|
|
|
var urlParams = "?ayaType=" + vm.ayaType;
|
|
if (!emptyQuery) {
|
|
var query = queryTerms[0];
|
|
if (queryTerms[1] != "[?]") {
|
|
query += " " + queryTerms[1];
|
|
}
|
|
urlParams += "&query=" + query;
|
|
}
|
|
|
|
if (vm.includeInactive) {
|
|
urlParams += "&inactive=true";
|
|
}
|
|
this.getList(urlParams);
|
|
// console.log("dosearch: calling api.get.picklist for type ", this.ayaType);
|
|
// window.$gz.api
|
|
// .get("PickList/List" + urlParams)
|
|
// .then(res => {
|
|
// if (res.error) {
|
|
// throw res.error;
|
|
// }
|
|
|
|
// vm.searchResults = res.data;
|
|
// if (vm.noSelectionValid) {
|
|
// window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
// // if (vm.value == null) {
|
|
// // vm.value = 0;
|
|
// // }
|
|
// }
|
|
// vm.searchUnderway = false;
|
|
// })
|
|
// .catch(err => {
|
|
// window.$gz.errorHandler.handleFormError(err);
|
|
// });
|
|
|
|
//------------
|
|
}, 300) //did some checking, 200-300ms seems to be the most common debounce time for ajax search queries
|
|
},
|
|
beforeCreate() {
|
|
//check pre-requisites exist just in case
|
|
if (window.$gz.errorHandler.devMode()) {
|
|
if (!window.$gz._) {
|
|
throw "tag-picker: $gz._ (lodash) is required and missing";
|
|
}
|
|
if (!window.$gz.translation) {
|
|
throw "tag-picker: $gz.translation is required and missing";
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|