{"id":16286777,"url":"https://github.com/jordanmarr/fable.formvalidation","last_synced_at":"2025-03-20T02:31:54.954Z","repository":{"id":68783936,"uuid":"352387244","full_name":"JordanMarr/Fable.FormValidation","owner":"JordanMarr","description":"A Fable React hook library for validating UI inputs and displaying error messages","archived":false,"fork":false,"pushed_at":"2025-02-13T18:43:20.000Z","size":438,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-28T22:43:44.989Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"F#","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/JordanMarr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-28T17:01:35.000Z","updated_at":"2025-02-14T15:14:06.000Z","dependencies_parsed_at":"2023-12-14T19:41:18.563Z","dependency_job_id":"b384fcc5-75cd-46d8-8526-5081ff01bc27","html_url":"https://github.com/JordanMarr/Fable.FormValidation","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JordanMarr%2FFable.FormValidation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JordanMarr%2FFable.FormValidation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JordanMarr%2FFable.FormValidation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JordanMarr%2FFable.FormValidation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JordanMarr","download_url":"https://codeload.github.com/JordanMarr/Fable.FormValidation/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244047544,"owners_count":20389203,"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-10-10T19:43:44.859Z","updated_at":"2025-03-20T02:31:54.940Z","avatar_url":"https://github.com/JordanMarr.png","language":"F#","readme":"# Fable.FormValidation\nA Fable React hook library for validating UI inputs and displaying error messages\n\n## Installation\nGet it from NuGet!\n\n[![NuGet version (Fable.FormValidation)](https://img.shields.io/nuget/v/Fable.FormValidation.svg?style=flat-square)](https://www.nuget.org/packages/Fable.FormValidation/)\n\n\n## Sample Form\n\n![validation-animated](https://user-images.githubusercontent.com/1030435/112941069-07166a80-90fc-11eb-9d24-abddaa3cd61e.gif)\n\n\n## Call the useValidation() hook\n\n``` fsharp\nopen Fable.FormValidation\n\n[\u003cReactComponent\u003e]\nlet Page() = \n    let model, setModel = React.useState init\n    let rulesFor, validate, resetValidation, errors = useValidation()\n\n    Fable.FormValidation.errorSummary errors\n\n    let save() = \n        if validate() then \n            resetValidation()\n            Toastify.success \"Form is valid!\"\n        \n    let cancel() = \n        resetValidation()\n        setModel init\n\n\n```\n\n## Common Validation Rules\n* `Rule.Required` -\u003e Validates a textbox text value attribute at validate time.\n* `Rule.MinLen n` -\u003e Validates a textbox text value minimum length at validate time.\n* `Rule.MaxLen n` -\u003e Validates a textbox text value maximum length at validate time.\n* `Rule.Regex (pattern, desc)` -\u003e Validates a textbox text value with a regex pattern at validate time.\n\n**Example:**\n\n``` fsharp\ninput [\n    Ref (rulesFor \"First Name\" [Required; MaxLen 50])\n    Class B.``form-control``\n    Value model.FName\n    OnChange (fun e -\u003e setModel { model with FName = e.Value })\n]\n```\n\n``` fsharp\ninput [\n    Ref (rulesFor \"User Email\" [ \n        Required \n        Regex(@\"^\\S+@\\S+$\", \"Email\")\n    ])\n    Class B.``form-control``\n    Value model.Email\n    OnChange (fun e -\u003e setModel { model with Email = e.Value })\n]\n```\n\n## Custom Validation Rules\n* `Rule.CustomRule (fn)` -\u003e Takes any function that returns a `Result\u003cunit,string\u003e`. These rules will directly validate against the current model values and will be calculated during render.\n\n**Example:**\n\nThis example features the Feliz date input with a custom rule:\n``` fsharp \nHtml.input [\n    prop.ref (rulesFor \"Birth Date\" [\n        Required\n        CustomRule (\n            match model.BirthDate with\n            | Some bd -\u003e \n                if bd \u003c= DateTime.Now \n                then Ok() \n                else (Error \"{0} cannot be a future date\")\n            | None -\u003e Ok()\n        )\n    ])\n    prop.className \"date\"\n    prop.type'.date\n    if model.BirthDate.IsSome\n    then prop.value model.BirthDate.Value\n    prop.onChange (fun value -\u003e\n        let success, bd = DateTime.TryParse value\n        if success then setModel { model with BirthDate = Some bd }\n    )\n]\n```\n\n## Validating Radio and Checkbox Groups\nValidation rules can also be applied to non-input elements!\nTo validate a radio button group, you can apply validation to the parent container div:\n\n``` fsharp\nlet fieldName = \"Favorite .NET Language\"\nlet rdoGroup = \"FavLangGrp\"\nrequiredField fieldName (\n    div [\n\tClass $\"{B.``p-2``} {B.``form-control``}\"\n\tStyle [Width 200]\n\tRef (rulesFor fieldName [\n\t    CustomRule (\n\t\tmatch model.FavoriteLang with\n\t\t| None -\u003e Error \"{0} is required\"\n\t\t| Some lang when lang \u003c\u003e FSharp -\u003e Error \"{0} is invalid\"\n\t\t| Some lang -\u003e Ok()\n\t    )\n\t])\n    ] [\n\tlabel [Class B.``mr-4``] [\n\t    input [\n\t\tType \"radio\"\n\t\tChecked (model.FavoriteLang = Some FSharp)\n\t\tClass B.``mr-1``\n\t\tRadioGroup rdoGroup\n\t\tValue \"Yes\"\n\t\tOnChange (fun e -\u003e setModel { model with FavoriteLang = Some FSharp })\n\t    ]\n\t    str \"F#\"\n\t]\n\n\tlabel [Class B.``mr-4``] [\n\t    input [\n\t\tType \"radio\"\n\t\tChecked (model.FavoriteLang = Some CSharp)\n\t\tClass B.``mr-1``\n\t\tRadioGroup rdoGroup\n\t\tValue \"No\"\n\t\tOnChange (fun e -\u003e setModel { model with FavoriteLang = Some CSharp })\n\t    ]\n\t    str \"C#\"\n\t]\n\n\tlabel [Class B.``mr-4``] [\n\t    input [\n\t\tType \"radio\"\n\t\tChecked (model.FavoriteLang = Some VB)\n\t\tClass B.``mr-1``\n\t\tRadioGroup rdoGroup\n\t\tValue \"No\"\n\t\tOnChange (fun e -\u003e setModel { model with FavoriteLang = Some VB })\n\t    ]\n\t    str \"VB\"\n\t]\n    ]\n)\n```\n\n## Creating Custom Rule Libraries\nIt is very easy to extract your custom rules into a reusable library.\n*When creating your custom rules, you can templatize the field name with `{0}`:*\n\n``` fsharp\nmodule CustomRules = \n    let mustBeTrue b = CustomRule (if b then Ok() else Error \"{0} must be true\")\n\n```\n\n## Built-in Rule Functions\nYou can also use the existing rule functions in the `RuleFn` module in your custom rules.\nIn fact, some rules, like `gt`, `gte`, `lt` and `lte` exist only as as functions. \nThis is because the common rules like `Required` and `MinLen` all expect a textbox text value, so we would lose out of F# type safety if we tried coerce those text values into numeric values at validate time. \nFortunately, `CustomRule` allows to use these in a type-safe manner:\n\n``` fsharp\ninput [\n    Ref (rulesFor \"Amount\" [\n        CustomRule (model.Amount |\u003e RuleFn.gte 0)\n    ])\n    Class B.``form-control``\n    Value model.Amount\n    OnChange (fun e -\u003e setModel { model with Amount = e.target?value })\n]\n```\n\n## Add an Error Summary\n\n``` fsharp\nFable.FormValidation.errorSummary errors\n```\n\n## Create an \"error\" style\nWhen a form input is invalid, the \"error\" class will be appended. \nYou must add styling for invalid inputs in your .css file:\n``` css\n.error {\n    border: 1px solid red;\n    background: rgb(255, 232, 235);\n}\n```\n\n## Edge Cases\nYou may encounter a situation where your validated input field is sometimes hidden and then redisplayed (as in the case of a collapsible panel).\nThis can cause an issue where React regenerates a different hashcode for the input each time it is made visible.  \nTo resolve this problem, you can add a override \"vkey\" (validation key) that will be used instead, which will allow Fable.FormValidation to consistently track the input.\n\n``` fsharp\ninput [\n    Ref (rulesFor \"First Name\" [Required; MinLen 2; MaxLen 50])\n    Data (\"vkey\", $\"username-{model.Id}\") // This value must uniquely identify this field\n    Class B.``form-control``\n    Value model.FName\n    OnChange (fun e -\u003e setModel { model with FName = e.Value })\n]\n```\n\n## Overriding the `getValue` and `setStyle` functions\nYou may now, optionally, pass in custom `getValue` and `setStyle` implementations. Use these if you require custom logic to pull your value from a control or want to apply a different error style.\n\n```F#\nlet rulesFor, validate, resetValidation, errors = \n    FormValidation.useValidation(\n        setStyle = setStyleCustom,\n        getValue = getValueCustom\n    )\n```\n\n_Please note that `FormValidation.useValidation` is a new static method with optional parameters. The original `useValidation` function still exists for backwards compatibility._\n\nHere are the default implementations:\n```F#\nlet setStyleDefault (el: Element) (fieldErrors: ValidationErrors) =\n    // Apply or remove error highlighting to fields\n    if fieldErrors.Length \u003e 0 then\n        el.classList.add(\"error\")\n        el.setAttribute(\"title\", fieldErrors.[0])\n    else\n        el.classList.remove(\"error\")\n        el.removeAttribute(\"title\")\n\nlet getValueDefault (el: Element) : string =\n    // NOTE: assumes you have opened \"Fable.Core.JsInterop\"\n    el?value\n```\n\n`setStyleDefault` adds or removes the \"error\" css class and a title/tooltip of the first error.\n`getValueDefault` pulls the element's `value` property for validation.\n\n### Targetting specific controls\nYou can target specific input controls in your custom handlers by checking for a special class name. \nFor example, if you wanted to override the error highlighting to use a different class for a subset of controls, you could add mark them with a custom class name, \"custom-validation\":\n\n```F#\n\tinput [\n\t    Ref (rulesFor \"Project Name\" [ Required ])\n\t    Class \"form-control custom-error\" projName\"\n\t    Value model.Project.Name\n\t    OnChange (fun e -\u003e dispatch (SetProject { model.Project with Name = e.Value }))\n\t]\n```\n\nThen, you can check for that class in your `setStyle` handler:\n\n```F#\nlet setStyle (el: Element) (fieldErrors: ValidationErrors) =\n    let errorClass = \n        if el.className.Contains \"custom-validation\"\n        then \"custom-error\" \n        else \"error\"\n\n    // Apply or remove error highlighting to fields\n    if fieldErrors.Length \u003e 0 then\n        el.classList.add(errorClass)\n        el.setAttribute(\"title\", fieldErrors.[0])\n    else\n        el.classList.remove(errorClass)\n        el.removeAttribute(\"title\")\n```\n\n## Sample App\nClick here to see the [full sample](https://github.com/JordanMarr/Fable.FormValidation/blob/main/src/SampleUI/src/UserForm.fs) app using the Fable 3 template, HookRouter and Toastify.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanmarr%2Ffable.formvalidation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjordanmarr%2Ffable.formvalidation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanmarr%2Ffable.formvalidation/lists"}