All Projects → thomashoneyman → Purescript Halogen Formless

thomashoneyman / Purescript Halogen Formless

Licence: mit
Painless forms for Halogen

Programming Languages

purescript
368 projects

Labels

Projects that are alternatives of or similar to Purescript Halogen Formless

Customui
Library to create custom UI's in MCPE 1.2+
Stars: ✭ 60 (-36.17%)
Mutual labels:  form
Mobx React Form Demo
Demo of MobX React Form
Stars: ✭ 78 (-17.02%)
Mutual labels:  form
Formulator
A form library for Phoenix
Stars: ✭ 89 (-5.32%)
Mutual labels:  form
Aura.input
Tools to describe HTML form fields and values.
Stars: ✭ 60 (-36.17%)
Mutual labels:  form
Just Validate
Lightweight (~4,5kb gzip) form validation in Javascript Vanilla, without dependencies, with customizable rules (including remote validation), customizable messages and customizable submit form with ajax helper.
Stars: ✭ 74 (-21.28%)
Mutual labels:  form
Vue Rawmodel
RawModel.js plugin for Vue.js v2. Form validation has never been easier!
Stars: ✭ 79 (-15.96%)
Mutual labels:  form
Awesomevalidation
Android validation library which helps developer boil down the tedious work to three easy steps.
Stars: ✭ 1,093 (+1062.77%)
Mutual labels:  form
Checkout.js
💳 Quickly capture payments with the best checkout flow for crowdfunding and product launches.
Stars: ✭ 92 (-2.13%)
Mutual labels:  form
Normform
Normform: A tiny CSS plugin to make your web forms beautiful again
Stars: ✭ 78 (-17.02%)
Mutual labels:  form
Antd Jsonschema Form
Aother ui implement for react-jsonschema-form
Stars: ✭ 86 (-8.51%)
Mutual labels:  form
Selectivity
Modular and light-weight selection library
Stars: ✭ 1,113 (+1084.04%)
Mutual labels:  form
Formst
Model-driven Form library for React
Stars: ✭ 69 (-26.6%)
Mutual labels:  form
Legit
input validation framework
Stars: ✭ 81 (-13.83%)
Mutual labels:  form
Redaxo yform
YForm für REDAXO 5 – Formulare im Frontend und Backend mit Verwaltung von Datenbank-Tabellen.
Stars: ✭ 60 (-36.17%)
Mutual labels:  form
Talquei
🤖 Vue components to build webforms looking like a conversation
Stars: ✭ 90 (-4.26%)
Mutual labels:  form
Dotnetify Elements
Backend-ready React components for .NET web apps.
Stars: ✭ 58 (-38.3%)
Mutual labels:  form
Formium
The headless form builder for the modern web.
Stars: ✭ 78 (-17.02%)
Mutual labels:  form
Ngx Dynamic Form Builder
FormBuilder + class-transformer + class-validator = dynamic form group builder for Angular10+
Stars: ✭ 93 (-1.06%)
Mutual labels:  form
Multipicker
Form styling plugin for jQuery
Stars: ✭ 90 (-4.26%)
Mutual labels:  form
React Native Merlin
🧙 Simple web-like forms in react native.
Stars: ✭ 83 (-11.7%)
Mutual labels:  form

Formless

CI Latest release Latest package set Maintainer: thomashoneyman

Formless is a flexible, extensible, type-safe Halogen component for building forms without boilerplate.

Installation

Install Formless with Spago:

spago install halogen-formless

Quick Start

You can write a basic Formless form in just a few lines of code. You are responsible for providing just a few pieces of information.

First, a form type that describes the fields in your form, along with their validation error type, user input type, and validated output type. Note: you can provide whatever custom error types you'd like, use Void to represent no possible errors, parse to whatever type you want, and none of your fields need to share any types.

import Prelude
import Data.Newtype (class Newtype, unwrap)

type Dog = { name :: String, age :: Age }

newtype Age = Age Int

derive instance newtypeAge :: Newtype Age _

instance showAge :: Show Age where
  show = show <<< unwrap

data AgeError = TooLow | TooHigh | InvalidInt

newtype DogForm r f = DogForm (r
  --          error    input  output
  ( name :: f Void     String String
  , age  :: f AgeError String Age
  ))

derive instance newtypeDogForm :: Newtype (DogForm r f) _

Next, the component input, which is made up of initial values and validation functions for each field in your form. Note: with your form type complete, the compiler will verify that your inputs are of the right type, that your validation takes the right input type, produces the right error type, and parses to the right output type, that fields exist at these proper keys, and more. There's no loss in type safety here! Plus, your validation functions can easily reference the value of other fields in the form, perform monadic effects, get debounced before running, and more.

You can generate sensible defaults for all input fields in your form by setting initialInputs to Nothing, or you can manually provide the starting value for each field in your form.

import Data.Either (Either(..))
import Data.Int as Int
import Data.Maybe (Maybe(..))
import Formless as F

input :: forall m. Monad m => F.Input' DogForm m
input =
  { initialInputs: Nothing -- same as: Just (F.wrapInputFields { name: "", age: "" })
  , validators: DogForm
      { name: F.noValidation
      , age: F.hoistFnE_ \str -> case Int.fromString str of
          Nothing -> Left InvalidInt
          Just n
            | n < 0 -> Left TooLow
            | n > 30 -> Left TooHigh
            | otherwise -> Right (Age n)
      }
  }

Finally, the component spec, which is made up of a number of optional functions and types you can use to extend the Formless component. At minimum you will need to provide your own render function that describes how your form should be presented to the user. But you can also freely extend the Formless state, query, action, child slots, and message types, as well as provide your own handlers for your extended queries, actions, and child slots, and handle Formless messages internally without leaking information to a parent. You can extend Formless to an incredible degree -- or you can keep things simple and just provide render function. All extensions are optional.

For our small form, we'll do two things: we'll provide a render function, and when the form is submitted, we'll output a Dog to parent components. Along the way we'll wire things up so that input fields display their current value from form state; typing into an input field updates its value in state, also running the correct validation function; we'll display the validation error for age if there is one; and we'll wire up a submit button.

Note: If you would like to have your form raise no messages (rare), do not supply a handleEvent function. If you would like to raise the usual Formless messages (Changed, Submitted), then provide H.raise as your handleEvent function. If you would like to simply raise your form's validated output type (Dog, in this example), then provide F.raiseResult as your handleEvent function. Finally, if you want to do something else, you can write a custom function that does whatever you would like.

import Data.Symbol (SProxy(..))
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP

spec :: forall input m. Monad m => F.Spec' DogForm Dog input m
spec = F.defaultSpec { render = render, handleEvent = F.raiseResult }
  where
  render [email protected]{ form } =
    HH.form_
      [ HH.input
          [ HP.value $ F.getInput _name form
          , HE.onValueInput $ Just <<< F.set _name
          ]
      , HH.input
          [ HP.value $ F.getInput _age form
          , HE.onValueInput $ Just <<< F.setValidate _age
          ]
      , HH.text case F.getError _age form of
          Nothing -> ""
          Just InvalidInt -> "Age must be an integer"
          Just TooLow -> "Age cannot be negative"
          Just TooHigh -> "No dog has lived past 30 before"
      , HH.button
          [ HE.onClick \_ -> Just F.submit ]
          [ HH.text "Submit" ]
      ]
    where
    _name = SProxy :: SProxy "name"
    _age = SProxy :: SProxy "age"

Our form is now complete. It's easy to put this form in a parent page component:

import Effect.Aff.Class (class MonadAff)
import Effect.Class.Console (logShow)
import Halogen as H

data Action = HandleDogForm Dog

page :: forall q i o m. MonadAff m => H.Component HH.HTML q i o m
page = H.mkComponent
  { initialState: const unit
  , render: const render
  , eval: H.mkEval $ H.defaultEval { handleAction = handleAction }
  }
  where
  handleAction (HandleDogForm dog) = logShow (dog :: Dog)

  render = HH.slot F._formless unit (F.component (const input) spec) unit handler
    where
    handler = Just <<< HandleDogForm

Next Steps

Ready to move past this simple example? Check out the examples, which vary in their complexity:

Running the examples locally

If you'd like to explore the example forms locally, you can run them by cloning this repository and then running these commands in the root of the project:

# Install PureScript dependencies
nix-shell

# Bundle the examples and open ./dist/index.html in your browser
spago -x example/example.dhall bundle-app --to ./dist/app.js

Comments & Improvements

Have any comments about the library or any ideas to improve it for your use case? Please file an issue or reach out on the PureScript user group.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].