Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customizing of container element #29

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,25 @@ Example:
)
```

### Form with Custom view
Especially when working with [custom field views](basicfields---custom-views), it may be beneficial, to also use a custom container element.

By default, the render function places all fields in a `div` element. When using `BuildWithCustomView` instead of `Build()` during form creation, a custom view function can be provided. This allows using any other fitting function (like `form`) or using a custom function that may return a `div` element with custom css-classes or even customize the contents.

Example:
```fsharp
let formView attributes children = div attributes (children |> Seq.rev)

let (formState, formConfig) =
Form<Msg>
.Create(OnFormMsg)
.AddField(xy..)
// instead of using `.Build()`, we provide our own view function:
.BuildWithCustomView formView
```

When combined with [custom fields](create-a-custom-field), this allows complex form layouts. But be carefull, with the given parameters - the actual attributes and children are considered an implementation detail that may change without notice (like the nesting of child elements for example).

### Server side validation

In order to support server side validation, the library defines the type `ErrorDef`.
Expand Down
22 changes: 12 additions & 10 deletions src/Form.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type Form<'AppMsg> private (onFormMsg : Msg -> 'AppMsg, fieldBuilders : Types.Fi

member __.AddFields(newFieldBuilders : FieldBuilder list) =
Form (onFormMsg, fieldBuilders @ newFieldBuilders)

member __.Build() =
member __.BuildWithCustomView view =
let duplicatesName =
fieldBuilders
|> List.groupBy (fun builder -> builder.Name)
Expand All @@ -41,6 +41,7 @@ type Form<'AppMsg> private (onFormMsg : Msg -> 'AppMsg, fieldBuilders : Types.Fi
builder.Type, builder.Config
)
|> Map.ofList
View = view
}

let fields : Types.Field list =
Expand All @@ -54,6 +55,8 @@ type Form<'AppMsg> private (onFormMsg : Msg -> 'AppMsg, fieldBuilders : Types.Fi
{ Fields = fields
IsLoading = false }, config

member __.Build() = __.BuildWithCustomView div

[<RequireQualifiedAccess>]
module Form =

Expand Down Expand Up @@ -174,7 +177,7 @@ module Form =
[ ] ]
else
nothing

/// Render the form in your view
/// Props description:
/// - `Config` - `Config` - Configuration of your form. You get it when calling `From.init`
Expand Down Expand Up @@ -202,13 +205,12 @@ module Form =
defaultLoader props.State.IsLoading
| CustomLoader loader ->
loader props.State.IsLoading

div [ Class "thoth-form"
Styles.form ]
[ loader
fields
props.ActionsArea ]


props.Config.View
([ Class "thoth-form"; Styles.form ] |> Seq.map (fun a -> a :> IHTMLProp))
([ loader; fields; props.ActionsArea ] :> ReactElement seq)


/// Validate the model and check if it's valid
/// Returns a tuple of 2 elements
/// - First element is the new state of the form to store in your model
Expand Down
4 changes: 3 additions & 1 deletion src/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Thoth.Elmish.FormBuilder

open Thoth.Json
open Fable.React
open Fable.React.Props
open Elmish

module Types =
Expand Down Expand Up @@ -83,7 +84,8 @@ module Types =
/// Configuration for the Form
type Config<'AppMsg> =
{ ToMsg : Msg -> 'AppMsg
FieldsConfig : Map<FieldType, FieldConfig> }
FieldsConfig : Map<FieldType, FieldConfig>
View: (IHTMLProp seq -> ReactElement seq -> ReactElement) }

type FieldBuilder =
{ Type : FieldType
Expand Down