• Stars
    star
    31
  • Rank 792,883 (Top 17 %)
  • Language
    Crystal
  • License
    MIT License
  • Created over 5 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Dead simple HTML form builder for Crystal with built-in support for many popular UI libraries such as Bootstrap

Form Builder.cr

Version CI Status Buy Me a Coffee

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 argument

If 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:

dependencies:
  form_builder:
    github: westonganger/form_builder.cr
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)

== 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

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 << "<strong>Hello World</strong>"
end

OR you can use the lower level String.build instead:

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 << "<strong>Hello World</strong>"
  end
end

FormBuilder without a Form

- 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.

== 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:

FormBuilder.form(theme: :custom)

Example Theme Class:

# config/initializers/form_builder.cr

module 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`
      end

      def 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? ? "<div>" : %(<div #{attr_str}>)}"

          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_text

          if html_errors
            s << html_errors.join
          end

          s << "</div>"
        end
      end

      def 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
      end

      def 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
      end

      def form_html_attributes(html_attrs : Hash(String, String))
        html_attrs["class"] = "form-inline #{html_attrs["class"]}"
        html_attrs
      end

      def build_html_help_text(help_text : String, html_attrs : StringHash)
        html_attrs["class"] = "help-text #{html_attrs["class"]?}".strip

        String.build do |s|
          s << (html_attrs.empty? ? "<div>" : %(<div #{build_html_attr_string(html_attrs)}>)
          s << help_text
          s << "</div>"
        end
      end

      def 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"]?}".strip

        String.build do |s|
          s << (html_attrs.empty? ? "<div>" : %(<div #{build_html_attr_string(html_attrs)}>)
          s << error
          s << "</div>"
        end
      end

    end
  end
end

Now you can use the theme just like any other built-in theme.

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

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 - @westonganger

Project Inspired By:

More Repositories

1

spreadsheet_architect

Spreadsheet Architect is a library that allows you to create XLSX, ODS, or CSV spreadsheets super easily from ActiveRecord relations, plain Ruby objects, or tabular data.
Ruby
1,298
star
2

rails_i18n_manager

Web interface to manage i18n translations helping to facilitate the editors of your translations. Provides a low-tech and complete workflow for importing, translating, and exporting your I18n translation files. Designed to allow you to keep the translation files inside your projects git repository where they should be.
Ruby
205
star
3

paper_trail-association_tracking

Plugin for the PaperTrail gem to track and reify associations
Ruby
118
star
4

rearmed-js

A collection of helpful methods and monkey patches for Arrays, Objects, Numbers, and Strings in Javascript
JavaScript
104
star
5

active_snapshot

Simplified snapshots and restoration for ActiveRecord models and associations with a transparent white-box implementation
Ruby
96
star
6

rodf

ODF generation library for Ruby
Ruby
53
star
7

protected_attributes_continued

The community continued version of protected_attributes for Rails 5+
Ruby
45
star
8

rearmed-rb

A collection of helpful methods and monkey patches for Arrays, Hash, Enumerables, Strings, Objects & Dates in Ruby
Ruby
41
star
9

sexy_form.rb

Dead simple HTML form field builder for Ruby with built-in support for many popular UI libraries such as Bootstrap
Ruby
38
star
10

rearmed_rails

A collection of helpful methods and monkey patches for Rails
Ruby
33
star
11

bootstrap-directional-buttons

Directional / Arrow buttons for Bootstrap
HTML
22
star
12

capistrano-precompile-chooser

Capistrano plugin to precompile your Rails assets locally, remotely, or not at all provided with a very convenient default terminal prompt.
Ruby
13
star
13

active_sort_order

The "easy-peasy" dynamic sorting pattern for ActiveRecord that your Rails apps deserve
Ruby
13
star
14

input-autogrow

jQuery plugin for autogrowing inputs
JavaScript
8
star
15

chosen-bootstrap-theme

A Bootstrap theme for Chosen Select that actually looks like Bootstrap
CSS
8
star
16

js-try

JS-Try is a Javascript implementation of the try method from Rails for safe navigation
JavaScript
6
star
17

chosen-material-theme

A Material theme for Chosen Select
CSS
6
star
18

search_architect

Dead simple, powerful and fully customizable searching for your Rails or ActiveRecord models and associations.
Ruby
6
star
19

pairer

Pairer is Rails app/engine to Easily rotate and keep track of working pairs
Ruby
5
star
20

rails_uuid_to_integer_primary_keys

A Rails Migration to convert your UUID primary keys back to integer / bigint primary keys
Ruby
5
star
21

chosen-remote-source

Provides remote data source support for chosen-js selects
JavaScript
4
star
22

input-case-enforcer

Enforce uppercase, lowercase, or Capitalized inputs & textareas
JavaScript
4
star
23

paperclip_utils

Collection of Paperclip processors and a Helper class for easier dynamic processors and styles on your Paperclip uploads
Ruby
4
star
24

active_record_simple_execute

Sanitize and Execute your raw SQL queries in ActiveRecord and Rails with a much more intuitive and shortened syntax
Ruby
4
star
25

active_record_case_insensitive_finders

Adds case-insensitive finder methods to Rails and ActiveRecord
Ruby
3
star
26

chosen-readonly

Readonly support for Chosen selects
JavaScript
3
star
27

better_exception_notifier

An exception notifier for Rails and Rack apps
Ruby
3
star
28

rails_nestable_layouts

Rails Nestable Layouts - Dead simple nested layouts for Rails
Ruby
3
star
29

minitest_change_assertions

Provides assertions for your Minitest suite to determine if an object has been changed
Ruby
2
star
30

common_website_scripts_and_styles

JavaScript
2
star
31

rails_template

Efficient Rails Template
Ruby
2
star
32

rearmed-css

CSS Utility Classes
CSS
2
star
33

basic_ruby_and_rails_style_guide

2
star
34

simple_assets

Dead simple HTML-based assets helper for Ruby. The main idea here is to promote re-usability for projects.
Ruby
2
star
35

active_record_alias_join

Easily add custom SQL table aliases for your joins and includes
2
star
36

github_activity_scraper.rb

Scrape your entire GitHub activity information for all-time. Helps for aggregating how much open source sh!t you get done.
Ruby
2
star
37

devise_whos_here

Devise extension for logging current active users logged in using only the fast Rails cache and not your database
Ruby
2
star
38

accepts_nested_attributes_for_public_id

A patch for Rails to support using a public ID column instead of ID for use with accepts_nested_attributes_for
Ruby
2
star
39

rails_upgrade

Ruby
1
star
40

rubocop_template_for_productive_teams

Rubocop template designed speed up your teams development process instead of dragging it down
Ruby
1
star
41

github_actions_ci_example

Ruby
1
star
42

jekyll_template

CSS
1
star
43

ruby_view_template_converters

Complete solutions to convert ERB, SLIM, AND HAML with the least amount of manual effort
Ruby
1
star
44

westonganger

1
star
45

prawn_invoice

Dead simple Prawn based PDF invoice generator with support for custom invoice templates
Ruby
1
star
46

rails_dummy_app

Rails Dummy App and test_helper.rb for testing gems
Ruby
1
star
47

wagon_starter

Starter Template for LocomotiveCMS Wagon Sites
JavaScript
1
star
48

essential_capybara_helpers

A set of essential capybara helpers for everyday use
1
star
49

no_bullshit_middleman_template

Template for efficient static website generation using Middleman
HTML
1
star
50

fast_try

FastTry is a simple method wrapper to the safe navigation operator in Ruby
Ruby
1
star
51

lazy_serialize

Lazy Serialize is an alternative to ActiveRecord's serialize method which does not serialize each column until the first call to the attribute.
Ruby
1
star
52

prawn_resume

Dead simple Prawn based PDF resume generator with support for custom resume templates
Ruby
1
star
53

automatic_rails_route_testing

Template for easy exception testing for all routes within a Rails app
Ruby
1
star
54

select-sync

Javascript plugin for HTML `select` elements to synchronize by selected or disabled options
JavaScript
1
star
55

facebook_marketplace_scraper

A locally run Rails app to automatically search facebook marketplace for deals and aggregate them all in a list.
Ruby
1
star
56

rails_scrabble_with_friends

Simple web-based scrabble for you and your friends with zero friction authentication
Ruby
1
star
57

wagon_kitchen_sink

Example website for LocomotiveCMS / Wagon
JavaScript
1
star
58

rails_custom_form_builder

A good example/starter pack for a custom form builder for use with Rails form_for
Ruby
1
star
59

rails_clientside_javascript_error_handler

Easily handle client-side Javascript exceptions within your Rails app
Ruby
1
star