Spec-Zone.ru › Ruby on Rails 5.0

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

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

Помощники для форм разработаны, чтобы сделать работу с ресурсами намного проще по сравнению с использованием обычного 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.new:

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

Интересно, что тот же код представления в предыдущем примере можно использовать для редактирования человека. Если @person — это существующая запись с именем «Иван Иванов» и 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 951
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 возвращает целое число, и если это целое число больше нуля, то checkbox отмечен. Дополнительные параметры тега ввода могут быть переданы в виде хэша с 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 980
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 1044
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" />

Значение по умолчанию генерируется путем попытки вызова strftime с «%Y-%m-%d» на значении объекта, что обеспечивает ожидаемое поведение для экземпляров 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 1102
def datetime_field(object_name, method, options = {})
  Tags::DatetimeLocalField.new(object_name, method, self, options).render
end

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

datetime_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_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_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 datetime как значения для «min» и «max».

datetime_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" />
Также алиас: datetime_local_field
datetime_local_field(object_name, method, options = {})
Псевдоним для: datetime_field
email_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 1156
def email_field(object_name, method, options = {})
  Tags::EmailField.new(object_name, method, self, options).render
end

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

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 718
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 как коллекцию, и правильно устанавливать индексы в разметке формы.

Если 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 автоматически сгенерирует скрытое поле для хранения ID записи. В некоторых случаях скрытое поле не нужно, и вы можете передать include_id: false для предотвращения автоматического рендеринга #fields_for.

file_field(object_name, method, options = {}) Показать исходный код
# File actionview/lib/action_view/helpers/form_helper.rb, line 863
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="multiple" />

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 428
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 добавляется для имитации глагола над отправкой.

  • :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 будет содержать предназначенный глагол, чтобы сервер мог его интерпретировать.

Unobtrusive JavaScript

Указание:

remote: true

в хэше параметров создаёт форму, которая позволит драйверам Unobtrusive 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 автоматически включает в форму скрытое поле с идентификатором модели. Это используется для сохранения корреляции между данными формы и связанной моделью. Некоторые ORM-системы не используют идентификаторы вложенных моделей, поэтому в этом случае вам нужно отключить скрытый идентификатор.

В следующем примере модель 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 831
def hidden_field(object_name, method, options = {})
  Tags::HiddenField.new(object_name, method, self, options).render
end

Возвращает скрытый тег input, настроенный для доступа к указанному атрибуту (идентифицированному по method) объекта, назначенного шаблону (идентифицированному по object). Дополнительные параметры тега input могут быть переданы в виде хэша с 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 771
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

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

Примеры

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
  raw('Accept <a href="/terms">Terms</a>.')
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 1121
def month_field(object_name, method, options = {})
  Tags::MonthField.new(object_name, method, self, options).render
end

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

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 1164
def number_field(object_name, method, options = {})
  Tags::NumberField.new(object_name, method, self, options).render
end

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

Параметры

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

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

Возвращает тег ввода типа “password”, настроенный для доступа к указанному атрибуту (идентифицированному по 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 972
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 1172
def range_field(object_name, method, options = {})
  Tags::RangeField.new(object_name, method, self, options).render
end

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

Параметры

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

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

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

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 1012
def telephone_field(object_name, method, options = {})
  Tags::TelField.new(object_name, method, self, options).render
end

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

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 891
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 792
def text_field(object_name, method, options = {})
  Tags::TextField.new(object_name, method, self, options).render
end

Возвращает тег ввода типа “text”, настроенный для доступа к указанному атрибуту (идентифицированному по 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 1073
def time_field(object_name, method, options = {})
  Tags::TimeField.new(object_name, method, self, options).render
end

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

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

Параметры

  • Поддерживает те же параметры, что и 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 1147
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 1138
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