UNPKG

keystone

Version:

Web Application Framework and Admin GUI / Content Management System built on Express.js and Mongoose

185 lines (173 loc) 5.2 kB
var _ = require('lodash'); var FieldType = require('../Type'); var util = require('util'); var utils = require('keystone-utils'); /** * Select FieldType Constructor * @extends Field * @api public */ function select (list, path, options) { this.ui = options.ui || 'select'; this.numeric = options.numeric ? true : false; this._nativeType = (options.numeric) ? Number : String; this._underscoreMethods = ['format', 'pluck']; this._properties = ['ops', 'numeric']; if (typeof options.options === 'string') { options.options = options.options.split(','); } if (!Array.isArray(options.options)) { throw new Error('Select fields require an options array.'); } this.ops = options.options.map(function (i) { var op = typeof i === 'string' ? { value: i.trim(), label: utils.keyToLabel(i) } : i; if (!_.isObject(op)) { op = { label: '' + i, value: '' + i }; } if (options.numeric && !_.isNumber(op.value)) { op.value = Number(op.value); } return op; }); // undefined options.emptyOption defaults to true if (options.emptyOption === undefined) { options.emptyOption = true; } // ensure this.emptyOption is a boolean this.emptyOption = !!options.emptyOption; // cached maps for options, labels and values this.map = utils.optionsMap(this.ops); this.labels = utils.optionsMap(this.ops, 'label'); this.values = _.map(this.ops, 'value'); select.super_.call(this, list, path, options); } select.properName = 'Select'; util.inherits(select, FieldType); /** * Registers the field on the List's Mongoose Schema. * * Adds a virtual for accessing the label of the selected value, * and statics to the Schema for converting a value to a label, * and retrieving all of the defined options. */ select.prototype.addToSchema = function (schema) { var field = this; this.paths = { data: this.options.dataPath || this.path + 'Data', label: this.options.labelPath || this.path + 'Label', options: this.options.optionsPath || this.path + 'Options', map: this.options.optionsMapPath || this.path + 'OptionsMap', }; schema.path(this.path, _.defaults({ type: this._nativeType, enum: this.values, set: function (val) { return (val === '' || val === null || val === false) ? undefined : val; }, }, this.options)); schema.virtual(this.paths.data).get(function () { return field.map[this.get(field.path)]; }); schema.virtual(this.paths.label).get(function () { return field.labels[this.get(field.path)]; }); schema.virtual(this.paths.options).get(function () { return field.ops; }); schema.virtual(this.paths.map).get(function () { return field.map; }); this.bindUnderscoreMethods(); }; /** * Returns a key value from the selected option */ select.prototype.pluck = function (item, property, _default) { var option = item.get(this.paths.data); return (option) ? option[property] : _default; }; /** * Retrieves a shallow clone of the options array */ select.prototype.cloneOps = function () { return _.map(this.ops, _.clone); }; /** * Retrieves a shallow clone of the options map */ select.prototype.cloneMap = function () { return utils.optionsMap(this.ops, true); }; /** * Add filters to a query */ select.prototype.addFilterToQuery = function (filter) { var query = {}; if (!Array.isArray(filter.value)) { if (filter.value) { filter.value = [filter.value]; } else { filter.value = []; } } if (filter.value.length > 1) { query[this.path] = (filter.inverted) ? { $nin: filter.value } : { $in: filter.value }; } else if (filter.value.length === 1) { query[this.path] = (filter.inverted) ? { $ne: filter.value[0] } : filter.value[0]; } else { query[this.path] = (filter.inverted) ? { $nin: ['', null] } : { $in: ['', null] }; } return query; }; /** * Asynchronously confirms that the provided value is valid */ select.prototype.validateInput = function (data, callback) { var value = this.getValueFromData(data); if (typeof value === 'string' && this.numeric) { value = utils.number(value); } var result = value === undefined || value === null || value === '' || (value in this.map) ? true : false; utils.defer(callback, result); }; /** * Asynchronously confirms that the provided value is present */ select.prototype.validateRequiredInput = function (item, data, callback) { var value = this.getValueFromData(data); var result = false; if (value === undefined) { if (item.get(this.path)) { result = true; } } else if (value) { if (value !== '') { // This is already checkind in validateInput, but it doesn't hurt // to check again for security if (value in this.map) { result = true; } } } utils.defer(callback, result); }; /** * Validates that a valid option has been provided in a data object * * Deprecated */ select.prototype.inputIsValid = function (data, required, item) { if (data[this.path]) { return (data[this.path] in this.map) ? true : false; } else { return (!required || (!(this.path in data) && item && item.get(this.path))) ? true : false; } }; /** * Formats the field value */ select.prototype.format = function (item) { return this.labels[item.get(this.path)] || ''; }; /* Export Field Type */ module.exports = select;