diff --git a/docs/index.md b/docs/index.md index dcd5f05..d51dcee 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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 + .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`. diff --git a/src/Form.fs b/src/Form.fs index a88a317..4c07326 100644 --- a/src/Form.fs +++ b/src/Form.fs @@ -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) @@ -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 = @@ -54,6 +55,8 @@ type Form<'AppMsg> private (onFormMsg : Msg -> 'AppMsg, fieldBuilders : Types.Fi { Fields = fields IsLoading = false }, config + member __.Build() = __.BuildWithCustomView div + [] module Form = @@ -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` @@ -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 diff --git a/src/Types.fs b/src/Types.fs index 697e5e8..c5c8e52 100644 --- a/src/Types.fs +++ b/src/Types.fs @@ -2,6 +2,7 @@ namespace Thoth.Elmish.FormBuilder open Thoth.Json open Fable.React +open Fable.React.Props open Elmish module Types = @@ -83,7 +84,8 @@ module Types = /// Configuration for the Form type Config<'AppMsg> = { ToMsg : Msg -> 'AppMsg - FieldsConfig : Map } + FieldsConfig : Map + View: (IHTMLProp seq -> ReactElement seq -> ReactElement) } type FieldBuilder = { Type : FieldType