module Hanami::Helpers::FormHelper
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>
@since 0.2.0
@see Hanami::Helpers::FormHelper#form_for
@see Hanami::Helpers::HtmlHelper
@example One output block (template)
<%= form_for :book, routes.books_path do text_field :title submit 'Create' end %>
@example 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 <!-- use this in the template --> <%= my_form %>
Constants
- CSRF_TOKEN
CSRF Token session key
This key is shared with
hanamirb
,hanami-controller
.@since 0.2.0 @api private
- DEFAULT_CHARSET
Default charset
@since 0.2.0 @api private
- DEFAULT_METHOD
Default HTTP method for form
@since 0.2.0 @api private
Public Instance Methods
Returns CSRF Protection Token stored in session.
It returns nil
if sessions aren't enabled or the value is missing.
@return [String,NilClass] token, if present
@since 0.2.0
# File lib/hanami/helpers/form_helper.rb, line 454 def csrf_token if defined?(session) session[CSRF_TOKEN] elsif defined?(locals) && locals[:session] locals[:session][CSRF_TOKEN] end end
Instantiate a HTML5 form builder
@overload form_for
(name, url, options, &blk)
Use inline values @param name [Symbol] the toplevel name of the form, it's used to generate input names, ids, and to lookup params to fill values. @param url [String] the form action URL @param options [Hash] HTML attributes to pass to the form tag and form values @option options [Hash] :values An optional payload of objects to pass @option options [Hash] :params An optional override of params @param blk [Proc] A block that describes the contents of the form
@overload form_for
(form, attributes = {}, &blk)
Use Form @param form [Hanami::Helpers::FormHelper::Form] a form object @param attributes [Hash] HTML attributes to pass to the form tag and form values @param blk [Proc] A block that describes the contents of the form
@return [Hanami::Helpers::FormHelper::FormBuilder] the form builder
@since 0.2.0
@see Hanami::Helpers::FormHelper
@see Hanami::Helpers::FormHelper::Form
@see Hanami::Helpers::FormHelper::FormBuilder
@example 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"> <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <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>
@example 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 <!-- in the corresponding template use this --> <%= form %> <!-- output --> <form action="/books" method="POST" accept-charset="utf-8" id="book-form" class="form-horizontal"> <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <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>
@example 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"> <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <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>
@example 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="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <input type="text" name="book[title]" id="book-title" value="Test Driven Development"> <button type="submit">Update</button> </form>
@example 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="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <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>
@example Override params
<%= form_for :song, routes.songs_path, params: { song: { title: "Envision" } } do text_field :title submit 'Create' end %> <!-- output --> <form action="/songs" accept-charset="utf-8" id="song-form" method="POST"> <input type="hidden" name="_csrf_token" value="920cd5bfaecc6e58368950e790f2f7b4e5561eeeab230aa1b7de1b1f40ea7d5d"> <input type="text" name="song[title]" id="song-title" value="Envision"> <button type="submit">Create</button> </form>
# File lib/hanami/helpers/form_helper.rb, line 431 def form_for(name, url = nil, options = {}, &blk) # rubocop:disable Metrics/MethodLength form = if name.is_a?(Form) options = url || {} name else Form.new(name, url, options.delete(:values)) end params = options.delete(:params) opts = options.dup opts[:"data-remote"] = opts.delete(:remote) if opts.key?(:remote) attributes = { action: form.url, method: form.verb, 'accept-charset': DEFAULT_CHARSET, id: "#{form.name}-form" }.merge(opts) FormBuilder.new(form, attributes, self, params, &blk) end