UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

169 lines (153 loc) • 5.42 kB
import { Model, IModelSetOptions } from './Model'; import { Assert } from '../misc/Assert'; import { IStringMap } from '../rest/GenericParam'; import * as _ from 'underscore'; import { Utils } from '../utils/Utils'; export const QUERY_STATE_ATTRIBUTES = { Q: 'q', FIRST: 'first', T: 't', TG: 'tg', SORT: 'sort', LAYOUT: 'layout', HD: 'hd', HQ: 'hq', QUICKVIEW: 'quickview', DEBUG: 'debug', NUMBER_OF_RESULTS: 'numberOfResults', MISSING_TERMS: 'missingTerms' }; export interface IQueryStateIncludedAttribute { title: string; included: string[]; } export interface IQueryStateExcludedAttribute { title: string; excluded: string[]; } /** * The `QueryStateModel` class is a key-value store which contains the current state of the components that can affect * the query (see [State](https://docs.coveo.com/en/344/)). This class inherits from the [`Model`](https://coveo.github.io/search-ui/classes/model.html) * class. Optionally, it is possible to persist the state in the query string in order to enable browser history * management (see the [`HistoryController`]{@link HistoryController} class). * * Components set values in the `QueryStateModel` instance to reflect their current state. The `QueryStateModel` * triggers state events (see [`eventTypes`]{@link Model.eventTypes}) whenever one of its values is modified. Components * listen to triggered state events to update themselves when appropriate. * * For instance, when a query is triggered, the [`Searchbox`]{@link Searchbox} component sets the `q` attribute (the * basic query expression), while the [`Pager`]{@link Pager} component sets the `first` attribute (the index of the * first result to display in the result list), and so on. * * **Example:** * * > The user modifies the content of the `Searchbox` and submits a query. This triggers the following state events: * > - `state:change:q` (because the value of `q` has changed). * > - `state:change` (because at least one value has changed in the `QueryStateModel`). * > * > Components or external code can attach handlers to those events: * > ```javascript * > Coveo.$$(document).on('state:change:q', function() { * > [ ... ] * > }); * > ``` * * **Note:** * > Normally, you should interact with the `QueryStateModel` instance using the [`Coveo.state`]{@link state} top-level * > function. */ export class QueryStateModel extends Model { static ID = 'state'; static defaultAttributes = { q: '', first: 0, fv: '', t: '', hd: '', hq: '', sort: '', layout: 'list', tg: '', quickview: '', debug: false, numberOfResults: 10, missingTerms: [] }; static attributesEnum = { q: 'q', first: 'first', fv: 'fv', t: 't', sort: 'sort', layout: 'layout', hd: 'hd', hq: 'hq', tg: 'tg', quickview: 'quickview', debug: 'debug', numberOfResults: 'numberOfResults', missingTerms: 'missingTerms' }; static getFacetId(id: string, include: boolean = true) { return 'f:' + id + (include ? '' : ':not'); } static getFacetOperator(id: string) { return 'f:' + id + ':operator'; } static getFacetLookupValue(id: string) { return QueryStateModel.getFacetId(id) + ':lookupvalues'; } /** * Creates a new `QueryStateModel` instance. * @param element The HTMLElement on which to instantiate the `QueryStateModel`. * @param attributes The state key-value store to instantiate the `QueryStateModel` with. */ constructor(element: HTMLElement, attributes?: IStringMap<string>) { const merged = { ...QueryStateModel.defaultAttributes, ...attributes }; super(element, QueryStateModel.ID, merged); } /** * Validates whether at least one facet is currently active (has selected or excluded values) in the interface. * * @returns {boolean} `true` if at least one facet is active; `false` otherwise. */ public atLeastOneFacetIsActive() { return !_.isUndefined( _.find(this.attributes, (value, key: string) => { return key.match(/^f:/) && !Utils.arrayEqual(this.getDefault(key), value); }) ); } public set(attribute: string, value: any, options?: IModelSetOptions) { this.validate(attribute, value); super.set(attribute, value, options); } private validate(attribute: string, value: any) { if (attribute == QueryStateModel.attributesEnum.first) { Assert.isNumber(value); Assert.isLargerOrEqualsThan(0, value); } } } export function setState(model: Model, args: any[]): any { Assert.exists(model); if (args.length == 0 || args[0] == undefined) { // No args means return the model return model; } else if (args.length == 1 && Utils.isNonEmptyString(args[0])) { // One string arg means retrieve value from model return model.get(args[0]); } else if (_.isObject(args[0])) { // One dictionary means set multiple values let toSet = args[0]; let options = _.extend(<IModelSetOptions>{ customAttribute: true }, <IModelSetOptions>args[1]); return model.setMultiple(toSet, options); } else if (args.length > 1) { // Otherwise we're setting a value let name = <string>args[0]; let value = args[1]; let options = _.extend(<IModelSetOptions>{ customAttribute: true }, <IModelSetOptions>args[2]); Assert.isNonEmptyString(name); return model.set(name, value, options); } }