401 lines
13 KiB
Vue
401 lines
13 KiB
Vue
<template>
|
|
<div>
|
|
<v-autocomplete
|
|
v-bind:value="value"
|
|
v-on:input="selectionMade($event)"
|
|
return-object
|
|
:items="searchResults"
|
|
:label="label"
|
|
item-text="name"
|
|
item-value="id"
|
|
item-disabled="!active"
|
|
:error-messages="errors"
|
|
:loading="fetching"
|
|
:placeholder="lt('Search')"
|
|
:search-input.sync="searchEntry"
|
|
:multiple="multiple"
|
|
:filter="customFilter"
|
|
hide-no-data
|
|
clearable
|
|
:no-filter="isTagFilter"
|
|
:prepend-icon="errorIcon"
|
|
@click:prepend="handleErrorClick"
|
|
@mousedown="dropdown"
|
|
>
|
|
<template v-slot:prepend-item v-if="hasError()">
|
|
<div class="pl-2">
|
|
<span class="error--text"> {{ errors[0] }}</span>
|
|
</div>
|
|
</template>
|
|
</v-autocomplete>
|
|
</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 cache-items
|
|
import _ from "../libs/lodash.min.js";
|
|
/*
|
|
States
|
|
1) Open form have a preselected Value
|
|
init: load preselected value only
|
|
change:
|
|
DROP DOWN need to drop down and have it populate with top 100
|
|
EDIT search as normal but keep preselected value until changed
|
|
|
|
2) Open form have no value
|
|
init: ALWAYS starts with no selection if no value, up to server routes to deal with 0 id selection if they need to
|
|
|
|
change
|
|
drop down, get top 100
|
|
edit and search as normal
|
|
|
|
NO VALUE
|
|
if not valid then there should be a rule enforcing that, not the control's issue
|
|
|
|
Control always has a value, either it's NO VALUE or it's a selection
|
|
|
|
DROP DOWN
|
|
if no items already loaded then fetches top 100 IN ADDITION to the preselect if present
|
|
if items already loaded then just shows those items as they are likely from a prior search and user can just search again
|
|
if user clears then drops down it acts as a fresh load of 100 items
|
|
|
|
PRE-FILL
|
|
NO prefill, fills only on user action or with defaults to save bandwidth. User must drop down to initiate action or type search text
|
|
|
|
|
|
todo: search entry, should preserve preset value until it's changed or maybe keep until the form is reloaded so can be inserted back again in search results
|
|
todo: set actual seleted ID value from our local selected whole object so outer form just gets id
|
|
todo: server sends whole on drop down of empty combo?
|
|
todo: needs to fill in the selected value when the form opens regardless of what any other setting is,
|
|
in other words it needs to ensure the pre-selectedvalue is in the list
|
|
Maybe an init property sent to server with query which is used to fetch teh pre-select value
|
|
Actually, traffic wise it might be best as an alternate route so that the form doesn't need to load any extras when there is already a selection
|
|
because, in general if already selected probably don't need a list of anything else
|
|
todo multiple selection
|
|
- bind value would be an array in this case, should I just change it to always be an array or maybe alternate control entirely for multiple since that's rare
|
|
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() {
|
|
var vm = this;
|
|
// console.log("CREATED:value is ", this.value);
|
|
|
|
//need to add no selection object always, if it's not valid then that's a rule for the form, not the control
|
|
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
|
|
// debugger;
|
|
//TODO: this may be needed to force new records to have a zero in their no selection valid fields rather than null
|
|
// however it could overwrite a valid value maybe so needs further testing
|
|
//this.$emit("input", 0);
|
|
|
|
//set initial value in control if selected
|
|
if (vm.value != null && vm.value != 0) {
|
|
//It has a prior non empty selection that needs to be fetched
|
|
//note that by default this will just fetch the selected record instead of the prefill list
|
|
// console.log("STUB: created: has value, sb fetched");
|
|
var urlParams = "?ayaType=" + vm.ayaType + "&preId=" + vm.value;
|
|
vm.getList(urlParams);
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
searchResults: [],
|
|
errors: [],
|
|
searchEntry: null,
|
|
lastSelection: null,
|
|
fetching: false,
|
|
isTagFilter: false,
|
|
errorIcon: null,
|
|
initialized: false
|
|
};
|
|
},
|
|
props: {
|
|
value: {
|
|
type: Number,
|
|
default: null
|
|
},
|
|
ayaType: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
includeInactive: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
multiple: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
label: { type: String, default: "" }
|
|
},
|
|
watch: {
|
|
searchEntry(val, oldVal) {
|
|
var vm = this;
|
|
//clear any local errors
|
|
vm.clearErrors();
|
|
//if the search entry is in the results list then it's a drop down selection not a typed search so bail
|
|
for (var i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].name == val) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// console.log(
|
|
// "watch:searchentry cleared check to see if it's a selected list item, doing search for ",
|
|
// val
|
|
// );
|
|
// console.log("search results:", vm.searchResults);
|
|
|
|
if (!val || vm.fetching || !vm.initialized) {
|
|
if (!vm.initialized) {
|
|
vm.$nextTick(() => {
|
|
vm.initialized = true;
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
this.doSearch();
|
|
},
|
|
errors(val) {
|
|
if (this.hasError()) {
|
|
this.errorIcon = "fa-question-circle";
|
|
} else {
|
|
this.errorIcon = null;
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
lt: function(ltkey) {
|
|
return window.$gz.translation.get(ltkey);
|
|
},
|
|
hasError: function() {
|
|
return this.errors.length > 0;
|
|
},
|
|
clearErrors: function() {
|
|
this.errors = [];
|
|
},
|
|
handleErrorClick: function() {
|
|
//open help nav for picklist
|
|
window.$gz.eventBus.$emit("menu-click", {
|
|
key: "app:help",
|
|
data: "ay-start-form-picklist"
|
|
});
|
|
},
|
|
selectionMade(e) {
|
|
this.clearErrors();
|
|
if (e == undefined) {
|
|
//this will happen when clear clicked
|
|
return;
|
|
}
|
|
if (e.id != null) {
|
|
this.$emit("input", e.id);
|
|
}
|
|
this.lastSelection = e;
|
|
},
|
|
replaceLastSelection() {
|
|
// console.log("replace last selection top");
|
|
var vm = this;
|
|
//check if searchREsults has last selection, if not then add it back in again
|
|
if (vm.lastSelection == null) {
|
|
//it might be initializing
|
|
|
|
for (var i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].id == vm.value) {
|
|
// console.log(
|
|
// "rpl last selection null but found in list so setting last selection"
|
|
// );
|
|
vm.lastSelection = vm.searchResults[i];
|
|
return;
|
|
}
|
|
}
|
|
|
|
// console.log("RPL bailing as last selection is null and not in list");
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < vm.searchResults.length; i++) {
|
|
if (vm.searchResults[i].id == vm.lastSelection.id) {
|
|
//console.log("replacelastselection bailing, it's in list already");
|
|
return;
|
|
}
|
|
}
|
|
|
|
vm.searchResults.push(vm.lastSelection);
|
|
// console.log(
|
|
// "replacelastselection bottom, search results now:",
|
|
// vm.searchResults
|
|
// );
|
|
},
|
|
dropdown(e) {
|
|
var vm = this;
|
|
//check if we have only the initial loaded item and no selection item
|
|
if (vm.searchResults.length < 3) {
|
|
//get the default list
|
|
vm.getList();
|
|
}
|
|
},
|
|
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;
|
|
if (vm.fetching) {
|
|
return;
|
|
}
|
|
vm.fetching = true;
|
|
// 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 => {
|
|
vm.fetching = false;
|
|
if (res.error) {
|
|
throw res.error;
|
|
}
|
|
vm.searchResults = res.data;
|
|
window.$gz.form.addNoSelectionItem(vm.searchResults);
|
|
vm.replaceLastSelection();
|
|
})
|
|
.catch(err => {
|
|
window.$gz.errorHandler.handleFormError(err);
|
|
vm.fetching = false;
|
|
});
|
|
},
|
|
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) {
|
|
vm.errors.push(vm.lt("ErrorPickListQueryInvalid"));
|
|
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], "..")
|
|
) {
|
|
vm.errors.push(vm.lt("ErrorPickListQueryInvalid"));
|
|
return;
|
|
}
|
|
|
|
//check that both aren't non-tags
|
|
if (
|
|
!window.$gz._.startsWith(queryTerms[0], "..") &&
|
|
!window.$gz._.startsWith(queryTerms[1], "..")
|
|
) {
|
|
vm.errors.push(vm.lt("ErrorPickListQueryInvalid"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//build url
|
|
//vm.fetching = 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);
|
|
//------------
|
|
}, 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>
|