2018-02-10 15:18:01 +00:00
|
|
|
import ngModule from '../../module';
|
2019-10-08 21:57:02 +00:00
|
|
|
import Field from '../field';
|
2018-10-18 07:24:20 +00:00
|
|
|
import assignProps from '../../lib/assign-props';
|
2019-08-02 06:27:55 +00:00
|
|
|
import {mergeWhere} from 'vn-loopback/util/filter';
|
2017-09-13 12:59:58 +00:00
|
|
|
import './style.scss';
|
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
/**
|
|
|
|
* Input with option selector.
|
|
|
|
*
|
2019-09-18 07:41:25 +00:00
|
|
|
* @property {String} showFiled The data field name that should be shown
|
|
|
|
* @property {String} valueField The data field name that should be used as value
|
2018-03-09 13:15:30 +00:00
|
|
|
* @property {Array} data Static data for the autocomplete
|
2019-09-18 07:41:25 +00:00
|
|
|
* @property {Object} intialData An initial data to avoid the server request used to get the selection
|
2018-03-09 13:15:30 +00:00
|
|
|
* @property {Boolean} multiple Wether to allow multiple selection
|
2019-01-31 06:08:26 +00:00
|
|
|
* @property {Object} selection Current object selected
|
2018-10-18 07:24:20 +00:00
|
|
|
*
|
|
|
|
* @event change Thrown when value is changed
|
2018-03-09 13:15:30 +00:00
|
|
|
*/
|
2019-10-08 21:57:02 +00:00
|
|
|
export default class Autocomplete extends Field {
|
2019-10-28 16:31:33 +00:00
|
|
|
constructor($element, $, $compile, $transclude) {
|
|
|
|
super($element, $, $compile);
|
|
|
|
this.$transclude = $transclude;
|
2019-07-02 10:12:15 +00:00
|
|
|
|
2019-10-08 21:57:02 +00:00
|
|
|
this._selection = null;
|
|
|
|
this.input = this.element.querySelector('input');
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
2017-11-13 21:15:44 +00:00
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
$postLink() {
|
2018-10-22 11:29:52 +00:00
|
|
|
super.$postLink();
|
2018-10-18 07:24:20 +00:00
|
|
|
this.assignDropdownProps();
|
|
|
|
this.showField = this.$.dropDown.showField;
|
|
|
|
this.valueField = this.$.dropDown.valueField;
|
2018-12-20 09:52:08 +00:00
|
|
|
this.refreshSelection();
|
2018-07-02 11:13:26 +00:00
|
|
|
}
|
|
|
|
|
2019-07-02 10:12:15 +00:00
|
|
|
/**
|
2019-10-08 21:57:02 +00:00
|
|
|
* @type {any} The autocomplete value.
|
2019-07-02 10:12:15 +00:00
|
|
|
*/
|
2019-10-08 21:57:02 +00:00
|
|
|
get field() {
|
|
|
|
return super.field;
|
|
|
|
}
|
|
|
|
|
|
|
|
set field(value) {
|
|
|
|
super.field = value;
|
|
|
|
this.refreshSelection();
|
2019-07-02 10:12:15 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
get model() {
|
|
|
|
return this._model;
|
|
|
|
}
|
|
|
|
|
|
|
|
set model(value) {
|
2018-10-18 18:48:21 +00:00
|
|
|
this.dropDownAssign({model: value});
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get data() {
|
|
|
|
return this._data;
|
|
|
|
}
|
|
|
|
|
|
|
|
set data(value) {
|
2018-10-18 18:48:21 +00:00
|
|
|
this.dropDownAssign({data: value});
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-02 11:13:26 +00:00
|
|
|
get url() {
|
|
|
|
return this._url;
|
|
|
|
}
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
set url(value) {
|
2018-10-18 18:48:21 +00:00
|
|
|
this.dropDownAssign({url: value});
|
|
|
|
}
|
|
|
|
|
|
|
|
dropDownAssign(props) {
|
|
|
|
for (let prop in props)
|
|
|
|
this[`_${prop}`] = props[prop];
|
|
|
|
if (this.$.dropDown)
|
|
|
|
Object.assign(this.$.dropDown, props);
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
/**
|
|
|
|
* @type {Object} The selected data object, you can use this property
|
|
|
|
* to prevent requests to display the initial value.
|
|
|
|
*/
|
|
|
|
get selection() {
|
|
|
|
return this._selection;
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
set selection(value) {
|
|
|
|
this._selection = value;
|
|
|
|
this.refreshDisplayed();
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
2017-11-13 21:15:44 +00:00
|
|
|
|
2018-10-19 07:33:12 +00:00
|
|
|
get initialData() {
|
|
|
|
return this._initialData;
|
|
|
|
}
|
|
|
|
|
|
|
|
set initialData(value) {
|
|
|
|
this._initialData = value;
|
|
|
|
this.refreshSelection();
|
|
|
|
}
|
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
selectionIsValid(selection) {
|
|
|
|
return selection
|
|
|
|
&& selection[this.valueField] == this._field
|
|
|
|
&& selection[this.showField] != null;
|
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
refreshSelection() {
|
2018-10-18 18:48:21 +00:00
|
|
|
if (this._field == null) {
|
|
|
|
this.selection = null;
|
2018-03-09 13:15:30 +00:00
|
|
|
return;
|
2018-10-18 18:48:21 +00:00
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
if (!(this.valueField && this.showField)
|
|
|
|
|| this.selectionIsValid(this._selection))
|
|
|
|
return;
|
2017-09-20 11:52:53 +00:00
|
|
|
|
2019-06-13 11:08:11 +00:00
|
|
|
const selection = this.fetchSelection();
|
2019-10-08 15:27:27 +00:00
|
|
|
this.selection = selection;
|
2018-10-18 18:48:21 +00:00
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
fetchSelection() {
|
|
|
|
if (this.selectionIsValid(this.initialData))
|
|
|
|
return this.initialData;
|
2017-09-20 11:52:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
if (!this.$.dropDown)
|
|
|
|
return null;
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
let data;
|
|
|
|
if (this.$.dropDown.model)
|
|
|
|
data = this.$.dropDown.model.orgData;
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
if (data) {
|
|
|
|
let selection = data.find(i => this.selectionIsValid(i));
|
|
|
|
if (selection) return selection;
|
|
|
|
}
|
2018-03-09 13:15:30 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
if (this.url) {
|
|
|
|
let where = {};
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
if (this.multiple)
|
|
|
|
where[this.valueField] = {inq: this.field};
|
|
|
|
else
|
|
|
|
where[this.valueField] = this._field;
|
|
|
|
|
2019-08-02 06:27:55 +00:00
|
|
|
where = mergeWhere(where, this.fetchFunction);
|
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
let filter = {
|
|
|
|
fields: this.$.dropDown.getFields(),
|
|
|
|
where: where
|
|
|
|
};
|
|
|
|
|
|
|
|
let json = encodeURIComponent(JSON.stringify(filter));
|
|
|
|
this.$http.get(`${this.url}?filter=${json}`).then(
|
|
|
|
json => this.onSelectionRequest(json.data),
|
|
|
|
() => this.onSelectionRequest()
|
|
|
|
);
|
|
|
|
}
|
2017-09-20 09:50:53 +00:00
|
|
|
|
2018-10-18 18:48:21 +00:00
|
|
|
return null;
|
2017-09-20 09:50:53 +00:00
|
|
|
}
|
2017-11-13 21:15:44 +00:00
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
onSelectionRequest(data) {
|
2018-11-07 12:32:09 +00:00
|
|
|
if (data && data.length > 0) {
|
2018-03-09 13:15:30 +00:00
|
|
|
if (this.multiple)
|
|
|
|
this.selection = data;
|
|
|
|
else
|
|
|
|
this.selection = data[0];
|
2018-11-07 12:32:09 +00:00
|
|
|
} else
|
2018-10-18 18:48:21 +00:00
|
|
|
this.selection = null;
|
2017-09-20 09:50:53 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
refreshDisplayed() {
|
|
|
|
let display = '';
|
2018-10-31 10:58:10 +00:00
|
|
|
let hasTemplate = this.$transclude && this.$transclude.isSlotFilled('tplItem');
|
2018-03-09 13:15:30 +00:00
|
|
|
|
2018-11-07 12:32:09 +00:00
|
|
|
if (this._selection && this.showField) {
|
2018-03-09 13:15:30 +00:00
|
|
|
if (this.multiple && Array.isArray(this._selection)) {
|
2018-10-31 10:58:10 +00:00
|
|
|
for (let item of this._selection) {
|
2018-03-09 13:15:30 +00:00
|
|
|
if (display.length > 0) display += ', ';
|
|
|
|
display += item[this.showField];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
display = this._selection[this.showField];
|
2018-10-31 10:58:10 +00:00
|
|
|
if (hasTemplate) {
|
|
|
|
let template = this.$transclude(() => {}, null, 'tplItem').text();
|
|
|
|
display = this.$interpolate(template)(this._selection);
|
|
|
|
}
|
2018-03-09 13:15:30 +00:00
|
|
|
}
|
2018-11-07 12:32:09 +00:00
|
|
|
}
|
2018-10-31 12:44:43 +00:00
|
|
|
|
2018-03-09 13:15:30 +00:00
|
|
|
this.input.value = display;
|
2018-10-30 09:52:40 +00:00
|
|
|
|
2018-11-07 12:32:09 +00:00
|
|
|
if (this.translateFields) {
|
2018-10-30 09:52:40 +00:00
|
|
|
if (this.translateFields.indexOf(this.showField) > -1)
|
2020-07-23 14:07:08 +00:00
|
|
|
this.input.value = this.$t(display);
|
2018-11-07 12:32:09 +00:00
|
|
|
}
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
2017-11-13 21:15:44 +00:00
|
|
|
|
2019-06-13 11:08:11 +00:00
|
|
|
onDropDownSelect(item) {
|
|
|
|
const value = item[this.valueField];
|
|
|
|
this.selection = item;
|
2019-10-28 16:31:33 +00:00
|
|
|
this.change(value);
|
2018-01-24 07:42:57 +00:00
|
|
|
}
|
2017-11-13 21:15:44 +00:00
|
|
|
|
2019-10-24 08:17:32 +00:00
|
|
|
onDropDownClose() {
|
|
|
|
setTimeout(() => this.focus());
|
|
|
|
}
|
|
|
|
|
2019-10-09 22:47:29 +00:00
|
|
|
onContainerKeyDown(event) {
|
|
|
|
if (event.defaultPrevented) return;
|
2019-10-28 16:31:33 +00:00
|
|
|
switch (event.key) {
|
2019-10-09 22:47:29 +00:00
|
|
|
case 'ArrowUp':
|
|
|
|
case 'ArrowDown':
|
2018-03-09 13:15:30 +00:00
|
|
|
this.showDropDown();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (event.key.length == 1)
|
|
|
|
this.showDropDown(event.key);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
2017-09-14 11:40:55 +00:00
|
|
|
}
|
|
|
|
|
2019-10-15 14:19:35 +00:00
|
|
|
onContainerClick(event) {
|
2019-10-08 21:57:02 +00:00
|
|
|
if (event.defaultPrevented) return;
|
2018-03-09 13:15:30 +00:00
|
|
|
this.showDropDown();
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
onDataReady() {
|
|
|
|
this.refreshSelection();
|
|
|
|
}
|
2017-11-09 09:11:59 +00:00
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
assignDropdownProps() {
|
|
|
|
if (!this.$.dropDown) return;
|
2019-10-28 16:31:33 +00:00
|
|
|
this.$.dropDown.copySlot('tplItem', this.$transclude);
|
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
assignProps(this, this.$.dropDown, [
|
2018-05-08 07:58:49 +00:00
|
|
|
'valueField',
|
|
|
|
'showField',
|
2018-10-18 07:24:20 +00:00
|
|
|
'showFilter',
|
|
|
|
'multiple',
|
|
|
|
'translateFields',
|
|
|
|
'model',
|
|
|
|
'data',
|
|
|
|
'url',
|
|
|
|
'fields',
|
|
|
|
'include',
|
2018-05-08 07:58:49 +00:00
|
|
|
'where',
|
|
|
|
'order',
|
2018-05-31 09:52:39 +00:00
|
|
|
'limit',
|
2020-09-19 11:02:00 +00:00
|
|
|
'searchFunction',
|
|
|
|
'whereFunction'
|
2018-05-08 07:58:49 +00:00
|
|
|
]);
|
2018-10-18 07:24:20 +00:00
|
|
|
}
|
2018-05-08 07:58:49 +00:00
|
|
|
|
2018-10-18 07:24:20 +00:00
|
|
|
showDropDown(search) {
|
2019-12-17 12:18:58 +00:00
|
|
|
if (!this.editable) return;
|
2018-10-18 07:24:20 +00:00
|
|
|
this.assignDropdownProps();
|
2019-10-09 22:47:29 +00:00
|
|
|
this.$.dropDown.show(this.container, search);
|
2018-03-09 13:15:30 +00:00
|
|
|
}
|
2019-08-02 06:27:55 +00:00
|
|
|
|
|
|
|
get fetchFunction() {
|
|
|
|
return this._fetchFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
set fetchFunction(value) {
|
|
|
|
this._fetchFunction = value;
|
|
|
|
|
|
|
|
if (value)
|
|
|
|
this.refreshSelection();
|
|
|
|
}
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
2019-10-28 16:31:33 +00:00
|
|
|
Autocomplete.$inject = ['$element', '$scope', '$compile', '$transclude'];
|
2017-09-13 12:59:58 +00:00
|
|
|
|
2019-10-08 21:57:02 +00:00
|
|
|
ngModule.vnComponent('vnAutocomplete', {
|
|
|
|
template: require('./index.html'),
|
2017-09-13 12:59:58 +00:00
|
|
|
controller: Autocomplete,
|
|
|
|
bindings: {
|
|
|
|
showField: '@?',
|
|
|
|
valueField: '@?',
|
2018-03-09 13:15:30 +00:00
|
|
|
initialData: '<?',
|
|
|
|
showFilter: '<?',
|
2019-01-31 06:08:26 +00:00
|
|
|
selection: '=?',
|
2018-03-09 13:15:30 +00:00
|
|
|
multiple: '<?',
|
2018-10-18 07:24:20 +00:00
|
|
|
data: '<?',
|
|
|
|
url: '@?',
|
|
|
|
fields: '<?',
|
|
|
|
include: '<?',
|
|
|
|
where: '<?',
|
|
|
|
order: '@?',
|
|
|
|
limit: '<?',
|
2018-10-30 09:52:40 +00:00
|
|
|
translateFields: '<?',
|
2019-08-02 06:27:55 +00:00
|
|
|
searchFunction: '&?',
|
2020-09-19 11:02:00 +00:00
|
|
|
whereFunction: '&?',
|
2019-08-02 06:27:55 +00:00
|
|
|
fetchFunction: '<?'
|
2017-09-20 09:50:53 +00:00
|
|
|
},
|
|
|
|
transclude: {
|
|
|
|
tplItem: '?tplItem'
|
2017-09-13 12:59:58 +00:00
|
|
|
}
|
|
|
|
});
|