hecrj / composable-form Goto Github PK
View Code? Open in Web Editor NEWBuild type-safe composable forms in Elm
Home Page: http://package.elm-lang.org/packages/hecrj/composable-form/latest
License: BSD 3-Clause "New" or "Revised" License
Build type-safe composable forms in Elm
Home Page: http://package.elm-lang.org/packages/hecrj/composable-form/latest
License: BSD 3-Clause "New" or "Revised" License
Most of the examples in the documentation are missing the new error
attribute and need to be updated.
Additionally, we should explain the new error
attribute in Form
and clarify the fields
property in Form.fill
.
If you begin type the sequence of 0, ., 0, the input field will end up as "0", as opposed to "0.0", for the cases where you would be entering values such as "0.005".
A workaround is to type 0, ., 5, ending up with "0.5", and then moving the cursor in front of the 5, and entering the desired number of 0s.
SSCCEs:
I'm creating an in browser http client with elm using the composable form package.
I was wondering how you picture doing something like a list field with an add and remove button for dynamic fields.
Currently I'm just creating that field within normal elm and adding it when submitting the composable form.
The pattern is something like this.
Thanks for your time!
Using Form.View.asHtml to render multiple radio buttons using Form.radioField, I have noticed that the options are displayed as expected, however they are unselectable.
Looking at the HTML generated I can see that the whole field is placed into a label, however multiple radio buttons are put into the label. As a result the browser always selects the first option regardless of where clicked.
<label class="elm-form-field">
<div class="elm-form-label">Status</div>
<fieldset>
<div class="elm-form-label">
<input name="Status" value="open" type="radio">
Open
</div>
<div class="elm-form-label">
<input name="Status" value="closed" type="radio">
Closed
</div>
</fieldset>
</label>
Rolling back to version 4.0.1 fixes this as the following html is generated:
<div class="elm-form-field">
<label>Status</label>
<fieldset>
<label><input name="Status" value="open" type="radio">open</label>
<label><input name="Status" value="closed" type="radio">closed</label>
</fieldset>
</div>
I believe the 4.0.1 HTML to be correct. In addition it was likely the fix in #13 that caused the break (sorry!)
Also, to nitpick, semantically it would make more sense for the elements with class "elm-form-label" should probably be a label instead of a div. I understand you're trying to keep the API simple without an "id" attribute though which I would agree is more valuable.
please tell me how to write code?
[scenario]
There are two form fields, zip field and address field.
User input for zip field, and access to server to get address.(HTTP GET)
And update address field value that server response value.
I can't find the description of what the error parameter does for the different fields.
Hi - composable-form looks great. I see that it hasn't been updated in a long while, and there are outstanding issues and a PR.
Is the project no longer maintained? If its deprecated in favor of something else, it would be great to have that in the Readme. Or if its in a "it works, but its not being maintained now" state, that would be great to know too.
Thanks!
Sometimes complex validations are being performed on the backend. Those errors are sent back to the frontend and stored, in my case, as a Dict String (List String)
where the key is the field, and the list of strings is a list of validation errors for that field.
I've been reading several times the source code & the documentation but I can't find a good way to add the errors to the form.
I have a custom form view based on https://github.com/hecrj/composable-form/blob/master/examples/src/Form/View/Ui.elm and I thought about passing the errors to the layout as a first argument:
layout : Dict String (List String) -> ViewConfig values msg -> Form values msg -> Model values -> Element msg
send those errors to every field, for example for the number field :
, numberField = numberField serverErrors
and change numberField
to:
numberField : Dict String -> (List String) -> NumberFieldConfig msg -> Element msg
And in that function maybe merge the serverErrors into error before showing it.
Isn't this too complex? Am I missing something? Could be useful to create a addErrorToField
function somewhere?
I need some custom attributes. data-test-id
or id
or name
for e2e tests, and autocomplete
to let browsers properly fill out login/signup forms.
I've been using Form.View.custom
so far, with CustomConfig
and it works out for styling (bootstrap in my case). However, TextFieldConfig
etc have only label
and placeholder
hardcoded into their attributes.
I am kind of lost here. Did I completely miss something?
Is this simply not possible without a PR?
Or should I start by copypasting the whole of Form.View.custom
and everything "under it"?
There are some times when a set of fields are related. It'd be cool if a label could be added to Form.group
similar to hos Form.list
works.
Hi,
I really like your approach of using a JSON decoder to validate the forms on the view events, but adding form rendering into the mixture does not fit my needs. Designs are to complex. Is it possible to have validation that I plug into my own forms?
Thanks!
This is a great library, thank you for making forms simple!
One thing that has come up is that the asHtml function doesn't add an id to the input nor a for attribute on the label. Could this be added in? Form.Base.TextField.Attributes seems to be the place to specify it.
Like radios and select for OR, there should be the AND option for an unspecified list of a thing that is translated to a select[multiple]
-- and probably a list of input[type=checkbox]
since most users find select[multiple]
to be unintuitive to hold ctrl
/option
(although it's debatably a better UX on mobile to get a native pop-up with checkbboxes).
I'm still trying to learn how this library works, so forgive me if this question doesn't make sense.
I was wondering why the message alias is the same for change messages and messages when the form is valid. I was trying to follow along with this article which creates the form with UserDetails
as the output. (However, in the elm docs and examples, I see it's done differently.)
I wanted to be able to put my forms in a separate module and include them where they're needed, but because the msg
alias is the same, I can't figure out how to make it work. I need to specify a message as a function that takes a type and returns a msg
, but on change, the type will be the values, and on submit it will be the output.
Here's the function signature to be clear what I'm referring to:
asHtml : ViewConfig values msg -> Form values msg -> Model values -> Html msg
type alias ViewConfig values msg =
{ onChange : Model values -> msg
, action : String
, loading : String
, validation : Validation
}
Why not use the name singleton
like many other libraries for succeed : a -> Form b a
? ...Like List.singleton
, Dict.singleton
, Set.singleton
, RemoteData.singleton
(this one is actually succeed
), etc..
I haven't been able to come up with any solution to disable a single field, based on some truth value.
I have a solution for disabled an entire form, but that can't be used as it needs to be a single field.
defaultConfig : Bool -> CustomConfig msg (Element msg)
defaultConfig readOnly =
{ form = form readOnly
, textField = setDisabled readOnly >> textField
, emailField = setDisabled readOnly >> emailField
, passwordField = setDisabled readOnly >> passwordField
, searchField = setDisabled readOnly >> textField
, textareaField = setDisabled readOnly >> textareaField
, numberField = setDisabled readOnly >> numberField
, rangeField = setDisabled readOnly >> rangeField
, checkboxField = setDisabled readOnly >> checkboxField
, radioField = setDisabled readOnly >> radioField
, selectField = setDisabled readOnly >> selectField
, group = group
, section = section
}
setDisabled : Bool -> { a | disabled : Bool } -> { a | disabled : Bool }
setDisabled readOnly config =
if readOnly then
{ config | disabled = True }
else
config
I can't see how I can get access to the config when in building the form though.
I could possibly do something really hacky like feed in a dictionary of field labels and a truth value to my CustomConfig
, and then if the labels match (assuming unique), set disabled to true. Or, I could set the error to some specific value, which also is hacky. Or, I could hijack the placeholder field to do a similar thing.
Could this possibly be made a first class thing? It seems like a common behaviour imo
The jump in complexity from using the Form
module to creating custom fields using Form.Base
can be really disconcerting.
As I explained in a related Discourse thread, we should explore different approaches to make this transition smoother and more pleasant.
A Form.Advanced
module with a customField
type variable could be a good start.
I like the API and general idea of this a lot but I'm struggling to figure out how to use it with varying layouts and styles of form elements. For instance, in Form.View.basic
, each field is rendered with the same styling and there is no control of layout, they're vertically stacked. Even if I wanted to add some content between fields, it's not possible.
Do you have any ideas on how to change the rendering functions to accommodate this?
I'm wondering what the best way would be to add a form using append, iff based on some boolean value?
For my use case, I have a select fields, and based on the value, I'd like to show 2 other fields. I'd do something like:
|> \form ->
if truth then
Form.append someField form
else
form
The issue with this is that the output
is now different. I'd have to do the same truth test before my call to succeed
and feed in the appropriate constructor—or I suppose I could keep the same constructor, but wrap that value in a Maybe
.
I looked at the dynamic form example and it uses andThen
to do something based on the form, but because of how the form is structured, it doesn't quite work.
Just on a related note, I've got a helper function I use elsewhere for optionally making a field optional:
{-| This will make a field optional dependent upon some test function of the current
form values. The field's parser is only called when the field is considered to be
not blank (for example, an input field isn't empty).
-}
optionalWhen : Form.Form values output -> output -> (values -> Bool) -> Form.Form values output
optionalWhen field default test =
Form.meta
(\values ->
if test values then
field
else
Form.map (Maybe.withDefault default) (Form.optional field)
)
Hi,
Form.View.Model
does not seem to update state
+ errorTracking
upon an invalid form validation.
I've been trying out this library and enjoy using it a lot. However, I'm unsure of the function of the errorTracking
that comes with Form.View.Model
? This is to propagate what fields in a form are invalid?
I tried out some of the provided examples, filling them out in an invalid way such that multiple field errors were displayed. However, the state of errorTracking
becomes
{ errorTracking =
ErrorTracking { showAllErrors = True, showFieldError = Set.fromList [] }
}
I.e. showAllErrors
is switched from False
=> True
but showFieldError
remains empty, which seems like unexpected behaviour?
Furthermore the state
of the Form.View.Model
remains Idle
rather than Error
. I would have expected the state
to be Error
?
Am I misunderstanding the intended behaviour of these fields or is there a real bug?
I am having trouble with the textareaField
and Form.View.idle
in the following code. When the form is submitted, the other fields are set to blank, but the textarea doesn't change. See Ellie snippet here: https://ellie-app.com/5sbKCffdg9Ba1
module Main exposing (..)
import Browser exposing (Document)
import Form exposing (Form)
import Form.View
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
type alias Flags = ()
type Msg
= FormChanged (Form.View.Model Values)
| Submit Values
type alias Values =
{ title : String
, body : String
, pword : String
, email : String }
form : Form Values Msg
form =
let
titleField =
Form.textField
{ parser = Ok
, value = .title
, update = \value values -> { values | title = value }
, attributes = { label = "Title", placeholder = "Some title here" }
}
bodyField =
Form.textareaField
{ parser = Ok
, value = .body
, update = \value values -> { values | body = value }
, attributes = { label = "Body", placeholder = "Your body" }
}
passwordField =
Form.passwordField
{ parser = Ok
, value = .pword
, update = \value values -> { values | pword = value }
, attributes = { label = "Password", placeholder = "A password" }
}
emailField =
Form.emailField
{ parser = Ok
, value = .email
, update = \value values -> { values | email = value }
, attributes = { label = "Email", placeholder = "Some email here" }
}
in Form.succeed Values
|> Form.append titleField
|> Form.append bodyField
|> Form.append passwordField
|> Form.append emailField
|> Form.map Submit
type alias Model = { form : Form.View.Model Values }
subscriptions : Model -> Sub Msg
subscriptions _ = Sub.none
init : Flags -> ( Model, Cmd Msg )
init _ =
( { form = { title = "", body = "", pword = "", email = "" } |> Form.View.idle }
, Cmd.none )
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FormChanged modelValues ->
( { model | form = modelValues }, Cmd.none )
Submit output ->
let initial = { title = "", body = "", pword = "", email = "" }
in ( { model | form = initial |> Form.View.idle }
, Cmd.none )
view : Model -> Document Msg
view model =
let formView =
Form.View.asHtml
{ onChange = FormChanged
, action = "Submit"
, loading = "Submit"
, validation = Form.View.ValidateOnSubmit
} form model.form
in
{ title = "", body = [ formView ] }
main : Program () Model Msg
main =
Browser.document
{ init = init
, update = update
, subscriptions = subscriptions
, view = view }
For a long time I thought this was impossible to do without writing toString/fromString
functions for your custom types, but I just realized that you can make it work by instead using the list index of the options as the "value", and then in the onChange
decoder use that index to retrieve the item from the passed in options list.
Here's a demo: https://ellie-app.com/7253Q8THR2xa1
Many well-known packages use the elm-
prefix (style-elements
is elm-ui
now, etc.).
Should we rename this package to elm-form
? I think it could help new users to find it.
Hey,
i have a Form with the need to display a Success Message once the submission has succeeded. However i'am stuck because Form.View.State
doesn't handle the Success case.
I was thinking about this kind of update on the State type :
type State
= Idle
| Loading
| Error String
| Success String
Is it a good idea ? or is there an other way to handle that ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.