{"id":13850181,"url":"https://github.com/etaque/elm-form","last_synced_at":"2025-07-12T21:33:21.636Z","repository":{"id":52694032,"uuid":"49791731","full_name":"etaque/elm-form","owner":"etaque","description":"Dynamic forms handling in Elm","archived":true,"fork":false,"pushed_at":"2021-04-20T20:30:43.000Z","size":326,"stargazers_count":192,"open_issues_count":15,"forks_count":34,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-08-05T20:31:55.017Z","etag":null,"topics":["elm","form","form-builder","form-validation"],"latest_commit_sha":null,"homepage":"http://package.elm-lang.org/packages/etaque/elm-form/latest","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/etaque.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}},"created_at":"2016-01-16T21:29:09.000Z","updated_at":"2024-05-02T08:48:11.000Z","dependencies_parsed_at":"2022-08-21T17:40:42.511Z","dependency_job_id":null,"html_url":"https://github.com/etaque/elm-form","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etaque%2Felm-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etaque%2Felm-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etaque%2Felm-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/etaque%2Felm-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/etaque","download_url":"https://codeload.github.com/etaque/elm-form/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225839485,"owners_count":17532305,"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":["elm","form","form-builder","form-validation"],"created_at":"2024-08-04T20:01:00.811Z","updated_at":"2024-11-22T03:30:51.802Z","avatar_url":"https://github.com/etaque.png","language":"Elm","funding_links":[],"categories":["Elm"],"sub_categories":[],"readme":"# Elm Form\n\nHTML live form builders and validation for Elm. [![Build Status](https://travis-ci.org/etaque/elm-form.svg?branch=master)](https://travis-ci.org/etaque/elm-form)\n\n    elm package install etaque/elm-form\n\nFor when the classical \"a message per field\" doesn't work well for you, at the price of losing some type safety (field names are made of strings, see [#97](https://github.com/etaque/elm-form/issues/97)).\n\n## Support\n\nWe have a dedicated channel in [Elm slack](https://elmlang.herokuapp.com/), join us in `#elm-form` for any question, support or issues coordination.\n\n## Features\n\n* Validation API similar to `Json.Decode` with the standard `map`, `andThen`, etc: you either get the desired output value or all field errors\n* HTML inputs helpers with pre-wired handlers for live validation\n* Suite of basic validations, with a way to add your own\n* Unlimited fields, see `andMap` function (as in `Json.Extra`)\n* Nested fields (`foo.bar.baz`) and lists (`todos.1.checked`) enabling rich form build\n\nSee [complete example here](http://etaque.github.io/elm-form/example/) ([source code](https://github.com/etaque/elm-form/tree/master/example)).\n\n\n## Basic usage\n\nSee the [example validation test suite](https://github.com/etaque/elm-form/blob/master/tests/ValidationTests.elm)\nand [test helper function docs](http://package.elm-lang.org/packages/etaque/elm-form/latest/Form-Test)\nfor how to test-drive validations.\n\n```elm\nmodule Main exposing (Foo, Model, Msg(..), app, formView, init, update, validate, view)\n\nimport Browser\nimport Form exposing (Form)\nimport Form.Input as Input\nimport Form.Validate as Validate exposing (..)\nimport Html exposing (..)\nimport Html.Attributes exposing (..)\nimport Html.Events exposing (..)\n\n\n\n-- your expected form output\n\n\ntype alias Foo =\n    { bar : String\n    , baz : Bool\n    }\n\n\n\n-- Add form to your model and msgs\n\n\ntype alias Model =\n    { form : Form () Foo }\n\n\ntype Msg\n    = NoOp\n    | FormMsg Form.Msg\n\n\n\n-- Setup form validation\n\n\ninit : Model\ninit =\n    { form = Form.initial [] validate }\n\n\nvalidate : Validation () Foo\nvalidate =\n    succeed Foo\n        |\u003e andMap (field \"bar\" email)\n        |\u003e andMap (field \"baz\" bool)\n\n\n\n-- Forward form msgs to Form.update\n\n\nupdate : Msg -\u003e Model -\u003e Model\nupdate msg ({ form } as model) =\n    case msg of\n        NoOp -\u003e\n            model\n\n        FormMsg formMsg -\u003e\n            { model | form = Form.update validate formMsg form }\n\n\n\n-- Render form with Input helpers\n\n\nview : Model -\u003e Html Msg\nview { form } =\n    Html.map FormMsg (formView form)\n\n\nformView : Form () Foo -\u003e Html Form.Msg\nformView form =\n    let\n        -- error presenter\n        errorFor field =\n            case field.liveError of\n                Just error -\u003e\n                    -- replace toString with your own translations\n                    div [ class \"error\" ] [ text (Debug.toString error) ]\n\n                Nothing -\u003e\n                    text \"\"\n\n        -- fields states\n        bar =\n            Form.getFieldAsString \"bar\" form\n\n        baz =\n            Form.getFieldAsBool \"baz\" form\n    in\n    div []\n        [ label [] [ text \"Bar\" ]\n        , Input.textInput bar []\n        , errorFor bar\n        , label []\n            [ Input.checkboxInput baz []\n            , text \"Baz\"\n            ]\n        , errorFor baz\n        , button\n            [ onClick Form.Submit ]\n            [ text \"Submit\" ]\n        ]\n\n\napp =\n    Browser.sandbox\n        { init = init\n        , update = update\n        , view = view\n        }\n\n```\n\n\n## Advanced usage\n\n### Custom inputs\n\n * For rendering, `Form.getFieldAsString`/`Bool` provides a `FieldState` record with all required fields (see package doc).\n\n * For event handling, see all field related messages in `Form.Msg` type.\n\nOverall, having a look at current [helpers source code](https://github.com/etaque/elm-form/blob/master/src/Form/Input.elm) should give you a good idea of the thing.\n\n### Incremental validation\n\nSimilar to what Json.Extra provides you can also use `Form.andMap`\n\n```elm\nForm.succeed Player\n    |\u003e andMap (field \"email\" (string |\u003e andThen email))\n    |\u003e andMap (field \"power\" int)\n```\n\n### Nested records\n\n* Validation:\n\n```elm\nvalidation =\n    map2 Player\n        (field \"email\" (string |\u003e andThen email))\n        (field \"power\" (int |\u003e andThen (minInt 0)))\n        (field \"options\"\n            (map2 Options\n                (field \"foo\" string)\n                (field \"bar\" string)\n            )\n        )\n```\n\n* View:\n\n```elm\nInput.textInput (Form.getFieldAsString \"options.foo\" form) []\n```\n\n### Dynamic lists\n\n```elm\n-- model\ntype alias TodoList =\n    { title : String\n    , items : List String\n    }\n\n-- validation\nvalidation : Validation () Issue\nvalidation =\n    map2 TodoList\n        (field \"title\" string)\n        (field \"items\" (list string))\n\n-- view\nformView : Form () Issue -\u003e Html Form.Msg\nformView form =\n    div\n        [ class \"todo-list\" ]\n        [ Input.textInput\n            (Form.getFieldAsString \"title\" form)\n            [ placeholder \"Title\" ]\n        , div [ class \"items\" ] \u003c|\n            List.map\n                (itemView form)\n                (Form.getListIndexes \"items\" form)\n        , button\n            [ class \"add\"\n            , onClick (Form.Append \"items\")\n            ]\n            [ text \"Add\" ]\n        ]\n\nitemView : Form () Issue -\u003e Int -\u003e Html Form.Msg\nitemView form i =\n    div\n        [ class \"item\" ]\n        [ Input.textInput\n            (Form.getFieldAsString (\"items.\" ++ (String.fromInt i)) form)\n            []\n        , a\n            [ class \"remove\"\n            , onClick (Form.RemoveItem \"items\" i)\n            ]\n            [ text \"Remove\" ]\n        ]\n```\n\n\n### Initial values and reset\n\n* At form initialization:\n\n```elm\nimport Form.Field as Field\n\n\ninitialFields : List ( String, Field )\ninitialFields =\n    [ ( \"power\", Field.string \"10\" )\n    , ( \"options\"\n      , Field.group\n            [ ( \"foo\", Field.string \"blah\" )\n            , ( \"bar\", Field.string \"meh\" )\n            ]\n      )\n    ]\n\n\ninitialForm : Form\ninitialForm =\n    Form.initial initialFields validation\n```\n\nSee `Form.Field` type for more options.\n\n* On demand:\n\n```elm\nbutton [ onClick (Form.Reset initialFields) ] [ text \"Reset\" ]\n```\n\n*Note:* To have programmatic control over any `input[type=text]`/`textarea` value, like reseting or changing the value, you must set the `value` attribute with `Maybe.withDefault \"\" state.value`, as seen [here](https://github.com/etaque/elm-form/pull/57/files#diff-bfb877e82b2c89b329fcda943a258611R50). There's a downside of doing this: if the user types too fast, the caret can go crazy.\n\nMore info: https://github.com/evancz/elm-html/pull/81#issuecomment-145676200\n\n\n### Custom errors\n\n```elm\ntype LocalError = Fatal | NotSoBad\n\nvalidation : Validation LocalError Foo\nvalidation =\n    (field \"foo\" (string |\u003e customError Fatal))\n\n-- creates `Form.Error.CustomError Fatal`\n```\n\n\n### Async validation\n\nThis package doesn't provide anything special for async validation, but doesn't prevent you to do that either. As field values are accessible from `update` with `Form.getStringAt/getBoolAt`, you can process them as you need, trigger effects like an HTTP request, and then add any errors to the view by yourself.\n\nAnother way would be to enable dynamic validation reload, to make it dependant of an effect, as it's part of the form state. Please ping me if this feature would be useful to you.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetaque%2Felm-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fetaque%2Felm-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fetaque%2Felm-form/lists"}