Spec-Zone.ru › Ruby on Rails 4.2

модуль ActionView::Helpers::FormHelper

Включенные модули:
ActionView::Helpers::FormTagHelper, ActionView::Helpers::UrlHelper, ActionView::ModelNaming

Помощники формы предназначены для того, чтобы работа с ресурсами была намного проще по сравнению с использованием обычного HTML.

Как правило, форма, предназначенная для создания или обновления ресурса, отражает идентичность ресурса несколькими способами: (i) URL, на который отправляется форма (атрибут элемента формы action) должен привести к перенаправлению запроса на соответствующий контроллер-действие (с соответствующим параметром :id в случае существующего ресурса), (ii) поля ввода должны быть именованы таким образом, чтобы в контроллере их значения появлялись в соответствующих местах в хеше params, и (iii) для существующей записи, когда форма первоначально отображается, поля ввода, соответствующие атрибутам ресурса, должны отображать текущие значения этих атрибутов.

В Rails это обычно достигается путем создания формы с помощью form_for и ряда связанных методов-помощников. form_for генерирует соответствующий тег form и возвращает объект генератора формы, который знает модель, к которой относится форма. Поля ввода создаются путем вызова методов, определенных в генераторе формы, что означает, что они могут генерировать соответствующие имена и значения по умолчанию, соответствующие атрибутам модели, а также удобные идентификаторы и т.д. Конвенции в генерируемых именах полей позволяют контроллерам получать данные формы в виде хорошо структурированного хеша params без особых усилий с вашей стороны.

Например, для создания нового человека вы обычно создаете новую инстанцию Person в действии PeopleController#new, @person, а в шаблоне представления передаете этот объект в form_for:

<%= form_for @person do |f| %>
  <%= f.label :first_name %>:
  <%= f.text_field :first_name %><br />

  <%= f.label :last_name %>:
  <%= f.text_field :last_name %><br />

  <%= f.submit %>
<% end %>

Сгенерированный HTML для этого будет следующим (модуль форматирования):

<form action="/people" class="new_person" id="new_person" method="post">
  <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
  <label for="person_first_name">First name</label>:
  <input id="person_first_name" name="person[first_name]" type="text" /><br />

  <label for="person_last_name">Last name</label>:
  <input id="person_last_name" name="person[last_name]" type="text" /><br />

  <input name="commit" type="submit" value="Create Person" />
</form>

Как вы видите, HTML отражает знания о ресурсе в нескольких местах, таких как путь, по которому должна быть отправлена форма, или имена полей ввода.

В частности, благодаря соглашениям, принятым в генерируемых именах полей, контроллер получает вложенный хеш params[:person] с атрибутами человека, установленными в форме. Этот хеш готов для передачи в Person.create:

if @person = Person.create(params[:person])
  # success
else
  # error handling
end

Интересно, что тот же код представления в предыдущем примере можно использовать для редактирования человека. Если @person это существующая запись с именем «John Smith» и ID 256, код выше по умолчанию выведет:

<form action="/people/256" class="edit_person" id="edit_person_256" method="post">
  <input name="_method" type="hidden" value="patch" />
  <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
  <label for="person_first_name">First name</label>:
  <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />

  <label for="person_last_name">Last name</label>:
  <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />

  <input name="commit" type="submit" value="Update Person" />
</form>

Обратите внимание, что конечная точка, значения по умолчанию и подпись кнопки отправки настроены для @person. Это работает таким образом, потому что связанные помощники знают, является ли ресурс новой записью или нет, и генерируют HTML соответственно.

Контроллер снова получит данные формы в params[:person], готовые для передачи в Person#update:

if @person.update(params[:person])
  # success
else
  # error handling
end

Вот как вы обычно работаете с ресурсами.

Общедоступные методы экземпляра

check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0") Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 945
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
  Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
end

Возвращает тег checkbox, настроенный для доступа к указанному атрибуту (определяемому method) объекта, назначенного шаблону (определяемому object). Этот объект должен быть объектом экземпляра (@object), а не локальным объектом. Предполагается, что method возвращает целое число, и если это целое число больше нуля, то флажок установлен. Дополнительные параметры тега ввода можно передать в виде хеша с options. Значение checked_value по умолчанию равно 1, а значение unchecked_value по умолчанию равно 0, что удобно для логических значений.

Возможная проблема

Спецификация HTML говорит, что неотмеченные флажки не успешны, и поэтому веб-браузеры их не отправляют. К сожалению, это приводит к проблеме: если модель Invoice имеет флаг paid, а в форме, редактирующей оплаченный счет-фактуру, пользователь снимает его флажок, параметр paid не отправляется. Таким образом, любой массовый механизм присваивания, такой как

@invoice.update(params[:invoice])

не обновит флаг.

Чтобы предотвратить это, помощник генерирует вспомогательное скрытое поле перед самим флажком. Скрытое поле имеет то же имя, а его атрибуты имитируют снятый флажок.

Таким образом, клиент либо отправляет только скрытое поле (представляющее снятый флажок), либо оба поля. Поскольку спецификация HTML гласит, что пары ключ/значение должны быть отправлены в том же порядке, в котором они появляются в форме, а извлечение параметров получает последнее вхождение любого повторяющегося ключа в строке запроса, это работает для обычных форм.

К сожалению, это решение не работает, когда флажок находится внутри массивоподобного параметра, как в

<%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
  <%= form.check_box :paid %>
  ...
<% end %>

поскольку повторение имени параметра именно то, что Rails пытается отличить элементы массива. Для каждого элемента с отмеченным флажком вы получаете дополнительный теневой элемент только с этим атрибутом, присвоенным «0».

В этом случае лучше использовать check_box_tag или использовать хеши вместо массивов.

# Let's say that @post.validated? is 1:
check_box("post", "validated")
# => <input name="post[validated]" type="hidden" value="0" />
#    <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />

# Let's say that @puppy.gooddog is "no":
check_box("puppy", "gooddog", {}, "yes", "no")
# => <input name="puppy[gooddog]" type="hidden" value="no" />
#    <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />

check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
# => <input name="eula[accepted]" type="hidden" value="no" />
#    <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
color_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 974
def color_field(object_name, method, options = {})
  Tags::ColorField.new(object_name, method, self, options).render
end

Возвращает #text_field типа «цвет».

color_field("car", "color")
# => <input id="car_color" name="car[color]" type="color" value="#000000" />
date_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1038
def date_field(object_name, method, options = {})
  Tags::DateField.new(object_name, method, self, options).render
end

Возвращает #text_field типа «дата».

date_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="date" />

Значение по умолчанию генерируется путем попытки вызвать «to_date» для значения объекта, что делает его поведением, ожидаемым для экземпляров DateTime и ActiveSupport::TimeWithZone. Вы все еще можете переопределить это, явно передав параметр «value», например:

@user.born_on = Date.new(1984, 1, 27)
date_field("user", "born_on", value: "1984-05-12")
# => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />

Вы можете создать значения для атрибутов «min» и «max», передав экземпляры Date или Time в хеш параметров.

date_field("user", "born_on", min: Date.today)
# => <input id="user_born_on" name="user[born_on]" type="date" min="2014-05-20" />

В качестве альтернативы вы можете передать String отформатированный как дата ISO8601 в качестве значений для «min» и «max».

date_field("user", "born_on", min: "2014-05-20")
# => <input id="user_born_on" name="user[born_on]" type="date" min="2014-05-20" />
datetime_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1096
def datetime_field(object_name, method, options = {})
  Tags::DatetimeField.new(object_name, method, self, options).render
end

Возвращает #text_field типа «дата и время».

datetime_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="datetime" />

Значение по умолчанию генерируется путем попытки вызвать strftime с «%Y-%m-%dT%T.%L%z» для значения объекта, что делает его поведением, ожидаемым для экземпляров DateTime и ActiveSupport::TimeWithZone.

@user.born_on = Date.new(1984, 1, 12)
datetime_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />

Вы можете создать значения для атрибутов «min» и «max», передав экземпляры Date или Time в хеш параметров.

datetime_field("user", "born_on", min: Date.today)
# => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />

В качестве альтернативы вы можете передать String отформатированный как дата и время ISO8601 с часовым поясом в качестве значений для «min» и «max».

datetime_field("user", "born_on", min: "2014-05-20T00:00:00+0000")
# => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
datetime_local_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1125
def datetime_local_field(object_name, method, options = {})
  Tags::DatetimeLocalField.new(object_name, method, self, options).render
end

Возвращает #text_field типа «дата и время локальное».

datetime_local_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="datetime-local" />

Значение по умолчанию генерируется путем попытки вызвать strftime с «%Y-%m-%dT%T» для значения объекта, что делает его поведением, ожидаемым для экземпляров DateTime и ActiveSupport::TimeWithZone.

@user.born_on = Date.new(1984, 1, 12)
datetime_local_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />

Вы можете создать значения для атрибутов «min» и «max», передав экземпляры Date или Time в хеш параметров.

datetime_local_field("user", "born_on", min: Date.today)
# => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />

В качестве альтернативы вы можете передать String отформатированный как дата и время ISO8601 в качестве значений для «min» и «max».

datetime_local_field("user", "born_on", min: "2014-05-20T00:00:00")
# => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
email_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1177
def email_field(object_name, method, options = {})
  Tags::EmailField.new(object_name, method, self, options).render
end

Возвращает #text_field типа «электронная почта».

email_field("user", "address")
# => <input id="user_address" name="user[address]" type="email" />
fields_for(record_name, record_object = nil, options = {}, &block) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 712
def fields_for(record_name, record_object = nil, options = {}, &block)
  builder = instantiate_builder(record_name, record_object, options)
  capture(builder, &block)
end

Создаёт область видимости вокруг конкретного объекта модели, как #form_for, но не создаёт сами теги формы. Это делает #fields_for подходящим для указания дополнительных объектов модели в той же форме.

Хотя использование и назначение fields_for аналогично form_for, его сигнатура метода немного отличается. Как и form_for, он передаёт объект FormBuilder, связанный с конкретным объектом модели, в блок, и внутри блока можно вызывать методы для генерации полей, связанных с объектом модели. Поля могут отражать объект модели двумя способами — как они называются (отсюда то, как отправленные значения отображаются в хэше params в контроллере) и какие значения по умолчанию отображаются, когда форма, в которой отображаются поля, впервые отображается. Для того, чтобы оба этих свойства можно было указать независимо, и имя объекта (представленное символом или строкой), и сам объект можно передавать в метод отдельно —

<%= form_for @person do |person_form| %>
  First name: <%= person_form.text_field :first_name %>
  Last name : <%= person_form.text_field :last_name %>

  <%= fields_for :permission, @person.permission do |permission_fields| %>
    Admin?  : <%= permission_fields.check_box :admin %>
  <% end %>

  <%= person_form.submit %>
<% end %>

В этом случае поле с чекбоксом будет представлено тегом HTML input с атрибутом name permission[admin], а отправленное значение отобразится в контроллере как params[:permission][:admin]. Если @person.permission является существующим записыванием с атрибутом admin, начальное состояние чекбокса при первом отображении будет отражать значение @person.permission.admin.

Часто это можно упростить, передав только имя объекта модели в fields_for —

<%= fields_for :permission do |permission_fields| %>
  Admin?: <%= permission_fields.check_box :admin %>
<% end %>

…в таком случае, если :permission также является именем переменной экземпляра @permission, начальное состояние поля ввода будет отражать значение атрибута этой переменной @permission.admin.

В качестве альтернативы, вы можете передать только сам объект модели (если первый аргумент не является строкой или символом, fields_for поймёт, что имя опущено) —

<%= fields_for @person.permission do |permission_fields| %>
  Admin?: <%= permission_fields.check_box :admin %>
<% end %>

и fields_for выведет необходимое имя поля из класса объекта модели, например, если @person.permission, принадлежит классу Permission, поле по-прежнему будет иметь имя permission[admin].

Примечание: это также работает для методов в FormOptionHelper и DateHelper, которые предназначены для работы с объектом в качестве основы, например FormOptionHelper#collection_select и ActionView::Helpers::DateHelper#datetime_select.

Примеры вложенных атрибутов

Когда у объекта, относящегося к текущей области видимости, есть вложенный писатель атрибутов для определённого атрибута, #fields_for создаст новую область видимости для этого атрибута. Это позволяет создавать формы, которые одновременно устанавливают или изменяют атрибуты родительского объекта и его ассоциаций.

Вложенные писатели атрибутов — это обычные методы установки, названные в честь ассоциации. Наиболее распространённый способ определения этих писателей — это использование accepts_nested_attributes_for в определении модели или определение метода с соответствующим именем. Например, метод установки атрибута для ассоциации :address называется address_attributes=.

То, будет ли сгенерирован билдер формы стиля один-к-одному или один-ко-многим, зависит от того, возвращает ли обычный метод чтения один объект или массив объектов.

Один-к-одному

Рассмотрим класс Person, который возвращает один адрес из метода чтения address и отвечает на метод записи address_attributes=:

class Person
  def address
    @address
  end

  def address_attributes=(attributes)
    # Process the attributes hash
  end
end

Теперь эту модель можно использовать с вложенным #fields_for, как показано ниже:

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :address do |address_fields| %>
    Street  : <%= address_fields.text_field :street %>
    Zip code: <%= address_fields.text_field :zip_code %>
  <% end %>
  ...
<% end %>

Если адрес уже является ассоциацией в Person, вы можете использовать accepts_nested_attributes_for для определения метода записи за вас:

class Person < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
end

Если вы хотите удалить связанную модель через форму, вы должны сначала включить её с помощью опции :allow_destroy для accepts_nested_attributes_for:

class Person < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address, allow_destroy: true
end

Теперь, когда вы используете элемент формы с параметром _destroy со значением, которое приводится к true, вы будете удалять связанную модель (например, 1, '1', true или 'true'):

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :address do |address_fields| %>
    ...
    Delete: <%= address_fields.check_box :_destroy %>
  <% end %>
  ...
<% end %>

Один-ко-многим

Рассмотрим класс Person, который возвращает массив экземпляров Project из метода чтения projects и отвечает на метод записи projects_attributes=:

class Person
  def projects
    [@project1, @project2]
  end

  def projects_attributes=(attributes)
    # Process the attributes hash
  end
end

Обратите внимание, что метод записи projects_attributes= фактически необходим для #fields_for, чтобы правильно определить :projects как коллекцию, и правильно установить индексы в разметке формы.

Если проекты уже являются ассоциацией в Person, вы можете использовать accepts_nested_attributes_for для определения метода записи за вас:

class Person < ActiveRecord::Base
  has_many :projects
  accepts_nested_attributes_for :projects
end

Теперь эту модель можно использовать с вложенным fields_for. Блок, переданный вложенному вызову #fields_for, будет повторяться для каждого экземпляра в коллекции:

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :projects do |project_fields| %>
    <% if project_fields.object.active? %>
      Name: <%= project_fields.text_field :name %>
    <% end %>
  <% end %>
  ...
<% end %>

Также можно указать используемый экземпляр:

<%= form_for @person do |person_form| %>
  ...
  <% @person.projects.each do |project| %>
    <% if project.active? %>
      <%= person_form.fields_for :projects, project do |project_fields| %>
        Name: <%= project_fields.text_field :name %>
      <% end %>
    <% end %>
  <% end %>
  ...
<% end %>

Или используемую коллекцию:

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
    Name: <%= project_fields.text_field :name %>
  <% end %>
  ...
<% end %>

Если вы хотите удалить какие-либо из связанных моделей через форму, вы должны сначала включить это с помощью опции :allow_destroy для accepts_nested_attributes_for:

class Person < ActiveRecord::Base
  has_many :projects
  accepts_nested_attributes_for :projects, allow_destroy: true
end

Это позволит вам указать, какие модели нужно удалить в хэше атрибутов, добавив элемент формы для параметра _destroy со значением, которое приводится к true (например, 1, '1', true или 'true'):

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :projects do |project_fields| %>
    Delete: <%= project_fields.check_box :_destroy %>
  <% end %>
  ...
<% end %>

Когда используется коллекция, вы можете узнать индекс каждого объекта в массиве. Для этой цели в объекте FormBuilder доступен метод index.

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :projects do |project_fields| %>
    Project #<%= project_fields.index %>
    ...
  <% end %>
  ...
<% end %>

Обратите внимание, что #fields_for автоматически сгенерирует скрытое поле для хранения идентификатора записи. В некоторых случаях это скрытое поле не нужно, и вы можете передать include_id: false для предотвращения автоматического рендеринга #fields_for.

file_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 857
def file_field(object_name, method, options = {})
  Tags::FileField.new(object_name, method, self, options).render
end

Возвращает тег ввода для загрузки файлов, настроенный для доступа к указанному атрибуту (идентифицируемому method) объекта, назначенного шаблону (идентифицируемому object). Дополнительные параметры для тега ввода можно передать в виде хэша с options. Эти параметры будут добавлены к HTML в качестве атрибута элемента HTML, как показано в примере.

Использование этого метода внутри блока form_for установит кодировку содержащей формы на multipart/form-data.

Параметры

  • Создаёт стандартные атрибуты HTML для тега.

  • :disabled - Если установлено в true, пользователь не сможет использовать этот ввод.

  • :multiple - Если установлено в true, *в большинстве обновлённых браузеров* пользователь сможет выбрать несколько файлов.

  • :accept - Если установлено на один или несколько типов MIME, пользователю будет предложен фильтр при выборе файла. Вам всё равно необходимо настроить валидацию модели.

Примеры

file_field(:user, :avatar)
# => <input type="file" id="user_avatar" name="user[avatar]" />

file_field(:post, :image, :multiple => true)
# => <input type="file" id="post_image" name="post[image]" multiple="true" />

file_field(:post, :attached, accept: 'text/html')
# => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />

file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
# => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />

file_field(:attachment, :file, class: 'file_input')
# => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
form_for(record, options = {}, &block) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 422
def form_for(record, options = {}, &block)
  raise ArgumentError, "Missing block" unless block_given?
  html_options = options[:html] ||= {}

  case record
  when String, Symbol
    object_name = record
    object      = nil
  else
    object      = record.is_a?(Array) ? record.last : record
    raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
    object_name = options[:as] || model_name_from_record_or_class(object).param_key
    apply_form_for_options!(record, object, options)
  end

  html_options[:data]   = options.delete(:data)   if options.has_key?(:data)
  html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
  html_options[:method] = options.delete(:method) if options.has_key?(:method)
  html_options[:enforce_utf8] = options.delete(:enforce_utf8) if options.has_key?(:enforce_utf8)
  html_options[:authenticity_token] = options.delete(:authenticity_token)

  builder = instantiate_builder(object_name, object, options)
  output  = capture(builder, &block)
  html_options[:multipart] ||= builder.multipart?

  html_options = html_options_for_form(options[:url] || {}, html_options)
  form_tag_with_body(html_options, output)
end

Создаёт форму, которая позволяет пользователю создавать или обновлять атрибуты конкретного объекта модели.

Метод может быть использован несколькими слегка разными способами, в зависимости от того, насколько вы хотите полагаться на Rails для автоматического вывода формы на основе модели. Для обобщённого объекта модели форма может быть создана путём передачи form_for строки или символа, представляющего объект, который нас интересует:

<%= form_for :person do |f| %>
  First name: <%= f.text_field :first_name %><br />
  Last name : <%= f.text_field :last_name %><br />
  Biography : <%= f.text_area :biography %><br />
  Admin?    : <%= f.check_box :admin %><br />
  <%= f.submit %>
<% end %>

Переменная f , передаваемая в блок, — это объект FormBuilder, который включает в себя знания об объекте модели, представленном :person , переданном в form_for. Методы, определённые в FormBuilder, используются для генерации полей, связанных с этой моделью. Таким образом, например,

<%= f.text_field :first_name %>

будет преобразовано в

<%= text_field :person, :first_name %>

что приводит к тегу HTML <input>, у которого атрибут name равен person[first_name]. Это означает, что при отправке формы значение, введённое пользователем, будет доступно в контроллере как params[:person][:first_name].

Для полей, сгенерированных таким образом с помощью FormBuilder, если :person также является именем экземпляра переменной @person, значение по умолчанию поля, отображаемого при первоначальном отображении формы (например, в ситуации, когда вы редактируете существующий объект), будет значением соответствующего атрибута @person.

Правый аргумент form_for — это необязательный хеш опций —

  • :url — URL, на который должна быть отправлена форма. Это может быть представлено так же, как значения, переданные в url_for или link_to. Таким образом, вы можете напрямую использовать именованный маршрут. Когда модель представлена строкой или символом, как в примере выше, если опция :url не указана, по умолчанию форма будет отправлена обратно на текущий URL (мы опишем ниже альтернативный резоурс-ориентированный способ использования form_for, в котором URL не нужно указывать явно).

  • :namespace — пространство имён для вашей формы, чтобы обеспечить уникальность атрибутов id элементов формы. Атрибут пространства имён будет предваряться символом подчеркивания в сгенерированном HTML id.

  • :method — метод, используемый при отправке формы, обычно «get» или «post». Если используется «patch», «put», «delete» или другой глагол, в форму добавляется скрытый элемент с именем _method, чтобы смоделировать глагол над POST.

  • :authenticity_token — маркер аутентификации, используемый в форме. Используйте только в случае необходимости передачи строки маркера аутентификации или для того, чтобы вообще не добавлять поле authenticity_token (передавая false). Дистанционные формы могут опускать встроенный маркер аутентификации, устанавливая config.action_view.embed_authenticity_token_in_remote_forms = false. Это полезно, когда вы кэшируете фрагменты формы. Дистанционные формы получают маркер аутентификации из тега meta, поэтому вставка не требуется, если вы поддерживаете браузеры без JavaScript.

  • :remote — если установлено в true, позволит драйверам Unobtrusive JavaScript контролировать поведение отправки. По умолчанию это поведение — отправка по AJAX.

  • :enforce_utf8 — если установлено в false, скрытый элемент с именем utf8 не выводится.

  • :html — необязательные HTML-атрибуты для тега формы.

Обратите также внимание, что form_for не создаёт исключительный контекст. По-прежнему можно использовать как самостоятельные методы FormHelper, так и методы из FormTagHelper. Например:

<%= form_for :person do |f| %>
  First name: <%= f.text_field :first_name %>
  Last name : <%= f.text_field :last_name %>
  Biography : <%= text_area :person, :biography %>
  Admin?    : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
  <%= f.submit %>
<% end %>

Это также работает для методов в FormOptionHelper и DateHelper, которые предназначены для работы с объектом в качестве основы, например, FormOptionHelper#collection_select и ActionView::Helpers::DateHelper#datetime_select.

form_for с объектом модели

В примерах выше объект для создания или редактирования представлялся символом, переданным в form_for, и мы отметили, что строка также может быть использована аналогично. Однако также можно передать сам объект модели в form_for. Например, если @post — это существующий объект, который вы хотите отредактировать, вы можете создать форму, используя

<%= form_for @post do |f| %>
  ...
<% end %>

Это работает почти так же, как описано ранее, с несколькими небольшими исключениями. Во-первых, префикс, используемый для именования элементов ввода в форме (следовательно, ключ, обозначающий их в хеше params ), на самом деле происходит от класса объекта, например, params[:post] если класс объекта Post. Однако это можно переопределить с помощью опции :as, например —

<%= form_for(@person, as: :client) do |f| %>
  ...
<% end %>

что приведёт к params[:client].

Во-вторых, значения полей, отображаемые при первоначальном отображении формы, берутся из атрибутов объекта, переданного в form_for, независимо от того, является ли объект переменной экземпляра. Таким образом, например, если у нас есть локальная переменная post , представляющая существующий объект,

<%= form_for post do |f| %>
  ...
<% end %>

будет генерировать форму с полями, начальное состояние которых отражает текущие значения атрибутов post.

Ресурсно-ориентированный стиль

В только что показанных примерах, хотя это не указано явно, нам всё равно нужно использовать опцию :url для указания места отправки формы. Однако возможна дальнейшая упрощение, если объект, переданный в form_for , является ресурсом, то есть соответствует набору маршрутов RESTful, например, определённых с помощью метода resources в config/routes.rb. В этом случае Rails просто выведет соответствующий URL из объекта. Например,

<%= form_for @post do |f| %>
  ...
<% end %>

тогда эквивалентно чему-то вроде:

<%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
  ...
<% end %>

И для нового объекта

<%= form_for(Post.new) do |f| %>
  ...
<% end %>

эквивалентно чему-то вроде:

<%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
  ...
<% end %>

Однако вы всё равно можете переопределить отдельные соглашения, такие как:

<%= form_for(@post, url: super_posts_path) do |f| %>
  ...
<% end %>

Вы также можете задать формат ответа, например так:

<%= form_for(@post, format: :json) do |f| %>
  ...
<% end %>

Для маршрутов с именованным пространством имён, как admin_post_url:

<%= form_for([:admin, @post]) do |f| %>
 ...
<% end %>

Если у вашего ресурса определены ассоциации, например, вы хотите добавить комментарии к документу, при условии, что маршруты настроены правильно:

<%= form_for([@document, @comment]) do |f| %>
 ...
<% end %>

Где @document = Document.find(params[:id]) и @comment = Comment.new.

Установка метода

Вы можете принудительно заставить форму использовать весь набор HTTP-глаголов, установив

method: (:get|:post|:patch|:put|:delete)

в хеше опций. Если глагол не GET или POST, которые напрямую поддерживаются HTML-формами, форма будет установлена в POST, а скрытый элемент, называемый _method, будет содержать предполагаемый глагол для интерпретации сервером.

Ненавязчивый JavaScript

Указание:

remote: true

в хеше опций создаёт форму, которая позволит драйверам неинвазивного JavaScript изменять её поведение. Ожидаемое поведение по умолчанию — XMLHttpRequest в фоновом режиме вместо обычной POST-отправки, но в конечном итоге поведение — это выбор разработчика реализации драйвера JavaScript. Даже если для сериализации элементов формы используется JavaScript, отправка формы будет работать так же, как и обычная отправка, с точки зрения получателя (все элементы доступны в params).

Пример:

<%= form_for(@post, remote: true) do |f| %>
  ...
<% end %>

Сгенерированный HTML для этого будет:

<form action='http://www.example.com' method='post' data-remote='true'>
  <input name='_method' type='hidden' value='patch' />
  ...
</form>

Установка HTML-опций

Вы можете установить атрибуты данных напрямую, передав хеш данных, но все остальные HTML-опции должны быть обернуты в ключ HTML. Пример:

<%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
  ...
<% end %>

Сгенерированный HTML для этого будет:

<form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
  <input name='_method' type='hidden' value='patch' />
  ...
</form>

Удаление скрытых id модели

Метод #form_for автоматически включает id модели в качестве скрытого поля в форме. Это используется для поддержания корреляции между данными формы и связанной моделью. Некоторые системы ORM не используют id вложенных моделей, поэтому в этом случае вы хотите иметь возможность отключить скрытый id.

В следующем примере модель Post имеет несколько комментариев, хранящихся в ней в NoSQL-базе данных, следовательно, для комментариев нет первичного ключа.

Пример:

<%= form_for(@post) do |f| %>
  <%= f.fields_for(:comments, include_id: false) do |cf| %>
    ...
  <% end %>
<% end %>

Настраиваемые билдеры форм

Вы также можете создавать формы, используя настраиваемый класс FormBuilder. Подклассируйте FormBuilder и переопределите или определите несколько дополнительных помощников, а затем используйте свой настраиваемый билдер. Например, давайте представим, что вы создали помощник для автоматического добавления меток к элементам ввода формы.

<%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
  <%= f.text_field :first_name %>
  <%= f.text_field :last_name %>
  <%= f.text_area :biography %>
  <%= f.check_box :admin %>
  <%= f.submit %>
<% end %>

В этом случае, если вы используете это:

<%= render f %>

Отображаемая шаблон — people/_labelling_form, а локальная переменная, ссылающаяся на билдер формы, называется labelling_form.

Настраиваемый класс FormBuilder автоматически объединяется с параметрами вложенного вызова #fields_for, если он не задан явно.

Во многих случаях вы захотите обернуть всё вышесказанное в другой помощник, поэтому вы можете сделать что-то вроде следующего:

def labelled_form_for(record_or_name_or_array, *args, &block)
  options = args.extract_options!
  form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
end

Если вам не нужно привязывать форму к экземпляру модели, ознакомьтесь с ActionView::Helpers::FormTagHelper#form_tag.

Форма для внешних ресурсов

Когда вы создаёте формы для внешних ресурсов, иногда вам нужно установить маркер аутентификации или просто отобразить форму без него, например, при отправке данных на платежную платформу, где количество и типы полей могут быть ограничены.

Для установки маркера аутентификации вам нужно передать параметр :authenticity_token

<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
  ...
<% end %>

Если вы не хотите, чтобы поле маркера аутентификации отображалось вообще, просто передайте false:

<%= form_for @invoice, url: external_url, authenticity_token: false do |f|
  ...
<% end %>
hidden_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 825
def hidden_field(object_name, method, options = {})
  Tags::HiddenField.new(object_name, method, self, options).render
end

Возвращает скрытый тег ввода, настроенный для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object). Дополнительные параметры тега ввода могут быть переданы в виде хэша с options. Эти параметры будут добавлены в HTML как атрибут HTML-элемента, как показано в примере.

Примеры

hidden_field(:signup, :pass_confirm)
# => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />

hidden_field(:post, :tag_list)
# => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />

hidden_field(:user, :token)
# => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
label(object_name, method, content_or_options = nil, options = nil, &block) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 765
def label(object_name, method, content_or_options = nil, options = nil, &block)
  Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
end

Возвращает тег метки, настроенный для подписи поля ввода для указанного атрибута (идентифицированного по method) объекта, назначенного шаблону (идентифицированного по object). Текст метки по умолчанию — имя атрибута, если нет перевода в текущем локали I18n (через helpers.label.<modelname>.<attribute>) или если вы его явно не укажете. Дополнительные параметры тега метки могут быть переданы в виде хэша с options. Эти параметры будут добавлены в HTML как атрибут HTML-элемента, как показано в примере, за исключением параметра :value, который предназначен для меток для тегов #radio_button (где значение используется в ID тега ввода).

Примеры

label(:post, :title)
# => <label for="post_title">Title</label>

Вы можете локализовать метки, основываясь на имени модели и имени атрибута. Например, вы можете определить следующее в своём локализованном файле (например, en.yml)

helpers:
  label:
    post:
      body: "Write your entire text here"

Что приведет к

label(:post, :body)
# => <label for="post_body">Write your entire text here</label>

Локализация также может основываться только на переводе имени атрибута (если вы используете ActiveRecord):

activerecord:
  attributes:
    post:
      cost: "Total cost"

label(:post, :cost)
# => <label for="post_cost">Total cost</label>

label(:post, :title, "A short title")
# => <label for="post_title">A short title</label>

label(:post, :title, "A short title", class: "title_label")
# => <label for="post_title" class="title_label">A short title</label>

label(:post, :privacy, "Public Post", value: "public")
# => <label for="post_privacy_public">Public Post</label>

label(:post, :terms) do
  'Accept <a href="/terms">Terms</a>.'.html_safe
end
# => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
month_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1142
def month_field(object_name, method, options = {})
  Tags::MonthField.new(object_name, method, self, options).render
end

Возвращает #text_field типа “месяц”.

month_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="month" />

Значение по умолчанию генерируется путём вызова strftime с “%Y-%m” для значения объекта, что делает его поведением, соответствующим экземплярам DateTime и ActiveSupport::TimeWithZone.

@user.born_on = Date.new(1984, 1, 27)
month_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
number_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1185
def number_field(object_name, method, options = {})
  Tags::NumberField.new(object_name, method, self, options).render
end

Возвращает тег ввода типа “число”.

Параметры

  • Принимает те же параметры, что и number_field_tag

password_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 807
def password_field(object_name, method, options = {})
  Tags::PasswordField.new(object_name, method, self, options).render
end

Возвращает тег ввода типа “пароль”, настроенный для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object). Дополнительные параметры тега ввода могут быть переданы в виде хэша с options. Эти параметры будут добавлены в HTML как атрибут HTML-элемента, как показано в примере. По соображениям безопасности поле по умолчанию пустое; передайте значение через options, если это не нужно.

Примеры

password_field(:login, :pass, size: 20)
# => <input type="password" id="login_pass" name="login[pass]" size="20" />

password_field(:account, :secret, class: "form_input", value: @account.secret)
# => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />

password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }")
# => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/>

password_field(:account, :pin, size: 20, class: 'form_input')
# => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
phone_field(object_name, method, options = {})

Псевдоним для #telephone_field

Псевдоним для: telephone_field
radio_button(object_name, method, tag_value, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 966
def radio_button(object_name, method, tag_value, options = {})
  Tags::RadioButton.new(object_name, method, self, tag_value, options).render
end

Возвращает тег радиокнопки для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object). Если текущее значение method равно tag_value, радиокнопка будет отмечена.

Чтобы принудительно отметить радиокнопку, передайте checked: true в хэш options. Вы также можете передать HTML-опции.

# Let's say that @post.category returns "rails":
radio_button("post", "category", "rails")
radio_button("post", "category", "java")
# => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
#    <input type="radio" id="post_category_java" name="post[category]" value="java" />

radio_button("user", "receive_newsletter", "yes")
radio_button("user", "receive_newsletter", "no")
# => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
#    <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
range_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1193
def range_field(object_name, method, options = {})
  Tags::RangeField.new(object_name, method, self, options).render
end

Возвращает тег ввода типа “диапазон”.

Параметры

  • Принимает те же параметры, что и range_field_tag

search_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 997
def search_field(object_name, method, options = {})
  Tags::SearchField.new(object_name, method, self, options).render
end

Возвращает элемент ввода типа “поиск” для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object_name). Элементы ввода типа “поиск” могут быть стилизованы по-разному различными браузерами.

search_field(:user, :name)
# => <input id="user_name" name="user[name]" type="search" />
search_field(:user, :name, autosave: false)
# => <input autosave="false" id="user_name" name="user[name]" type="search" />
search_field(:user, :name, results: 3)
# => <input id="user_name" name="user[name]" results="3" type="search" />
#  Assume request.host returns "www.example.com"
search_field(:user, :name, autosave: true)
# => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
search_field(:user, :name, onsearch: true)
# => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
search_field(:user, :name, autosave: false, onsearch: true)
# => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
search_field(:user, :name, autosave: true, onsearch: true)
# => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
telephone_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1006
def telephone_field(object_name, method, options = {})
  Tags::TelField.new(object_name, method, self, options).render
end

Возвращает #text_field типа “телефон”.

telephone_field("user", "phone")
# => <input id="user_phone" name="user[phone]" type="tel" />
Также алиас для: phone_field
text_area(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 885
def text_area(object_name, method, options = {})
  Tags::TextArea.new(object_name, method, self, options).render
end

Возвращает теги открытия и закрытия textarea, настроенные для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object). Дополнительные параметры тега ввода могут быть переданы в виде хэша с options.

Примеры

text_area(:post, :body, cols: 20, rows: 40)
# => <textarea cols="20" rows="40" id="post_body" name="post[body]">
#      #{@post.body}
#    </textarea>

text_area(:comment, :text, size: "20x30")
# => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
#      #{@comment.text}
#    </textarea>

text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
# => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
#      #{@application.notes}
#    </textarea>

text_area(:entry, :body, size: "20x20", disabled: 'disabled')
# => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
#      #{@entry.body}
#    </textarea>
text_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 786
def text_field(object_name, method, options = {})
  Tags::TextField.new(object_name, method, self, options).render
end

Возвращает тег ввода типа “текст”, настроенный для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированного по object). Дополнительные параметры тега ввода могут быть переданы в виде хэша с options. Эти параметры будут добавлены в HTML как атрибут HTML-элемента, как показано в примере.

Примеры

text_field(:post, :title, size: 20)
# => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />

text_field(:post, :title, class: "create_input")
# => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />

text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
# => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>

text_field(:snippet, :code, size: 20, class: 'code_input')
# => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
time_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1067
def time_field(object_name, method, options = {})
  Tags::TimeField.new(object_name, method, self, options).render
end

Возвращает #text_field типа “время”.

Значение по умолчанию генерируется путём вызова strftime с “%T.%L” для значения объекта. Возможна переопределние с помощью параметра “значение”.

Параметры

  • Принимает те же параметры, что и time_field_tag

Пример

time_field("task", "started_at")
# => <input id="task_started_at" name="task[started_at]" type="time" />

Вы можете создать значения для атрибутов “min” и “max”, передав экземпляры Date или Time в хэш параметров.

time_field("task", "started_at", min: Time.now)
# => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />

В качестве альтернативы, вы можете передать строку, отформатированную как время ISO8601, в качестве значений для “min” и “max”.

time_field("task", "started_at", min: "01:00:00")
# => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />
url_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1168
def url_field(object_name, method, options = {})
  Tags::UrlField.new(object_name, method, self, options).render
end

Возвращает #text_field типа “URL”.

url_field("user", "homepage")
# => <input id="user_homepage" name="user[homepage]" type="url" />
week_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1159
def week_field(object_name, method, options = {})
  Tags::WeekField.new(object_name, method, self, options).render
end

Возвращает #text_field типа “week”.

week_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="week" />

Значение по умолчанию генерируется путём попытки вызова strftime с “%Y-W%W” на значении объекта, что обеспечивает ожидаемое поведение для экземпляров DateTime и ActiveSupport::TimeWithZone.

@user.born_on = Date.new(1984, 5, 12)
week_field("user", "born_on")
# => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />

© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API