Module: Hanami::Helpers::FormHelper

Defined in:
lib/hanami/helpers/form_helper.rb,
lib/hanami/helpers/form_helper/values.rb,
lib/hanami/helpers/form_helper/html_node.rb,
lib/hanami/helpers/form_helper/form_builder.rb

Overview

Form builder

By including Hanami::Helpers::FormHelper it will inject one public method: form_for. This is a HTML5 form builder.

To understand the general HTML5 builder syntax of this framework, please consider to have a look at Hanami::Helpers::HtmlHelper documentation.

This builder is independent from any template engine. This was hard to achieve without a compromise: the form helper should be used in one output block in a template or as a method in a view (see the examples below).

Features:

* Support for complex markup without the need of concatenation
* Auto closing HTML5 tags
* Support for view local variables
* Method override support (PUT/PATCH/DELETE HTTP verbs aren't understood by browsers)
* Automatic generation of HTML attributes for inputs: <tt>id</tt>, <tt>name</tt>, <tt>value</tt>
* Allow to override HTML attributes
* Extract values from request params and fill <tt>value</tt> attributes
* Automatic selection of current value for radio button and select inputs
* Infinite nested fields

Supported tags and inputs:

* <tt>check_box</tt>
* <tt>color_field</tt>
* <tt>date_field</tt>
* <tt>datetime_field</tt>
* <tt>datetime_local_field</tt>
* <tt>email_field</tt>
* <tt>fields_for</tt>
* <tt>file_field</tt>
* <tt>form_for</tt>
* <tt>hidden_field</tt>
* <tt>label</tt>
* <tt>number_field</tt>
* <tt>password_field</tt>
* <tt>radio_button</tt>
* <tt>select</tt>
* <tt>submit</tt>
* <tt>text_area</tt>
* <tt>text_field</tt>

Examples:

One output block (template)

<%=
  form_for :book, routes.books_path do
    text_field :title

    submit 'Create'
  end
%>

Method (view)

require 'hanami/helpers'

class MyView
  include Hanami::Helpers::FormHelper

  def my_form
    form_for :book, routes.books_path do
      text_field :title
    end
  end
end

# Corresponding template:
#
#  <%= my_form %>

See Also:

Since:

  • 0.2.0

Defined Under Namespace

Classes: Form, FormBuilder, HtmlNode, Values

Constant Summary collapse

DEFAULT_METHOD =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Default HTTP method for form

Since:

  • 0.2.0

'POST'.freeze
DEFAULT_CHARSET =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Default charset

Since:

  • 0.2.0

'utf-8'.freeze
CSRF_TOKEN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

CSRF Token session key

This key is shared with hanamirb, hanami-controller.

Since:

  • 0.2.0

:_csrf_token

Instance Method Summary collapse

Instance Method Details

#csrf_tokenString, NilClass

Returns CSRF Protection Token stored in session.

It returns nil if sessions aren't enabled or the value is missing.

Returns:

  • (String, NilClass)

    token, if present

Since:

  • 0.2.0


417
418
419
420
421
422
423
# File 'lib/hanami/helpers/form_helper.rb', line 417

def csrf_token
  if defined?(session)
    session[CSRF_TOKEN]
  elsif defined?(locals) && locals[:session]
    locals[:session][CSRF_TOKEN]
  end
end

#form_for(name, url, options, &blk) ⇒ Hanami::Helpers::FormHelper::FormBuilder #form_for(form, attributes, &blk) ⇒ Hanami::Helpers::FormHelper::FormBuilder

Instantiate a HTML5 form builder

Examples:

Inline Values In Template

<%=
  form_for :book, routes.books_path, class: 'form-horizontal' do
    div do
      label      :title
      text_field :title, class: 'form-control'
    end

    submit 'Create'
  end
%>

Output:
  # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
  #   <div>
  #     <label for="book-title">Title</label>
  #     <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
  #   </div>
  #
  #   <button type="submit">Create</button>
  # </form>

Use In A View


module Web::Views::Books
  class New

  def form
    form_for :book, routes.books_path, class: 'form-horizontal' do
      div do
        label      :title
        text_field :title, class: 'form-control'
      end

      submit 'Create'
    end
  end
end

<%= form %>

Output:
  # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
  #   <div>
  #     <label for="book-title">Title</label>
  #     <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
  #   </div>
  #
  #   <button type="submit">Create</button>
  # </form>

Share Code Between Views


# Given the following views to create and update a resource
module Web::Views::Books
  class New
    include Web::View

    def form
      Form.new(:book, routes.books_path)
    end

    def submit_label
      'Create'
    end
  end

  class Edit
    include Web::View

    def form
      Form.new(:book, routes.book_path(id: book.id),
        {book: book}, {method: :patch})
    end

    def submit_label
      'Update'
    end
  end
end

# The respective templates can be identical:

## books/new.html.erb
<%= render partial: 'books/form' %>

## books/edit.html.erb
<%= render partial: 'books/form' %>

# While the partial can have the following markup:

## books/_form.html.erb
<%=
  form_for form, class: 'form-horizontal' do
    div do
      label      :title
      text_field :title, class: 'form-control'
    end

    submit submit_label
  end
%>

Output:
  # <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal">
  #   <div>
  #     <label for="book-title">Title</label>
  #     <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
  #   </div>
  #
  #   <button type="submit">Create</button>
  # </form>

Method override

<%=
  form_for :book, routes.book_path(id: book.id), method: :put do
    text_field :title

    submit 'Update'
  end
%>

Output:
  # <form action="/books/23" accept-charset="utf-8" id="book-form" method="POST">
  #   <input type="hidden" name="_method" value="PUT">
  #   <input type="text" name="book[title]" id="book-title" value="Test Driven Development">
  #
  #   <button type="submit">Update</button>
  # </form>

Nested fields

<%=
  form_for :delivery, routes.deliveries_path do
    text_field :customer_name

    fields_for :address do
      text_field :city
    end

    submit 'Create'
  end
%>

Output:
  # <form action="/deliveries" accept-charset="utf-8" id="delivery-form" method="POST">
  #   <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
  #   <input type="text" name="delivery[address][city]" id="delivery-address-city" value="">
  #
  #   <button type="submit">Create</button>
  # </form>

Overloads:

  • #form_for(name, url, options, &blk) ⇒ Hanami::Helpers::FormHelper::FormBuilder

    Use inline values

    Parameters:

    • name (Symbol)

      the toplevel name of the form, it's used to generate input names, ids, and to lookup params to fill values.

    • url (String)

      the form action URL

    • options (Hash)

      HTML attributes to pass to the form tag and form values

    • blk (Proc)

      A block that describes the contents of the form

    Options Hash (options):

    • :values (Hash)

      An optional payload of objects to pass

  • #form_for(form, attributes, &blk) ⇒ Hanami::Helpers::FormHelper::FormBuilder

    Use Form

    Parameters:

    • form (Hanami::Helpers::FormHelper::Form)

      a form object

    • attributes (Hash)

      HTML attributes to pass to the form tag and form values

    • blk (Proc)

      A block that describes the contents of the form

Returns:

See Also:

Since:

  • 0.2.0


398
399
400
401
402
403
404
405
406
407
408
# File 'lib/hanami/helpers/form_helper.rb', line 398

def form_for(name, url, options = {}, &blk)
  form = if name.is_a?(Form)
           options = url
           name
         else
           Form.new(name, url, options.delete(:values))
         end

  attributes = { action: form.url, method: form.verb, :'accept-charset' => DEFAULT_CHARSET, id: "#{form.name}-form" }.merge(options)
  FormBuilder.new(form, attributes, self, &blk)
end