如何编写验证函数
How to compose validation functions
我被要求编写函数来验证具有标题和描述输入的表单的数据
在下面的代码中,我使用 if
s 进行验证,但我的老师要求我将这些 if
s 转换为函数并组合它们。
validate = () => {
let titleError = '';
let descriptionError = '';
const { title, description } = this.state;
const trimTitle = title.trim();
if (trimTitle === '') titleError = 'The title cannot be empty';
if (title.length > 500)
titleError = 'The title cannot be longer than 500 characters';
if (description.length > 2000)
descriptionError =
'The description cannot be longer than 2000 characters';
if (titleError !== '' || descriptionError !== '') {
this.setState({ titleError, descriptionError });
return false;
}
return true;
};
我试过制作如下功能:
emptyTitle = ({ titleError }) => {
// let titleError = '';
const trimTitle = title.trim();
if (trimTitle === '') titleError = 'The title cannot be empty';
this.setState({ titleError });
};
maxTitleLength = ({ title }) => {
let titleError = '';
if (title.length > 500)
titleError = 'The title cannot be longer than 500 characters';
this.setState({ titleError });
};
但我不确定我的方法。拜托,我将非常感谢任何建议。
如果你想应用所有的验证规则并得到所有的错误,你可以规范化输入以便能够累加结果:
/* You can create all validation functions following the pattern:
* validationFunc(rule, msg)(normalizedParam): ValidationResult
*/
const normalizeParam = param => {
if (typeof param.errors === 'undefined') {
return {
value: param,
errors: []
}
}
return param
}
const makeError = function (param, msg) {
return {
...param,
errors: param.errors.concat(msg)
}
}
const makeRule = (msg, rule) => param => {
const normalizedParam = normalizeParam(param)
if (rule(normalizedParam.value)) {
return normalizedParam
} else {
return makeError(normalizedParam, msg)
}
}
const validationGreaterThan = (limit, msg) => makeRule(msg, function(param) {
return param > limit
})
const validationLesserThan = (limit, msg) => makeRule(msg, function(param) {
return param < limit
})
const validate = R.compose(
validationGreaterThan(10, 'The number must be greater than 10'),
validationLesserThan(100, 'The number must be lesser than 100')
)
let result = validate(1)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
result = validate(101)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
result = validate(20)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
但是如果你想削减验证过程,你可以在发现错误时抛出异常(每次验证都会抛出一个错误或者returns参数如果一切正常)。
您可以在此处研究其他方法:
这是使用 ramda cond 方法的一种可能实现:
this.state = {title: '', description: ''};
this.setState = (s) => { console.log(s); };
isEmpty = (t) => (t === '');
isLong = (t, n) => (t.length > n);
emptyTitle = (t) => isEmpty(t.trim());
longTitle = (t) => isLong(t, 500);
longDesc = (d) => isLong(d, 2000);
titleValidator = R.cond([
[emptyTitle, R.always('The title cannot be empty')],
[longTitle, R.always('The title cannot be longer than 500 characters')],
[R.T, R.always('')]
]);
descValidator = R.cond([
[longDesc, R.always('The description cannot be longer than 2000 characters')],
[R.T, R.always('')]
]);
thereIsError = (e) => (e.title !== '' || e.description !== '');
errorDetected = function (e) {
this.setState(e);
return false;
};
validate = () => {
const { title, description } = this.state;
const validationError = {
title: titleValidator(title),
description: descValidator(description)
};
const errorLog = R.cond([
[thereIsError, errorDetected],
[R.T, R.T]
]);
return errorLog(validationError);
};
console.log('Validate:', validate.call(this));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
我想你被要求避免命令式控制流,R.cond
是封装 if/else
逻辑的功能方式。希望对你有帮助。
const createValidator = (name, { min, max }) => R.cond([
[
// normalize and compare min-length
R.pipe(R.trim, R.length, R.gte(min)),
// eventually return error message
R.always(`${name} cannot be empty`)
],
[
// normalize and compare max-length
R.pipe(R.trim, R.length, R.lt(max)),
// eventually return error message
R.always(`${name} too long`)
],
// all good, no errors to display
[R.T, R.always(null)]
]);
// The following can be ignored,
// only needed as boilerplate to prove the example
const Test = () => {
const [title, setTitle] = React.useState('');
const [description, setDescription] = React.useState('');
const withValue = (cb) => R.pipe(R.pathOr('', ['target', 'value']), cb);
const isTitleValid = createValidator('Title', { min: 0, max: 500 });
const isDescriptionValid = createValidator('Description', { min: 0, max: 2000 });
return (
<form onSubmit={e => e.preventDefault()}>
<div className="form-group">
<input className="form-control" placeholder="Title" value={title} onChange={withValue(setTitle)} />
</div>
<div className="form-group">
<textarea className="form-control" placeholder="Description" onChange={withValue(setDescription)} value={description} />
</div>
<ul className="text-danger">
<li>{isTitleValid(title)}</li>
<li>{isDescriptionValid(description)}</li>
</ul>
</form>
);
};
ReactDOM.render(<Test />, document.querySelector('#test'));
form { margin: 1em 2em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<div id=test></div>
我被要求编写函数来验证具有标题和描述输入的表单的数据
在下面的代码中,我使用 if
s 进行验证,但我的老师要求我将这些 if
s 转换为函数并组合它们。
validate = () => {
let titleError = '';
let descriptionError = '';
const { title, description } = this.state;
const trimTitle = title.trim();
if (trimTitle === '') titleError = 'The title cannot be empty';
if (title.length > 500)
titleError = 'The title cannot be longer than 500 characters';
if (description.length > 2000)
descriptionError =
'The description cannot be longer than 2000 characters';
if (titleError !== '' || descriptionError !== '') {
this.setState({ titleError, descriptionError });
return false;
}
return true;
};
我试过制作如下功能:
emptyTitle = ({ titleError }) => {
// let titleError = '';
const trimTitle = title.trim();
if (trimTitle === '') titleError = 'The title cannot be empty';
this.setState({ titleError });
};
maxTitleLength = ({ title }) => {
let titleError = '';
if (title.length > 500)
titleError = 'The title cannot be longer than 500 characters';
this.setState({ titleError });
};
但我不确定我的方法。拜托,我将非常感谢任何建议。
如果你想应用所有的验证规则并得到所有的错误,你可以规范化输入以便能够累加结果:
/* You can create all validation functions following the pattern:
* validationFunc(rule, msg)(normalizedParam): ValidationResult
*/
const normalizeParam = param => {
if (typeof param.errors === 'undefined') {
return {
value: param,
errors: []
}
}
return param
}
const makeError = function (param, msg) {
return {
...param,
errors: param.errors.concat(msg)
}
}
const makeRule = (msg, rule) => param => {
const normalizedParam = normalizeParam(param)
if (rule(normalizedParam.value)) {
return normalizedParam
} else {
return makeError(normalizedParam, msg)
}
}
const validationGreaterThan = (limit, msg) => makeRule(msg, function(param) {
return param > limit
})
const validationLesserThan = (limit, msg) => makeRule(msg, function(param) {
return param < limit
})
const validate = R.compose(
validationGreaterThan(10, 'The number must be greater than 10'),
validationLesserThan(100, 'The number must be lesser than 100')
)
let result = validate(1)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
result = validate(101)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
result = validate(20)
if (result.errors.length) {
result.errors.forEach(err => console.log(err))
} else {
console.log('Success!')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
但是如果你想削减验证过程,你可以在发现错误时抛出异常(每次验证都会抛出一个错误或者returns参数如果一切正常)。
您可以在此处研究其他方法:
这是使用 ramda cond 方法的一种可能实现:
this.state = {title: '', description: ''};
this.setState = (s) => { console.log(s); };
isEmpty = (t) => (t === '');
isLong = (t, n) => (t.length > n);
emptyTitle = (t) => isEmpty(t.trim());
longTitle = (t) => isLong(t, 500);
longDesc = (d) => isLong(d, 2000);
titleValidator = R.cond([
[emptyTitle, R.always('The title cannot be empty')],
[longTitle, R.always('The title cannot be longer than 500 characters')],
[R.T, R.always('')]
]);
descValidator = R.cond([
[longDesc, R.always('The description cannot be longer than 2000 characters')],
[R.T, R.always('')]
]);
thereIsError = (e) => (e.title !== '' || e.description !== '');
errorDetected = function (e) {
this.setState(e);
return false;
};
validate = () => {
const { title, description } = this.state;
const validationError = {
title: titleValidator(title),
description: descValidator(description)
};
const errorLog = R.cond([
[thereIsError, errorDetected],
[R.T, R.T]
]);
return errorLog(validationError);
};
console.log('Validate:', validate.call(this));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
我想你被要求避免命令式控制流,R.cond
是封装 if/else
逻辑的功能方式。希望对你有帮助。
const createValidator = (name, { min, max }) => R.cond([
[
// normalize and compare min-length
R.pipe(R.trim, R.length, R.gte(min)),
// eventually return error message
R.always(`${name} cannot be empty`)
],
[
// normalize and compare max-length
R.pipe(R.trim, R.length, R.lt(max)),
// eventually return error message
R.always(`${name} too long`)
],
// all good, no errors to display
[R.T, R.always(null)]
]);
// The following can be ignored,
// only needed as boilerplate to prove the example
const Test = () => {
const [title, setTitle] = React.useState('');
const [description, setDescription] = React.useState('');
const withValue = (cb) => R.pipe(R.pathOr('', ['target', 'value']), cb);
const isTitleValid = createValidator('Title', { min: 0, max: 500 });
const isDescriptionValid = createValidator('Description', { min: 0, max: 2000 });
return (
<form onSubmit={e => e.preventDefault()}>
<div className="form-group">
<input className="form-control" placeholder="Title" value={title} onChange={withValue(setTitle)} />
</div>
<div className="form-group">
<textarea className="form-control" placeholder="Description" onChange={withValue(setDescription)} value={description} />
</div>
<ul className="text-danger">
<li>{isTitleValid(title)}</li>
<li>{isDescriptionValid(description)}</li>
</ul>
</form>
);
};
ReactDOM.render(<Test />, document.querySelector('#test'));
form { margin: 1em 2em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<div id=test></div>