salix/front/core/components/autocomplete/autocomplete.js

344 lines
8.5 KiB
JavaScript
Raw Normal View History

2018-02-10 15:18:01 +00:00
import ngModule from '../../module';
import Input from '../../lib/input';
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
*/
export default class Autocomplete extends Input {
constructor($element, $scope, $http, $transclude, $translate, $interpolate) {
2018-03-09 13:15:30 +00:00
super($element, $scope);
2017-09-13 12:59:58 +00:00
this.$http = $http;
this.$interpolate = $interpolate;
2018-03-09 13:15:30 +00:00
this.$transclude = $transclude;
this.$translate = $translate;
2018-03-09 13:15:30 +00:00
this._field = undefined;
this._selection = null;
this.readonly = true;
2018-01-24 07:42:57 +00:00
this.form = null;
2018-03-09 13:15:30 +00:00
this.input = this.element.querySelector('.mdl-textfield__input');
2017-09-13 12:59:58 +00:00
2018-03-09 13:15:30 +00:00
componentHandler.upgradeElement(
this.element.querySelector('.mdl-textfield'));
this.registerEvents();
2017-09-13 12:59:58 +00:00
}
2018-10-18 07:24:20 +00:00
$postLink() {
super.$postLink();
2018-10-18 07:24:20 +00:00
this.assignDropdownProps();
this.showField = this.$.dropDown.showField;
this.valueField = this.$.dropDown.valueField;
this.refreshSelection();
}
/**
* Registers all event emitters
*/
registerEvents() {
this.input.addEventListener('focus', event => {
this.emit('focus', {event});
});
}
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
}
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 {any} The autocomplete value.
*/
get field() {
return this._field;
2017-09-13 12:59:58 +00:00
}
2018-03-09 13:15:30 +00:00
set field(value) {
this._field = value;
2018-03-09 13:15:30 +00:00
this.refreshSelection();
2018-10-18 07:24:20 +00:00
this.emit('change', {value});
2018-03-09 13:15:30 +00:00
}
2017-09-20 11:52:53 +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
}
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
const selection = this.fetchSelection();
if (!this.selection)
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
}
2018-03-09 13:15:30 +00:00
onSelectionRequest(data) {
if (data && data.length > 0) {
2018-03-09 13:15:30 +00:00
if (this.multiple)
this.selection = data;
else
this.selection = data[0];
} 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 = '';
let hasTemplate = this.$transclude && this.$transclude.isSlotFilled('tplItem');
2018-03-09 13:15:30 +00:00
if (this._selection && this.showField) {
2018-03-09 13:15:30 +00:00
if (this.multiple && Array.isArray(this._selection)) {
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];
if (hasTemplate) {
let template = this.$transclude(() => {}, null, 'tplItem').text();
display = this.$interpolate(template)(this._selection);
}
2018-03-09 13:15:30 +00:00
}
}
2018-10-31 12:44:43 +00:00
2018-03-09 13:15:30 +00:00
this.input.value = display;
if (this.translateFields) {
if (this.translateFields.indexOf(this.showField) > -1)
this.input.value = this.$translate.instant(display);
}
2018-03-09 13:15:30 +00:00
this.mdlUpdate();
2017-09-20 09:50:53 +00:00
}
2018-03-09 13:15:30 +00:00
mdlUpdate() {
let field = this.element.querySelector('.mdl-textfield');
let mdlField = field.MaterialTextfield;
if (mdlField) mdlField.updateClasses_();
}
2018-03-09 13:15:30 +00:00
setValue(value) {
this.field = value;
if (this.form) this.form.$setDirty();
2017-09-13 12:59:58 +00:00
}
onDropDownSelect(item) {
const value = item[this.valueField];
this.selection = item;
2018-03-09 13:15:30 +00:00
this.setValue(value);
2017-09-13 12:59:58 +00:00
}
2018-03-09 13:15:30 +00:00
onClearClick(event) {
event.preventDefault();
this.setValue(null);
2018-01-24 07:42:57 +00:00
}
2018-03-09 13:15:30 +00:00
onKeyDown(event) {
// if (event.defaultPrevented) return;
2017-09-13 12:59:58 +00:00
2018-03-09 13:15:30 +00:00
switch (event.keyCode) {
case 38: // Up
case 40: // Down
case 13: // Enter
this.showDropDown();
break;
default:
if (event.key.length == 1)
this.showDropDown(event.key);
else
return;
}
2017-09-13 12:59:58 +00:00
2018-03-09 13:15:30 +00:00
event.preventDefault();
2017-09-14 11:40:55 +00:00
}
2018-03-09 13:15:30 +00:00
onMouseDown(event) {
event.preventDefault();
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;
assignProps(this, this.$.dropDown, [
'valueField',
'showField',
2018-10-18 07:24:20 +00:00
'showFilter',
'multiple',
'$transclude',
'translateFields',
'model',
'data',
'url',
'fields',
'include',
'where',
'order',
'limit',
2018-10-18 07:24:20 +00:00
'searchFunction'
]);
2018-10-18 07:24:20 +00:00
}
2018-10-18 07:24:20 +00:00
showDropDown(search) {
this.assignDropdownProps();
this.$.dropDown.show(this.input, 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
}
Autocomplete.$inject = ['$element', '$scope', '$http', '$transclude', '$translate', '$interpolate'];
2017-09-13 12:59:58 +00:00
2018-02-10 15:18:01 +00:00
ngModule.component('vnAutocomplete', {
2017-09-13 12:59:58 +00:00
template: require('./autocomplete.html'),
controller: Autocomplete,
bindings: {
2018-10-18 07:24:20 +00:00
label: '@',
field: '=?',
disabled: '<?',
2019-06-17 06:14:48 +00:00
required: '@?',
2017-09-13 12:59:58 +00:00
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: '<?',
translateFields: '<?',
2019-08-02 06:27:55 +00:00
searchFunction: '&?',
fetchFunction: '<?'
2017-09-20 09:50:53 +00:00
},
transclude: {
tplItem: '?tplItem'
2018-03-09 13:15:30 +00:00
},
require: {
form: '?^form'
2017-09-13 12:59:58 +00:00
}
});