isomorphic-search
Version:
A configurable search interface.
149 lines (117 loc) • 3.68 kB
JavaScript
/**
* Search agent's control.
*/
import abstraction from './abstraction';
import presentation from './presentation';
let control = {
// Runs module with the given configurations.
constructor(data=[], options={}) {
// Configure.
this.abstraction = Object.assign(abstraction, options);
this.presentation = presentation;
// Run.
this.set(data);
// Enable chaining.
return this;
},
// Instantiate & configure search engine.
engine(engine, data, options) {
// Notify if search engine has not been set.
if (!engine) {
return this.abstraction.notify(this.abstraction.messages.engine);
}
// Instantiate.
if (typeof engine === 'function') {
engine = new engine(data, options);
}
// Get search method.
if (engine.search && typeof engine.search === 'function') {
engine = engine.search.bind(engine);
}
// Save instantiated engine to abstraction.
this.abstraction.search = engine;
return engine;
},
// Executes search and shows results.
onSearch(data=[], search='', engine) {
let results = engine(search);
this.presentation.renderSearchResults(results, this.abstraction);
return results;
},
/**
* Sends user's search to server.
*
* @param {string} input An element selector for the search input.
* @return {string} The user's entry into the search input.
*/
execute(abstraction, data, engine) {
let {$el, attr} = abstraction,
$input = $el.input,
search = $input.val(),
hiddenClass = attr.hidden,
eachResult = $el.result;
// Handle empty search value.
if (!search) {
// Show all possible results.
eachResult.removeClass(hiddenClass);
return search;
}
// Initiate search for user input.
this.onSearch(data, search, engine);
return search;
},
/**
* Sets search form and its events on the view.
*
* @return {array} An array of searchable objects.
*/
set(data) {
const model = this.abstraction,
valid = model.validate(data);
if (valid !== true) {
return this.abstraction.notify(valid);
}
// Render form and bind events to it.
this.renderForm(data);
// Render results.
this.renderResults(model, data);
return data;
},
// Sets search form on view, and sets events in anticipation of a search.
renderForm(data) {
const model = this.abstraction,
$parent = model.$el.parent,
form = model.template.form,
engine = this.engine(model.engine, data, model.options),
metadata = model.metadata,
callback = metadata && metadata.form && metadata.form.callback ?
metadata.form.callback : this.execute.bind(this);
// Render form.
this.presentation.render($parent, form(model), {action: 'prepend'});
// Bind events to view.
this.presentation.bind(model, data, engine, callback);
return true;
},
// Render results.
renderResults(model, data) {
let {$el, metadata, template} = model,
callback = metadata && metadata.result ?
metadata.result.callback : false;
// Render results view.
this.presentation.render(
$el.container, template.results(model), {action: 'append'});
// Get results list.
let resultsList = model.$el.resultsList;
// Render each potential result.
data.forEach((datum) => {
// Render.
this.presentation.render(
resultsList, template.list(datum, model), {action: 'append'});
// Execute callback on this list item.
if (callback) {
callback(datum);
}
});
}
}
export default control;