{"id":15029170,"url":"https://github.com/caolan/forms","last_synced_at":"2025-05-15T00:09:36.579Z","repository":{"id":47076716,"uuid":"732377","full_name":"caolan/forms","owner":"caolan","description":"An easy way to create, parse and validate forms in node.js","archived":false,"fork":false,"pushed_at":"2024-03-16T17:06:52.000Z","size":704,"stargazers_count":1011,"open_issues_count":35,"forks_count":167,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-04-13T21:34:02.355Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/caolan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2010-06-21T17:29:45.000Z","updated_at":"2025-03-19T03:57:06.000Z","dependencies_parsed_at":"2022-09-13T02:02:41.478Z","dependency_job_id":"bd16613d-4b30-4bf6-a323-f3d4ff96f143","html_url":"https://github.com/caolan/forms","commit_stats":{"total_commits":564,"total_committers":46,"mean_commits":12.26086956521739,"dds":0.3351063829787234,"last_synced_commit":"9f118a89cf5c42e9013beeff89ba898652958e52"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caolan%2Fforms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caolan%2Fforms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caolan%2Fforms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caolan%2Fforms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caolan","download_url":"https://codeload.github.com/caolan/forms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254249206,"owners_count":22039029,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T20:09:52.704Z","updated_at":"2025-05-15T00:09:31.565Z","avatar_url":"https://github.com/caolan.png","language":"JavaScript","readme":"# Forms \u003csup\u003e[![Version Badge][npm-version-svg]][npm-url]\u003c/sup\u003e\n\n[![Build Status][travis-svg]][travis-url]\n[![dependency status][deps-svg]][deps-url]\n[![dev dependency status][dev-deps-svg]][dev-deps-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][npm-badge-png]][npm-url]\n\nConstructing a good form by hand is a lot of work. Popular frameworks like\nRuby on Rails and Django contain code to make this process less painful.\nThis module is an attempt to provide the same sort of helpers for node.js.\n\n```shell\n$ npm install forms\n```\n\n## Contribute\n\nThis code is still in its infancy, and I'd really appreciate any contributions,\nbug reports, or advice. Especially on the following key areas:\n\n* __Creating sensible default rendering functions that generate flexible,\n  accessible markup__. This is an early priority because without being\n  confident that the standard markup won't change under their feet, developers\n  will not be able to adopt the module for any sort of production use.\n* __Exploring write-once validation that works on the client and the server__.\n  There are some unique advantages to using the same language at both ends,\n  let's try and make the most of it!\n* __Ensuring it's easy to use with existing node web frameworks__. Ideally this\n  module would integrate well with projects using any of the popular frameworks.\n### [Contributors](https://github.com/caolan/forms/contributors)\n\n* [ljharb](https://github.com/ljharb)\n* [ivanquirino](https://github.com/ivanquirino)\n* [richardngn](https://github.com/richardngn)\n* [caulagi](https://github.com/caulagi)\n* …and [many more](https://github.com/caolan/forms/graphs/contributors)\n\n## Example\n\nCreating an example registration form:\n\n```javascript\nvar forms = require('forms');\nvar fields = forms.fields;\nvar validators = forms.validators;\n\nvar reg_form = forms.create({\n    username: fields.string({ required: true }),\n    password: fields.password({ required: validators.required('You definitely want a password') }),\n    confirm:  fields.password({\n        required: validators.required('don\\'t you know your own password?'),\n        validators: [validators.matchField('password')]\n    }),\n    email: fields.email()\n});\n```\n\nRendering a HTML representation of the form:\n\n```javascript\nreg_form.toHTML();\n```\n\nWould produce:\n\n```html\n\u003cdiv class=\"field required\"\u003e\n    \u003clabel for=\"id_username\"\u003eUsername\u003c/label\u003e\n    \u003cinput type=\"text\" name=\"username\" id=\"id_username\" value=\"test\" /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field required\"\u003e\n    \u003clabel for=\"id_password\"\u003ePassword\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"password\" id=\"id_password\" value=\"test\" /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field required\"\u003e\n    \u003clabel for=\"id_confirm\"\u003eConfirm\u003c/label\u003e\n    \u003cinput type=\"password\" name=\"confirm\" id=\"id_confirm\" value=\"test\" /\u003e\n\u003c/div\u003e\n\u003cdiv class=\"field\"\u003e\n    \u003clabel for=\"id_email\"\u003eEmail\u003c/label\u003e\n    \u003cinput type=\"text\" name=\"email\" id=\"id_email\" /\u003e\n\u003c/div\u003e\n```\n\nYou'll notice you have to provide your own form tags and submit button, its\nmore flexible this way ;)\n\nHandling a request:\n\n```javascript\nfunction myView(req, res) {\n    reg_form.handle(req, {\n        success: function (form) {\n            // there is a request and the form is valid\n            // form.data contains the submitted data\n        },\n        error: function (form) {\n            // the data in the request didn't validate,\n            // calling form.toHTML() again will render the error messages\n        },\n        empty: function (form) {\n            // there was no form data in the request\n        }\n    });\n}\n```\n\nThat's it! For more detailed / working examples look in the example folder.\nAn example server using the form above can be run by doing:\n\n```shell\n$ node example/simple.js\n```\n\n### Bootstrap compatible output\nFor integrating with Twitter bootstrap 3 (horizontal form), this is what you need to do:\n\n```javascript\nvar widgets = require('forms').widgets;\n\nvar my_form = forms.create({\n    title: fields.string({\n        required: true,\n        widget: widgets.text({ classes: ['input-with-feedback'] }),\n        errorAfterField: true,\n        cssClasses: {\n            label: ['control-label col col-lg-3']\n        }\n    }),\n    description: fields.string({\n        errorAfterField: true,\n        widget: widgets.text({ classes: ['input-with-feedback'] }),\n        cssClasses: {\n            label: ['control-label col col-lg-3']\n        }\n    })\n});\n\nvar bootstrapField = function (name, object) {\n    if (!Array.isArray(object.widget.classes)) { object.widget.classes = []; }\n    if (object.widget.classes.indexOf('form-control') === -1) {\n        object.widget.classes.push('form-control');\n    }\n\n    var label = object.labelHTML(name);\n    var error = object.error ? '\u003cdiv class=\"alert alert-error help-block\"\u003e' + object.error + '\u003c/div\u003e' : '';\n\n    var validationclass = object.value \u0026\u0026 !object.error ? 'has-success' : '';\n    validationclass = object.error ? 'has-error' : validationclass;\n\n    var widget = object.widget.toHTML(name, object);\n    return '\u003cdiv class=\"form-group ' + validationclass + '\"\u003e' + label + widget + error + '\u003c/div\u003e';\n};\n```\n\nAnd while rendering it:\n\n```javascript\nreg_form.toHTML(bootstrapField);\n```\n\n## Available types\n\nA list of the fields, widgets, validators and renderers available as part of\nthe forms module. Each of these components can be switched with customised\ncomponents following the same API.\n\n### Fields\n\n* string\n* number\n* boolean\n* array\n* password\n* email\n* tel\n* url\n* date\n\n### Widgets\n\n* text\n* email\n* number\n* password\n* hidden\n* color\n* tel\n* date\n* datetimeLocal\n* checkbox\n* select\n* textarea\n* multipleCheckbox\n* multipleRadio\n* multipleSelect\n* label\n\n### Validators\n\n* matchField\n* matchValue\n* required\n* requiresFieldIfEmpty\n* min\n* max\n* range\n* minlength\n* maxlength\n* rangelength\n* regexp\n* color\n* email\n* url\n* date\n* datetimeLocal\n* alphanumeric\n* digits\n* integer\n\n### Renderers\n\n* div\n* p\n* li\n* table\n\n\n## API\n\nA more detailed look at the methods and attributes available. Most of these\nyou will not need to use directly.\n\n### forms.create(fields)\n\nConverts a form definition (an object literal containing field objects) into a\nform object.\n\n#### forms.create(fields, options)\n\nForms can be created with an optional \"options\" object as well.\n\n#### Supported options:\n\n* `validatePastFirstError`: `true`, otherwise assumes `false`\n  * If `false`, the first validation error will halt form validation.\n  * If `true`, all fields will be validated.\n\n\n### Form object\n\n#### Attributes\n\n* ``fields`` - Object literal containing the field objects passed to the create\n  function\n\n#### form.handle(req, callbacks)\n\nInspects a request or object literal and binds any data to the correct fields.\n\n#### form.bind(data)\n\nBinds data to correct fields, returning a new bound form object.\n\n#### form.toHTML(iterator)\n\nRuns toHTML on each field returning the result. If an iterator is specified,\nit is called for each field with the field name and object as its arguments,\nthe iterator's results are concatenated to create the HTML output, allowing\nfor highly customised markup.\n\n\n### Bound Form object\n\nContains the same methods as the unbound form, plus:\n\n#### Attributes\n\n* ``data`` - Object containing all the parsed data keyed by field name\n* ``fields`` - Object literal containing the field objects passed to the create\n  function\n\n#### form.validate(callback)\n\nCalls validate on each field in the bound form and returns the resulting form\nobject to the callback.\n\n#### form.isValid()\n\nChecks all fields for an error attribute. Returns false if any exist, otherwise\nreturns true.\n\n#### form.toHTML(iterator)\n\nRuns toHTML on each field returning the result. If an iterator is specified,\nit is called for each field with the field name and object as its arguments,\nthe iterator's results are concatenated to create the HTML output, allowing\nfor highly customised markup.\n\n\n### Field object\n\n#### Attributes\n\n* ``label`` - Optional label text which overrides the default\n* ``required`` - Boolean describing whether the field is mandatory\n* ``validators`` - An array of functions which validate the field data\n* ``widget`` - A widget object to use when rendering the field\n* ``id`` - An optional id to override the default\n* ``choices`` - A list of options, used for multiple choice fields (see the field.choices section below)\n* ``cssClasses`` - A list of CSS classes for label and field wrapper\n* ``hideError`` - if true, errors won't be rendered automatically\n* ``labelAfterField`` - if true, the label text will be displayed after the field, rather than before\n* ``errorAfterField`` - if true, the error message will be displayed after the field, rather than before\n* ``fieldsetClasses`` - for widgets with a fieldset (multipleRadio and multipleCheckbox), set classes for the fieldset\n* ``legendClasses`` - for widgets with a fieldset (multipleRadio and multipleCheckbox), set classes for the fieldset's legend\n\n#### field.choices\n\nThe choices property is used for radio, checkbox, and select fields. Two\nformats are supported and in case of select fields the format can be nested once to support option groups.\n\nThe first format is based on objects and is easy to write. Object keys are treated as values and object values are treated as labels. If the value is another object and nesting is supported by the widget the key will be used as label and the value as nested list.\n\nThe second format is array-based and therefore ordered (object keys are unordered by definition). The array should contain arrays with two values the first being the value and the second being the label. If the label is an array and nesting is supported by the widget the value will be used as label and the label as nested list.\n\nBoth formats are demonstrated below:\n\n```\n// objects\n{\n    'val-1': 'text-1',\n    'val-2': 'text-2',\n    'text-3': {\n        'nested-val-1': 'nested-text-1',\n        'nested-val-2': 'nested-text-2',\n        'nested-val-3': 'nested-text-3'\n    }\n}\n\n// arrays\n[\n    ['val-1', 'text-1'],\n    ['val-2', 'text-2'],\n    ['text-3', [\n        ['nested-val-1', 'nested-text-1'],\n        ['nested-val-2', 'nested-text-2'],\n        ['nested-val-3', 'nested-text-3'],\n    ]]\n]\n```\n\n#### field.parse(rawdata)\n\nCoerces the raw data from the request into the correct format for the field,\nreturning the result, e.g. '123' becomes 123 for the number field.\n\n#### field.bind(rawdata)\n\nReturns a new bound field object. Calls parse on the data and stores in the\nbound field's data attribute, stores the raw value in the value attribute.\n\n#### field.errorHTML()\n\nReturns a string containing a HTML element containing the fields error\nmessage, or an empty string if there is no error associated with the field.\n\n#### field.labelText(name)\n\nReturns a string containing the label text from field.label, or defaults to\nusing the field name with underscores replaced with spaces and the first\nletter capitalised.\n\n#### field.labelHTML(name, id)\n\nReturns a string containing a label element with the correct 'for' attribute\ncontaining the text from field.labelText(name).\n\n#### field.classes()\n\nReturns an array of default CSS classes considering the field's attributes,\ne.g. ['field', 'required', 'error'] for a required field with an error message.\n\n#### field.toHTML(name, iterator)\n\nCalls the iterator with the name and field object as arguments. Defaults to\nusing forms.render.div as the iterator, which returns a HTML representation of\nthe field label, error message and widget wrapped in a div.\n\n### Bound Field object\n\n_same as field object, but with a few extensions_\n\n#### Attributes\n\n* ``value`` - The raw value from the request data\n* ``data`` - The request data coerced to the correct format for this field\n* ``error`` - An error message if the field fails validation\n\n#### validate(callback)\n\nChecks if the field is required and whether it is empty. Then runs the\nvalidator functions in order until one fails or they all pass. If a validator\nfails, the resulting message is stored in the field's error attribute.\n\n\n### Widget object\n\n#### Attributes\n\n* ``classes`` - Custom classes to add to the rendered widget\n* ``labelClasses`` - Custom classes to add to the choices label when applicable (multipleRadio and multipleCheckbox)\n* ``type`` - A string representing the widget type, e.g. 'text' or 'checkbox'\n\n#### toHTML(name, field)\n\nReturns a string containing a HTML representation of the widget for the given\nfield.\n\n\n### Validator\n\nA function that accepts a bound form, bound field and a callback as arguments.\nIt should apply a test to the field to assert its validity. Once processing\nhas completed it must call the callback with no arguments if the field is\nvalid or with an error message if the field is invalid.\n\n\n### Renderer\n\nA function which accepts a name and field as arguments and returns a string\ncontaining a HTML representation of the field.\n\n[travis-svg]: https://travis-ci.org/caolan/forms.svg\n[travis-url]: https://travis-ci.org/caolan/forms\n[deps-svg]: https://david-dm.org/caolan/forms.svg\n[deps-url]: https://david-dm.org/caolan/forms\n[dev-deps-svg]: https://david-dm.org/caolan/forms/dev-status.svg\n[dev-deps-url]: https://david-dm.org/caolan/forms#info=devDependencies\n[npm-badge-png]: https://nodei.co/npm/forms.png?downloads=true\u0026stars=true\n[npm-url]: https://npmjs.org/package/forms\n[npm-version-svg]: http://versionbadg.es/caolan/forms.svg\n[license-image]: http://img.shields.io/npm/l/forms.svg\n[license-url]: LICENSE\n[downloads-image]: http://img.shields.io/npm/dm/forms.svg\n[downloads-url]: http://npm-stat.com/charts.html?package=forms\n\n","funding_links":[],"categories":["Form Validation"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaolan%2Fforms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaolan%2Fforms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaolan%2Fforms/lists"}