Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/westonganger/form_builder.cr
Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap
https://github.com/westonganger/form_builder.cr
bootstrap crystal crystal-lang form-builder html html-form html-form-builder
Last synced: about 6 hours ago
JSON representation
Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap
- Host: GitHub
- URL: https://github.com/westonganger/form_builder.cr
- Owner: westonganger
- License: mit
- Created: 2019-01-29T05:01:33.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2023-11-18T20:32:31.000Z (12 months ago)
- Last Synced: 2024-11-01T07:24:31.295Z (7 days ago)
- Topics: bootstrap, crystal, crystal-lang, form-builder, html, html-form, html-form-builder
- Language: Crystal
- Homepage:
- Size: 169 KB
- Stars: 31
- Watchers: 4
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-crystal - form_builder.cr - Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap (HTML Builders)
- awesome-crystal - form_builder.cr - Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap (HTML Builders)
README
# Form Builder.cr
Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap. Works well with your favourite Crystal web framework such as Kemal, Amber, or Lucky.
# Features
- Easily generate HTML markup for forms, labels, inputs, help text and errors
- Integrates with many UI libraries such as Bootstrap
- Custom theme support# Supported UI Libraries
Out of the box Form Builder can generate HTML markup for the following UI libraries:
- Bootstrap 4
* `theme: :bootstrap_4_vertical`
* `theme: :bootstrap_4_inline`
* `theme: :bootstrap_4_horizontal` or `theme: FormBuilder::Themes::Bootstrap4Horizontal.new(column_classes: ["col-sm-3","col-sm-9"])`
- Bootstrap 3
* `theme: :bootstrap_3_vertical`
* `theme: :bootstrap_3_inline`
* `theme: :bootstrap_3_horizontal` or `theme: FormBuilder::Themes::Bootstrap3Horizontal.new(column_classes: ["col-sm-3","col-sm-9"])`
- Bootstrap 2
* `theme: :bootstrap_2_vertical`
* `theme: :bootstrap_2_inline`
* `theme: :bootstrap_2_horizontal`
- Bulma
* `theme: :bulma_vertical`
* `theme: :bulma_horizontal`
- Foundation
* `theme: :foundation`
- Materialize
* `theme: :materialize`
- Milligram
* `theme: :milligram`
- Semantic UI
* `theme: :semantic_ui_vertical`
* `theme: :semantic_ui_inline`
- None (Default)
* `theme: :default`
* `theme: nil`
* or simply do not provide a `:theme` argumentIf you dont see your favourite UI library here feel free to create a PR to add it. I recommend creating an issue to discuss it first.
# Installation
Add this to your application's shard.yml:
```yaml
dependencies:
form_builder:
github: westonganger/form_builder.cr
``````crystal
require "form_builder"
```# Usage
The following field types are supported:
- `:checkbox`
- `:file`
- `:hidden`
- `:password`
- `:radio`
- `:select`
- `:text`
- `:textarea`## FormBuilder in View Templates (Example in Slang)
```crystal
== FormBuilder.form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
.row.main-examples
.col-sm-6
### -- Field Options
### type : (String | Symbol)
### name : (String | Symbol)?
### label : (String | Bool)? = true
### help_text : String?### value : (String | Symbol)?
### -- Note: The `input_html["value"]` option will take precedence over the :value option (except for `type: :textarea/:select`)### errors : (Array(String) | String)?
### -- Note: Array(String) generates a list of help text elements. If you have an Array of errors and you only want a single help text element, then join your errors array to a String### -- For the following Hash options, String keys will take precedence over any Symbol keys
### input_html : (Hash | NamedTuple)? ### contains attributes to be added to the input/field
### label_html : (Hash | NamedTuple)? ### contains attributes to be added to the label
### wrapper_html : (Hash | NamedTuple)? ### contains attributes to be added to the outer wrapper for the label and input
### help_text_html : (Hash | NamedTuple)? ### contains attributes to be added to the help text container
### error_html : (Hash | NamedTuple)? ### contains attributes to be added to the error container(s)
== f.field name: "product[name]", label: "Name", type: :text, errors: product_errors["name"]== f.field name: "product[description]", label: "Description", type: :textarea, input_html: {class: "foobar"}, wrapper_html: {style: "margin-top: 10px"}, label_html: {style: "color: red;"}
== f.field name: "product[file]", type: :file, help_text: "Must be a PDF", help_text_html: {style: "color: blue;"}
.col-sm-6
== f.field name: "product[available]", type: :checkbox, label: "In Stock?"== f.field name: "product[class]", type: :radio, label: false
== f.field name: "product[secret]", type: :hidden, value: "foobar"
.row.select-example
### -- Additional Options for `type: :select`
### collection : (Hash | NamedTuple) = {
### options : (Array(String) | Array(String | Array(String)) | String) ### Required, Note: The non-Array String type is for passing in a pre-built html options string
### selected : (String | Array(String))?
### disabled : (String | Array(String))?
### include_blank : (String | Bool)?
### }
### -- Note: String keys will take precedence over any Symbol keys### -- When passing a nested array to collection[:options] the Option pairs are defined as: [required_value, optional_label]
- opts = [["A", "Type A"], ["B" "Type B"], ["C", "Type C"], "Other"]== f.field name: "product[type]", label: "Type", type: :select, collection: {options: opts, selected: ["B"], disabled: ["C"]}
```## FormBuilder in Plain Crystal Code
When using the `FormBuilder.form` method in plain Crystal code, the `<<` syntax is required to add the generated field HTML to the form HTML string
```crystal
form_html_str = FormBuilder.form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
f << f.field(name: "name", type: :text, label: "Name")
f << f.field(name: "sku", type: :text, label: "SKU")
f << "Hello World"
end
```OR you can use the lower level `String.build` instead:
```crystal
form_html_str = String.build do |str|
str << FormBuilder.form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
str << f.field(name: "name", type: :text, label: "Name")
str << f.field(name: "sku", type: :text, label: "SKU")
str << "Hello World"
end
end
```## FormBuilder without a Form
```crystal
- f = FormBuilder::Builder.new(theme: :bootstrap_4_vertical)== f.field name: "name", type: :text, label: "Name"
== f.field name: "sku", type: :text, label: "SKU"
```## Error Handling
The form builder is capable of handling error messages too. If the `:errors` argument is provided it will generate the appropriate error help text element(s) next to the field.
```crystal
== FormBuilder.form(theme: :bootstrap_4_vertical) do |f|
== f.field name: "name", type: :text, label: "Name", errors: "cannot be blank"
== f.field name: "sku", type: :text, label: "SKU", errors: ["must be unique", "incorrect SKU format")
```## Custom Themes
FormBuilder allows you to create custom themes very easily.
Example Usage:
```ruby
FormBuilder.form(theme: :custom)
```Example Theme Class:
```crystal
# config/initializers/form_builder.crmodule FormBuilder
module Themes
class Custom < BaseTheme### (Optional) If your theme name doesnt perfectly match the `.underscore` of the theme class name
def self.theme_name
"custom"
end### (Optional) If your theme requires additional variables similar to `Bootstrap3Horizontal.new(columns: ["col-sm-3", "col-sm-9"])`
def initialize
### For an example see `src/form_builders/themes/bootstrap_3_horizontal.cr`
enddef wrap_field(field_type : String, html_field : String, html_label : String?, html_help_text : String?, html_errors : Array(String)?, wrapper_html_attributes : StringHash)
String.build do |s|
wrapper_html_attributes["class"] = "form-group #{wrapper_html_attributes["class"]?}".strip### `FormBuilder.build_html_attr_string` is the one and only helper method for Themes
### It converts any Hash to an HTML Attributes String
### Example: {"class" => "foo", "data-role" => "ninja"} converts to "class=\"foo\" data-role=\"ninja\""
attr_str = FormBuilder.build_html_attr_string(wrapper_html_attributes)s << "#{attr_str.empty? ? "
" : %()}""if {"checkbox", "radio"}.includes?(field_type) && html_label && (i = html_label.index(">"))
s << html_label.insert(i+1, "#{html_field} ")
else
s << html_label
s << html_field
end
s << html_help_textif html_errors
s << html_errors.join
ends << "
end
enddef input_html_attributes(html_attrs : Hash(String, String), field_type : String, has_errors? : Bool)
html_attrs["class"] = "form-field other-class #{html_attrs["class"]?}".strip
html_attrs["style"] = "color: blue; #{html_attrs["style"]?}".strip
unless html_attrs.has_key?("data-foo")
html_attrs["data-foo"] = "bar #{html_attrs["class"]?}"
end
html_attrs
enddef label_html_attributes(html_attrs : Hash(String, String), field_type : String, has_errors? : Bool)
html_attrs["class"] = "form-label other-class #{html_attrs["class"]?}".strip
html_attrs["style"] = "color: red; #{html_attrs["style"]?}".strip
html_attrs
enddef form_html_attributes(html_attrs : Hash(String, String))
html_attrs["class"] = "form-inline #{html_attrs["class"]}"
html_attrs
enddef build_html_help_text(help_text : String, html_attrs : StringHash)
html_attrs["class"] = "help-text #{html_attrs["class"]?}".stripString.build do |s|
s << (html_attrs.empty? ? "" : %()"
s << help_text
s << "
end
enddef build_html_error(error : String, html_attrs : StringHash)
html_attrs["class"] = "help-text error #{html_attrs["class"]?}".strip
html_attrs["style"] = "color: red; #{html_attrs["style"]?}".stripString.build do |s|
s << (html_attrs.empty? ? "" : %()"
s << error
s << "
end
endend
end
end
```Now you can use the theme just like any other built-in theme.
```crystal
FormBuilder.form(theme: :custom)
```# Contributing
To run the test suite:
```
crystal spec
```# Ruby Alternative
This library has been ported to the Ruby language as [SexyForm.rb](https://github.com/westonganger/sexy_form.rb)
The pattern/implementation of this form builder library turned out so beautifully that I felt the desire to have the same syntax available in the Ruby language. Many Crystal developers also write Ruby and vice versa so this only made sense. What was awesome is that, the Crystal and Ruby syntax is so similar that converting Crystal code to Ruby was straight forward and quite simple.
# Credits
Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
Project Inspired By:
- [SimpleForm](https://github.com/plataformatec/simple_form)
- [Jasper Helpers](https://github.com/amberframework/jasper-helpers) used within the [Amber framework](https://github.com/amberframework/amber)