import ngModule from '../../module'; import Input from '../../lib/input'; import assignProps from '../../lib/assign-props'; import './style.scss'; /** * Input with option selector. * * @property {String} valueField The data field name that should be shown * @property {String} showFiled The data field name that should be used as value * @property {Array} data Static data for the autocomplete * @property {Object} intialData A initial data to avoid the server request used to get the selection * @property {Boolean} multiple Wether to allow multiple selection * @property {Object} selection Current object selected * * @event change Thrown when value is changed */ export default class Autocomplete extends Input { constructor($element, $scope, $http, $transclude, $translate, $interpolate) { super($element, $scope); this.$http = $http; this.$interpolate = $interpolate; this.$transclude = $transclude; this.$translate = $translate; this._field = undefined; this._selection = null; this.readonly = true; this.form = null; this.input = this.element.querySelector('.mdl-textfield__input'); componentHandler.upgradeElement( this.element.querySelector('.mdl-textfield')); } $postLink() { super.$postLink(); this.assignDropdownProps(); this.showField = this.$.dropDown.showField; this.valueField = this.$.dropDown.valueField; this.refreshSelection(); } get model() { return this._model; } set model(value) { this.dropDownAssign({model: value}); } get data() { return this._data; } set data(value) { this.dropDownAssign({data: value}); } get url() { return this._url; } set url(value) { this.dropDownAssign({url: value}); } dropDownAssign(props) { for (let prop in props) this[`_${prop}`] = props[prop]; if (this.$.dropDown) Object.assign(this.$.dropDown, props); } /** * @type {any} The autocomplete value. */ get field() { return this._field; } set field(value) { if (angular.equals(value, this._field)) return; this._field = value; this.refreshSelection(); this.emit('change', {value}); } /** * @type {Object} The selected data object, you can use this property * to prevent requests to display the initial value. */ get selection() { return this._selection; } set selection(value) { this._selection = value; this.refreshDisplayed(); } get initialData() { return this._initialData; } set initialData(value) { this._initialData = value; this.refreshSelection(); } selectionIsValid(selection) { return selection && selection[this.valueField] == this._field && selection[this.showField] != null; } refreshSelection() { if (this._field == null) { this.selection = null; return; } if (!(this.valueField && this.showField) || this.selectionIsValid(this._selection)) return; this.selection = this.fetchSelection(); } fetchSelection() { if (this.selectionIsValid(this.initialData)) return this.initialData; if (!this.$.dropDown) return null; let data; if (this.$.dropDown.model) data = this.$.dropDown.model.orgData; if (data) { let selection = data.find(i => this.selectionIsValid(i)); if (selection) return selection; } if (this.url) { let where = {}; if (this.multiple) where[this.valueField] = {inq: this.field}; else where[this.valueField] = this._field; 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() ); } return null; } onSelectionRequest(data) { if (data && data.length > 0) { if (this.multiple) this.selection = data; else this.selection = data[0]; } else this.selection = null; } refreshDisplayed() { let display = ''; let hasTemplate = this.$transclude && this.$transclude.isSlotFilled('tplItem'); if (this._selection && this.showField) { if (this.multiple && Array.isArray(this._selection)) { for (let item of this._selection) { 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); } } } this.input.value = display; if (this.translateFields) { if (this.translateFields.indexOf(this.showField) > -1) this.input.value = this.$translate.instant(display); } this.mdlUpdate(); } mdlUpdate() { let field = this.element.querySelector('.mdl-textfield'); let mdlField = field.MaterialTextfield; if (mdlField) mdlField.updateClasses_(); } setValue(value) { this.field = value; if (this.form) this.form.$setDirty(); } onDropDownSelect(value) { this.setValue(value); this.field = value; } onClearClick(event) { event.preventDefault(); this.setValue(null); } onKeyDown(event) { if (event.defaultPrevented) return; 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; } event.preventDefault(); } onMouseDown(event) { event.preventDefault(); this.showDropDown(); } onDataReady() { this.refreshSelection(); } assignDropdownProps() { if (!this.$.dropDown) return; assignProps(this, this.$.dropDown, [ 'valueField', 'showField', 'showFilter', 'multiple', '$transclude', 'translateFields', 'model', 'data', 'url', 'fields', 'include', 'where', 'order', 'limit', 'searchFunction' ]); } showDropDown(search) { this.assignDropdownProps(); this.$.dropDown.show(this.input, search); } } Autocomplete.$inject = ['$element', '$scope', '$http', '$transclude', '$translate', '$interpolate']; ngModule.component('vnAutocomplete', { template: require('./autocomplete.html'), controller: Autocomplete, bindings: { label: '@', field: '=?', disabled: '