express-form-handler
Version:
A form handler for express, the nodejs framework.
160 lines (132 loc) • 3.93 kB
JavaScript
/**
* @licence MIT
* @author Louis Audeon <louis.audeon@mail.be>
*/
const Field = require('./field/field')
const Strategy = require('express-form-handler-strategy')
const Options = require('options')
let acceptedMethods = ['post', 'put', 'patch', 'POST', 'PUT', 'PATCH']
// Private API
// @returns {Promise}
let validate = function () {
if (!this.configs.validationByModel) return validateByForm.call(this)
return validateByModel.call(this)
}
// Private API
// @returns {Promise}
let validateByModel = function () {
return new Promise(function (resolve, reject) {
this.modelStrategy
.validate()
.then(() => resolve())
.catch(function (errors) {
this.errors.concat(errors)
resolve()
}.bind(this))
}.bind(this))
}
// Private API
// @returns {Promise}
let validateByForm = function () {
return new Promise(function (resolve, reject) {
for (let field of this.fields) {
try {
field.check(this.fields)
} catch (e) {
reject(e)
}
if (field.errors.length > 0) this.errors.concat(field.errors)
resolve()
}
}.bind(this))
}
// Private API
let send = function (req, res, next) {
return validate.call(this)
.then(function () {
req.form = {}
req.form.model = this.configs.modelStrategy.modelInstance || null
req.form.isValid = this.errors.length === 0
req.form.errors = this.errors
req.form.body = this.fields
return next()
}.bind(this))
.catch(err => next(err))
}
// Public API
// Constructor
let Form = function (fields = []) {
if (Array.isArray(fields) === false) throw new Error('You must pass an array')
this.fields = fields.map(field => new Field(field))
this.errors = []
// Avoid error due to express middleware context
this.process = this.process.bind(this)
// Default configurations
this.configs = {
modelStrategy: null,
validationByModel: false,
modelSourcing: null
}
}
// Express route middleware
Form.prototype.process = function (req, res, next) {
if (!acceptedMethods.includes(req.method)) {
return next(
new Error(`Expected post, put or patch method. ${req.method} given`)
)
}
for (let field of this.fields) {
if (!req.body.hasOwnProperty(field.definition.name)) {
return next(
new Error(`No field found in the request body for the field name: ${field.definition.name}`)
)
}
field.value = req.body[field.definition.name]
}
if (this.configs.modelStrategy) {
return this.configs.modelStrategy
.update(req)
.then(function () {
send.call(this, req, res, next)
}.bind(this))
.catch(e => next(e))
}
return send.call(this, req, res, next)
}
Form.prototype.addField = function (field) {
this.fields.push(field)
}
// "Extend" the current Form instance with the given one
Form.prototype.extends = function (form) {
if (typeof form !== 'object' || form instanceof Form === false) {
throw new Error('Extends method expects a Form instance')
}
// Push new fields
form.fields.forEach(function (field) {
this.fields.push(field)
}, this)
return this
}
// Overwrite default Form configuration
Form.prototype.config = function (options) {
let opts
let defaultOptions = this.configs
opts = new Options(defaultOptions)
opts = opts.merge(options)
if (
opts.isDefinedAndNonNull('modelStrategy') &&
opts.value.modelStrategy instanceof Strategy === false
) throw new Error('A strategy must be an instance of Strategy')
if (
opts.isDefined('validationByModel') &&
typeof opts.value.validationByModel !== 'boolean'
) throw new Error('Validation model option must be a boolean')
this.configs = {
modelStrategy: opts.value.modelStrategy,
validationByModel: opts.value.validationByModel,
modelSourcing: opts.value.modelSourcing
}
return this
}
module.exports = exports = (fields) => new Form(fields)