Here is a code posted originally on stackoverflow but better suited here as I was suggested.
I recently converted a code to what I think is partly at least, functional javascript. It's a web form validation process. At each step of the process, the functions perform their own layer of validation and modify the data before passing it to the next function.
This is my first attempt at 'functional' programing:
Here is the model:
function linkmodel() {
return {
title: {
content: undefined,
validation: {
type: "string",
required: true,
minLength: 1,
maxLength: 3,
validationErrorMessage: "Your title must be a valid string between 1 and 35 characters"
}
},
email: {
content: undefined,
validation: {
type: "email",
required: true,
minLength: 1,
maxLength: 60,
validationErrorMessage: "Your email must be between 1 and 50 characters"
}
},
link: {
content: undefined,
validation: {
type: "url",
required: true,
minLength: 1,
maxLength: 500,
validationErrorMessage: "Your link name must be a valid email between 1 and 50 characters"
}
},
description: {
content: undefined
}
}
}
export default linkmodel
And here is the validation system:
app.post( "/", function( req, res ) {
let form = new forms.IncomingForm()
form.parse( req, function( err, fields, files ) {
// Lodash launching the function flow
let errorMessageBag = _.flow( objectWithFieldsToValidate,
requiredFields,
stringLengthValidationCheck,
emailValidation )
let result = errorMessageBag(fields, linkmodel()) // That's the end result
console.log( "result", result )
})
// Return object containing all fields to validate and their criterias
function objectWithFieldsToValidate( fields, model ) {
// Remove all model fields that have no validation criteria in model. Description is one of those.
let modelFieldsToValidate = _.pickBy( model, function( value, key ) { return value.validation !== undefined })
// Remove from form field any entry that doesn't have a corresponding key in model
let formFieldsToValidate = _.pick( fields, Object.keys( modelFieldsToValidate ) )
_.forOwn( modelFieldsToValidate, function( value1, key1 ) {
_.forOwn( formFieldsToValidate, function( value, key ) {
if ( key1 === key ) {
modelFieldsToValidate[ key ].content = value
}
})
})
return modelFieldsToValidate
}
// Take care of required fields
function requiredFields( objectWithFieldsToValidate ) {
let okField = {}
let errors = {}
// error: field required but empty: add it to errors literal object
_.forOwn( objectWithFieldsToValidate, function( value, key ) {
if ( objectWithFieldsToValidate[ key ].validation.required === true && objectWithFieldsToValidate[ key ].content === "" ) {
errors[ key ] = objectWithFieldsToValidate[ key ].validation.validationErrorMessage
} else {
// no error: add field to litteral okField
okField[ key ] = value
}
})
return ( { "okField": okField, "errors": errors })
}
function stringLengthValidationCheck( requiredFields ) {
let validatedFields = requiredFields
_.forOwn( validatedFields.okField, function( value, key ) {
// Error: field length is not valid
if ( !validator.isLength( value[ "content" ],
{ min: value[ "validation" ].minLength, max: value[ "validation" ].maxLength }
) ) {
// Add error message to errors errors literal object
validatedFields[ "errors" ][ key ] = value[ "validation" ].validationErrorMessage
// Remove problematic field from okFields
delete validatedFields[ "okField" ][ key ]
}
})
return validatedFields
}
function emailValidation( stringLengthValidationCheck ) {
let validatedFields = stringLengthValidationCheck
_.forOwn( validatedFields.okField, function( value, key ) {
// Error
if ( value["validation"]["type"] === "email" && !validator.isEmail( value[ "content" ])) {
// Add error message to errors
validatedFields[ "errors" ][ key ] = value[ "validation" ].validationErrorMessage
// Remove problematic field from okFields
delete validatedFields[ "okField" ][ key ]
}
})
return validatedFields
}
If you see way to improve this code, I would appreciate to see what a better functional refactoring you can come up with, still using lodash.