модуль ActionView::Helpers::FormHelper
Помощники для форм предназначены для того, чтобы работа с ресурсами стала намного проще по сравнению с использованием обычного 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">
<div style="display:none">
<input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
</div>
<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 — это существующий ресурс с именем «Иван Иванов» и ID 256, то вышеприведенный код вернёт:
<form action="/people/256" class="edit_person" id="edit_person_256" method="post">
<div style="display:none">
<input name="_method" type="hidden" value="patch" />
<input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
</div>
<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
Вот как обычно работают с ресурсами.
Публичные методы экземпляров
Возвращает тег флажка, настроенный для доступа к указанному атрибуту (определяемому 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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 929
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 Возвращает поле типа «цвет» #text_field.
color_field("car", "color")
# => <input id="car_color" name="car[color]" type="color" value="#000000" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 958
def color_field(object_name, method, options = {})
Tags::ColorField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1010
def date_field(object_name, method, options = {})
Tags::DateField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1044
def datetime_field(object_name, method, options = {})
Tags::DatetimeField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1061
def datetime_local_field(object_name, method, options = {})
Tags::DatetimeLocalField.new(object_name, method, self, options).render
end Возвращает поле типа «email» #text_field.
email_field("user", "address")
# => <input id="user_address" name="user[address]" type="email" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1113
def email_field(object_name, method, options = {})
Tags::EmailField.new(object_name, method, self, options).render
end Создаёт область видимости вокруг конкретного объекта модели, подобно #form_for, но не создаёт сами теги формы. Это делает #fields_for подходящим для указания дополнительных объектов модели в одной форме.
Хотя использование и назначение field_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 %>
<%= f.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 и отвечает на метод записи 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 actionview/lib/action_view/helpers/form_helper.rb, line 697
def fields_for(record_name, record_object = nil, options = {}, &block)
builder = instantiate_builder(record_name, record_object, options)
capture(builder, &block)
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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 841
def file_field(object_name, method, options = {})
Tags::FileField.new(object_name, method, self, options).render
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. -
: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'>
<div style='display:none'>
<input name='_method' type='hidden' value='patch' />
</div>
...
</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'>
<div style='display:none'>
<input name='_method' type='hidden' value='patch' />
</div>
...
</form> Удаление скрытых идентификаторов модели
Метод #form_for автоматически включает идентификатор модели в качестве скрытого поля в форме. Это используется для поддержания корреляции между данными формы и связанной с ней моделью. Некоторые 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 %>
# File actionview/lib/action_view/helpers/form_helper.rb, line 413
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[:authenticity_token] = options.delete(:authenticity_token)
builder = instantiate_builder(object_name, object, options)
output = capture(builder, &block)
html_options[:multipart] ||= builder.multipart?
form_tag(options[:url] || {}, html_options) { output }
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}" />
Возвращает тег метки, настроенный для подписи поля ввода для указанного атрибута (идентифицированного как method) объекта, присвоенного шаблону (идентифицированного как object). Текст метки по умолчанию будет именем атрибута, если не найдено соответствие в текущем локали I18n (через helpers.label.<modelname>.<attribute>) или если вы его явно не укажете. Дополнительные параметры тега метки можно передать как хэш с 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
'Accept <a href="/terms">Terms</a>.'.html_safe
end # File actionview/lib/action_view/helpers/form_helper.rb, line 749 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
Возвращает #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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1078
def month_field(object_name, method, options = {})
Tags::MonthField.new(object_name, method, self, options).render
end Возвращает тег ввода типа “number”.
Параметры
-
Принимает те же параметры, что и number_field_tag
# File actionview/lib/action_view/helpers/form_helper.rb, line 1121
def number_field(object_name, method, options = {})
Tags::NumberField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 791
def password_field(object_name, method, options = {})
Tags::PasswordField.new(object_name, method, self, options).render
end псевдоним для #telephone_field
Возвращает тег радиокнопки для доступа к указанному атрибуту (идентифицированному как 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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 950
def radio_button(object_name, method, tag_value, options = {})
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
end Возвращает тег ввода типа “range”.
Параметры
-
Принимает те же параметры, что и range_field_tag
# File actionview/lib/action_view/helpers/form_helper.rb, line 1129
def range_field(object_name, method, options = {})
Tags::RangeField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 981
def search_field(object_name, method, options = {})
Tags::SearchField.new(object_name, method, self, options).render
end Возвращает #text_field типа “tel”.
telephone_field("user", "phone")
# => <input id="user_phone" name="user[phone]" type="tel" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 990
def telephone_field(object_name, method, options = {})
Tags::TelField.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>
# File actionview/lib/action_view/helpers/form_helper.rb, line 869
def text_area(object_name, method, options = {})
Tags::TextArea.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 770
def text_field(object_name, method, options = {})
Tags::TextField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1027
def time_field(object_name, method, options = {})
Tags::TimeField.new(object_name, method, self, options).render
end Возвращает #text_field типа “url”.
url_field("user", "homepage")
# => <input id="user_homepage" name="user[homepage]" type="url" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1104
def url_field(object_name, method, options = {})
Tags::UrlField.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" />
# File actionview/lib/action_view/helpers/form_helper.rb, line 1095
def week_field(object_name, method, options = {})
Tags::WeekField.new(object_name, method, self, options).render
end
© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.