import ngModule from '../../module'; import Component from '../../lib/component'; import {buildFilter} from 'vn-loopback/util/filter'; import focus from '../../lib/focus'; import './style.scss'; /** * An input specialized to perform searches, it allows to use a panel * for advanced searches when the panel property is defined. * When model and exprBuilder properties are used, the model is updated * automatically with an and-filter exprexion in which each operand is built * by calling the exprBuilder function for each non-null parameter. * * @property {Object} filter A key-value object with filter parameters * @property {SearchPanel} panel The panel used for advanced searches */ export default class Controller extends Component { constructor($element, $) { super($element, $); this.searchState = '.'; let criteria = {}; this.deregisterCallback = this.$transitions.onSuccess( criteria, () => this.onStateChange()); } $postLink() { this.onStateChange(); } $onDestroy() { this.deregisterCallback(); } get filter() { return this._filter; } set filter(value) { this._filter = value; this.toBar(value); } get shownFilter() { return this.filter != null ? this.filter : this.suggestedFilter; } get searchString() { return this._searchString; } set searchString(value) { this._searchString = value; if (value == null) this.params = []; } onStateChange() { let filter = null; if (this.$state.is(this.searchState)) { if (this.$params.q) { try { filter = JSON.parse(this.$params.q); } catch (e) { console.error(e); } } focus(this.element.querySelector('vn-textfield input')); } this.filter = filter; } openPanel(event) { if (event.defaultPrevented) return; this.$.popover.show(this.element); this.$panelScope = this.$.$new(); this.panelEl = this.$compile(`<${this.panel}/>`)(this.$panelScope)[0]; let panel = this.panelEl.$ctrl; if (this.shownFilter) panel.filter = JSON.parse(JSON.stringify(this.shownFilter)); panel.onSubmit = filter => this.onPanelSubmit(filter.$filter); this.$.popover.content.appendChild(this.panelEl); } onPopoverClose() { this.$panelScope.$destroy(); this.$panelScope = null; this.panelEl.remove(); this.panelEl = null; } onPanelSubmit(filter) { this.$.popover.hide(); filter = compact(filter); filter = filter != null ? filter : {}; this.doSearch(filter); } onSubmit() { this.doSearch(this.fromBar()); } removeParam(index) { this.params.splice(index, 1); this.doSearch(this.fromBar()); } doSearch(filter) { this.filter = filter; let opts = this.$state.is(this.searchState) ? {location: 'replace'} : null; this.$state.go(this.searchState, {q: JSON.stringify(filter)}, opts); } fromBar() { let filter = {}; if (this.searchString) filter.search = this.searchString; if (this.params) { for (let param of this.params) filter[param.key] = param.value; } return filter; } toBar(filter) { this.params = []; this.searchString = filter && filter.search; if (!filter) return; let keys = Object.keys(filter); keys.forEach(key => { if (key == 'search') return; let value = filter[key]; let chip; if (typeof value == 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) value = new Date(value); switch (typeof value) { case 'boolean': chip = `${value ? '' : 'not '}${key}`; break; case 'number': case 'string': chip = `${key}: ${value}`; break; default: if (value instanceof Date) { let format = 'yyyy-MM-dd'; if (value.getHours() || value.getMinutes()) format += ' HH:mm'; chip = `${key}: ${this.$filter('date')(value, format)}`; } else chip = key; } this.params.push({chip, key, value}); }); } } ngModule.vnComponent('vnSearchbar', { controller: Controller, template: require('./searchbar.html'), bindings: { searchState: '@?', filter: ' this.onStateChange()); this.fetchFilter(); } $postLink() { if (this.filter !== null) this.doSearch(); } $onDestroy() { this.deregisterCallback(); } fetchFilter() { if (this.$state.params.q) { try { this.filter = JSON.parse(this.$state.params.q); } catch (e) { console.error(e); } } else this.filter = null; } onStateChange() { this.fetchFilter(); this.doSearch(); } doSearch() { let filter = this.filter; if (filter == null && this.autoload) filter = {}; if (this.onSearch) this.onSearch({$params: filter}); if (this.model) { if (filter !== null) { let where = buildFilter(filter, (param, value) => this.exprBuilder({param, value})); let userParams = {}; let hasParams = false; if (this.paramBuilder) { for (let param in filter) { let value = filter[param]; if (value == null) continue; let expr = this.paramBuilder({param, value}); if (expr) { Object.assign(userParams, expr); hasParams = true; } } } this.model.applyFilter( where ? {where} : null, hasParams ? userParams : null ); } else this.model.clear(); } } exprBuilder(param, value) { return {[param]: value}; } } AutoSearch.$inject = ['$state', '$transitions']; ngModule.vnComponent('vnAutoSearch', { controller: AutoSearch, bindings: { model: '= 0; i--) { if (compact(obj[i]) === undefined) obj.splice(i, 1); } if (obj.length == 0) return undefined; } else if (typeof obj == 'object' && obj.constructor == Object) { let keys = Object.keys(obj); for (let key of keys) { if (key.charAt(0) == '$' || compact(obj[key]) === undefined) delete obj[key]; } if (Object.keys(obj).length == 0) return undefined; } return obj; }