UNPKG

simple-vue3-validator

Version:

lightweight yet flexible validation lib for vue.js - fork of simple-vue-validator

243 lines (225 loc) 7.49 kB
'use strict'; var utils = require('./utils'); var ValidationBag = require('./validation-bag'); var get = require('lodash.get'); var mixin = { Promise: null, beforeMount: function () { this.$setValidators(this.$options.validators); if (this.validation) { // set vm to validation this.validation._setVM(this); } }, beforeUnmount: function () { unwatch(this.$options.validatorsUnwatchCallbacks); }, data: function () { if (this.$options.validators) { return { validation: new ValidationBag() }; } return {}; }, methods: { $setValidators: function (validators) { unwatch(this.$options.validatorsUnwatchCallbacks); // validate methods contains all application validate codes var validateMethods = {}; this.$options.validateMethods = validateMethods; var unwatchCallbacks = []; this.$options.validatorsUnwatchCallbacks = unwatchCallbacks; // generate validate methods and watch properties change for validators if (validators) { Object.keys(validators).forEach(function (key) { var properties = key.split(','); properties = properties.map(function (property) { return property.trim(); }); var getters = properties.map(function (property) { return generateGetter(this, property); }, this); var validator = validators[key]; var options = {}; if (!utils.isFunction(validator)) { options = utils.omit(validator, 'validator'); validator = validator.validator; } if (options.cache) { // cache the validation result, so that async validator can be fast when submitting the form var option = options.cache === 'last' ? 'last' : 'all'; validator = cache(validator, option); } var validation = this.validation; var validateMethod = function () { if (utils.mode === 'conservative' && !validation.activated) { // do nothing if in conservative mode and $validate() method is not called before return getPromise().resolve(false); } var args = getters.map(function (getter) { return getter(); }); var rule = validator.apply(this, args); if (rule) { if (!rule._field) { // field defaults to the first property rule.field(properties[0]); } return this.validation.checkRule(rule); } else { return getPromise().resolve(false); } }.bind(this); // add to validate method list validateMethods[properties[0]] = validateMethod; // watch change and invoke validate method var validateMethodForWatch = validateMethod; if (options.debounce) { // TODO what if custom field name is used? var decoratedValidateMethod = function () { if (decoratedValidateMethod.sessionId !== this.validation.sessionId) { // skip validation if it's reset before return getPromise().resolve(false); } return validateMethod.apply(this, arguments); }.bind(this); var debouncedValidateMethod = utils.debounce(decoratedValidateMethod, parseInt(options.debounce)); var field = properties[0]; validateMethodForWatch = function () { // eagerly resetting passed flag if debouncing is used. this.validation.resetPassed(field); // store sessionId decoratedValidateMethod.sessionId = this.validation.sessionId; debouncedValidateMethod.apply(this, arguments); }.bind(this); } if (utils.mode !== 'manual') { // have to call $validate() to trigger validation in manual mode, so don't watch, watchProperties(this, properties, validateMethodForWatch).forEach(function (unwatch) { unwatchCallbacks.push(unwatch); }); } }, this); } }, $validate: function (fields) { if (this.validation._validate) { return this.validation._validate; } this.validation.activated = true; var validateMethods = this.$options.validateMethods; if (utils.isUndefined(fields)) { validateMethods = Object.keys(validateMethods).map(function (key) { return validateMethods[key]; }); } else { fields = utils.isArray(fields) ? fields : [fields]; validateMethods = fields.map(function (field) { return validateMethods[field]; }); } if (utils.isEmpty(validateMethods)) { return getPromise().resolve(true); } else { var always = function() { this.validation._validate = null; }.bind(this); this.validation._validate = getPromise() .all(validateMethods.map(function (validateMethod) { return validateMethod(); })) .then(function (results) { always(); return results.filter(function (result) { return !!result; }).length <= 0; }.bind(this)) .catch(function(e) { always(); throw e; }); return this.validation._validate; } } } }; function unwatch(list) { if (list) { list.forEach(function (unwatch) { unwatch(); }); } } function generateGetter(vm, property) { var names = property.split('.'); return function () { var value = vm; for (var i = 0; i < names.length; i++) { if (utils.isNull(value) || utils.isUndefined(value)) { break; } value = value[names[i]]; } return value; }; } function watchProperties(vm, properties, callback) { return properties.map(function (property) { return vm.$watch( function () { return get(vm, property) }, function () { vm.validation.setTouched(property) callback.call() } ) }); } function cache(validator, option) { return function () { var cache = validator.cache; if (!cache) { cache = []; validator.cache = cache; } var args = Array.prototype.slice.call(arguments); var cachedResult = findInCache(cache, args); if (!utils.isUndefined(cachedResult)) { return cachedResult; } var result = validator.apply(this, args); if (!utils.isUndefined(result)) { if (result.then) { return result.tab(function (promiseResult) { if (!utils.isUndefined(promiseResult)) { if (option !== 'all') { cache.splice(0, cache.length); } cache.push({args: args, result: promiseResult}); } }); } else { if (option !== 'all') { cache.splice(0, cache.length); } cache.push({args: args, result: result}); return result; } } }; } function getPromise() { if (mixin.Promise) { return mixin.Promise; } return require('es6-promise').Promise; } function findInCache(cache, args) { var items = cache.filter(function (item) { return utils.isEqual(args, item.args); }); if (!utils.isEmpty(items)) { return items[0].result; } } module.exports = mixin;