Skip to content

Forms

Form Builder

Using #form_builder you can access some utility methods to build labels and inputs:

class ExamplePage
  include Blueprint::HTML

  def blueprint
    form_builder action: "/sign-in", method: :post do |form|
      form.label :email
      form.email_input :email

      form.label :password
      form.password_input :password
    end
  end
end

ExamplePage.new.to_s

Output:

<form action="/sign-in" method="post">
  <label for="email">Email</label>
  <input type="email" id="email" name="email">

  <label for="password">Password</label>
  <input type="password" id="password" name="password">
</form>

When passing a scope to the form builder, it will be used to build input IDs and names:

class ExamplePage
  include Blueprint::HTML

  def blueprint
    form_builder scope: :user, action: "/sign-in", method: :post do |form|
      form.label :email
      form.email_input :email

      form.label :password
      form.password_input :password
    end
  end
end

ExamplePage.new.to_s

Output:

<form action="/sign-in" method="post">
  <label for="user_email">Email</label>
  <input type="email" id="user_email" name="user[email]">

  <label for="user_password">Password</label>
  <input type="password" id="user_password" name="user[password]">
</form>

Inputs

If you don't need a form builder and still want to build inputs easily, you can use the #inputs method, it is similar to form builder but it doesn't create a form tag around the inputs. You can provide a scope or even use it inside the form builder.

class ExamplePage
  include Blueprint::HTML

  def blueprint
    inputs do |builder|
      builder.label :query
      builder.text_input :query
    end

    inputs :search do |builder|
      builder.label :query
      builder.text_input :query
    end

    form_builder scope: :user do |form|
      form.label :username
      form.text_input :username

      inputs :settings do |settings|
        settings.label :subdomain
        settings.text_input :subdomain
      end
    end
  end
end

ExamplePage.new.to_s

Output:

<label for="query">Query</label>
<input type="text" id="query" name="query">

<label for="search_query">Query</label>
<input type="text" id="search_query" name="search[query]">

<form>
  <label for="user_username">Username</label>
  <input type="text" id="user_username" name="user[username]">

  <label for="settings_subdomain">Subdomain</label>
  <input type="text" id="settings_subdomain" name="settings[subdomain]">
</form>

Custom Form Builder

You can create your own form builder by subclassing Blueprint::Form::Builder(T) and use builder: YourFormBuilder in the #form_builder method.

class MyFormBuilder(T) < Blueprint::Form::Builder(T)
  def text_input(attribute : Symbol, **html_options)
    super(attribute, **html_options.merge(class: "form-input"))
  end

  def money_input(attribute : Symbol, **html_options)
    text_input(attribute, **html_options.merge(inputmode: :numeric, mask: "$#,##"))
  end

  def input_wrapper(&)
    div class: "form-control" do
      yield
    end
  end
end

class ExamplePage
  include Blueprint::HTML

  def blueprint
    form_builder scope: :income, builder: MyFormBuilder do |form|
      form.input_wrapper do
        form.label :description
        form.text_input :description
      end

      form.input_wrapper do
        form.label :amount
        form.money_input :amount
      end
    end
  end
end

ExamplePage.new.to_s

Output:

<form>
  <div class="form-control">
    <label for="income_description">Description</label>
    <input type="text" id="income_description" name="income[description]" class="form-input">
  </div>

  <div class="form-control">
    <label for="income_amount">Amount</label>
    <input type="text" id="income_amount" name="income[amount]" inputmode="numeric" mask="$#,##" class="form-input">
  </div>
</form>

If you want to set your form builder as the default form builder, you can override default_form_builder method to return your form builder class:

class BasePage
  include Blueprint::HTML

  def default_form_builder
    MyFormBuilder
  end
end

Methods

These are the available methods for form/input builder:

label

form.label :name
# <label for="name">Name</label>

form.label :name, "Custom label"
# <label for="name">Custom label</label>

form.label :name, class: "form-label" do
  "Nome"
end
# <label for="name" class="form-label">Nome</label>

form.label :name do
  span "Name"
  span(class: "label-addon") { "*required" }
end
# <label for="name">
#   <span>Name</span>
#   <span class="label-addon">*required</span>
# </label>

form.label :color, "Red", value: :red
# <label for="color_red">Red</label>

checkbox_input

The HTML specification says unchecked checkboxes are not successful, and thus web browsers do not send them. To prevent this the checkbox_input generates an auxiliary hidden field before every checkbox. The hidden field has the same name and its attributes mimic an unchecked checkbox.

form.checkbox_input :accepted
# <input type="hidden" name="accepted" value="0">
# <input type="checkbox" id="accepted" name="accepted" value="1">

The default values are "1" for checked, and "0" for unchecked. You can provide custom values if needed:

form.checkbox_input :accepted, "yes", "no"
# <input type="hidden" name="accepted" value="no">
# <input type="checkbox" id="accepted" name="accepted" value="yes">

If you don't want the hidden field, you can pass unchecked_value: nil:

form.checkbox_input :accepted, unchecked_value: nil
# <input type="checkbox" id="accepted" name="accepted" value="1">

color_input

form.color_input :bg_color, class: "color-picker"
# <input type="color" id="bg_color" name="bg_color" class="color-picker">

date_input

form.date_input :due_date, class: "date-picker"
# <input type="date" id="due_date" name="due_date" class="date-picker">

datetime_input

form.datetime_input :due_date, class: "date-picker"
# <input type="datetime" id="due_date" name="due_date" class="date-picker">

datetime_local_input

form.datetime_local_input :due_date, class: "date-picker"
# <input type="datetime-local" id="due_date" name="due_date" class="date-picker">

email_input

form.email_input :email, class: "form-input"
# <input type="email" id="email" name="email" class="form-input">

file_input

form.file_input :upload, class: "file-upload"
# <input type="file" id="upload" name="upload" class="file-upload">

hidden_input

form.hidden_input :token, value: "1234"
# <input type="hidden" id="token" name="token" value="1234">

month_input

form.month_input :date_month, class: "month-picker"
# <input type="month" id="date_month" name="date_month" value="month-picker">

number_input

form.number_input :age, value: 18
# <input type="number" id="age" name="age" value="18">

password_input

form.password_input :secret, class: "form-input"
# <input type="password" id="secret" name="secret" class="form-input">

radio_input

form.radio_input :color, :red, class: "radio-input"
# <input type="radio" id="color_red" name="color" value="red" class="radio-input">

range_input

form.range_input :volume, 1..99, step: 5
# <input type="range" id="volume" name="volume" min="1" max="99" step="5">

form.range_input :volume_db, min: -20, max: 20, step: 2
# <input type="range" id="volume_db" name="volume_db" min="-20" max="20" step="2">

search_input

form.search_input :query, class: "form-input"
# <input type="search" id="query" name="query" class="form-input">

tel_input

form.tel_input :phone, class: "form-input"
# <input type="tel" id="phone" name="phone" class="form-input">

text_input

form.text_input :username, class: "form-input"
# <input type="text" id="username" name="username" class="form-input">

time_input

form.time_input :paid_at, class: "time-picker"
# <input type="time" id="paid_at" name="paid_at" class="time-picker">

url_input

form.url_input :base_url, class: "form-input"
# <input type="url" id="base_url" name="base_url" class="form-input">

week_input

form.week_input :born_on, class: "form-input"
# <input type="week" id="born_on" name="born_on" class="form-input">

submit

form.submit
# <input type="submit" value="Submit">

form.submit "Send"
# <input type="submit" value="Send">

form.submit "Upload", class: "btn btn-lg"
# <input type="submit" value="Upload" class="btn btn-lg">

reset

form.reset
# <input type="reset" value="Reset">

form.reset "Clear"
# <input type="reset" value="Clear">

form.reset "Clear", class: "btn btn-lg"
# <input type="reset" value="Clear" class="btn btn-lg">