diff --git a/source/form_helpers.md b/source/form_helpers.md index 0e9c041a..a4160d9c 100644 --- a/source/form_helpers.md +++ b/source/form_helpers.md @@ -18,27 +18,24 @@ NOTE: Это руководство не является подробной д Разбираемся с простыми формами ------------------------------ -Самый простой хелпер форм - это `form_tag`. +Главный хелпер форм - это `form_with`. ```erb -<%= form_tag do %> +<%= form_with do %> Содержимое формы <% end %> ``` -При подобном вызове без аргументов, он создает тег `
`, который при отправке сформирует POST-запрос на текущую страницу. Например, предположим текущая страница `/home/index`, тогда сгенерированный HTML будет выглядеть следующим образом (некоторые разрывы строчек добавлены для читаемости): +При подобном вызове без аргументов, он создает тег формы, который при отправке сформирует POST-запрос на текущую страницу. Например, предположим текущая страница является домашней, тогда сгенерированный HTML будет выглядеть следующим образом (некоторые разрывы строчек добавлены для читаемости): ```html - - + Содержимое формы
``` -Можно увидеть, что HTML содержит элемент `input` с типом `hidden`. Этот `input` важен, поскольку без него форма не может быть успешно отправлена. Скрытый элемент input с атрибутом name `utf8` обеспечивает, чтобы браузер правильно относился к кодировке вашей формы, он генерируется для всех форм, у которых action равен "GET" или "POST". - -Второй элемент input с именем `authenticity_token` является особенностью безопасности Rails, называемой **защитой от межсайтовой подделки запроса**, и хелперы форм генерируют его для каждой формы, у которых action не "GET" (при условии, что эта особенность безопасности включена). Подробнее об этом можно прочитать в руководстве [Безопасность приложений на Rails](/ruby-on-rails-security-guide#cross-site-request-forgery-csrf). +Можно увидеть, что HTML содержит элемент `input` с типом `hidden`. Этот `input` важен, поскольку без него форма, у которой action не "GET", не может быть успешно отправлена. Скрытый элемент input с именем `authenticity_token` является особенностью безопасности Rails, называемой **защитой от межсайтовой подделки запроса**, и хелперы форм генерируют его для каждой формы, у которых action не "GET" (при условии, что эта особенность безопасности включена). Подробнее об этом можно прочитать в руководстве [Безопасность приложений на Rails](/ruby-on-rails-security-guide#cross-site-request-forgery-csrf). ### Характерная форма поиска @@ -49,10 +46,10 @@ NOTE: Это руководство не является подробной д * элемент поля ввода текста и * элемент отправки. -Чтобы создать эту форму, используем, соответственно, `form_tag`, `label_tag`, `text_field_tag` и `submit_tag`. Как здесь: +Чтобы создать эту форму, используем, соответственно, `form_with`, `label_tag`, `text_field_tag` и `submit_tag`. Как здесь: ```erb -<%= form_tag("/search", method: "get") do %> +<%= form_with(url: "/search", method: "get") do %> <%= label_tag(:q, "Search for:") %> <%= text_field_tag(:q) %> <%= submit_tag("Search") %> @@ -62,43 +59,24 @@ NOTE: Это руководство не является подробной д Это сгенерирует следующий HTML: ```html -
- + - +
``` -TIP: Для каждого поля ввода формы генерируется атрибут ID из его имени (`"q"` в примере). Эти ID могут быть очень полезны для стилизации CSS или управления элементами форм с помощью JavaScript. - -Кроме `text_field_tag` и `submit_tag` имеется похожий хелпер для _каждого_ элемента управления формой в HTML. - -IMPORTANT: Всегда используйте "GET" как метод для форм поиска. Это позволяет пользователям добавлять в закладки определенный поиск и потом возвращаться к нему. В более общем смысле Rails призывает вас использовать правильный метод HTTP для экшна. - -### Несколько хэшей в вызовах хелпера формы +TIP: Передача `url: my_specified_path` в `form_with` сообщает форме, куда осуществлять запрос. Однако, как объясняется ниже, в форму также можно передавать объекты ActiveRecord. -Хелпер `form_tag` принимает 2 аргумента: путь для экшна и хэш опций. Этот хэш определяет метод отправки формы и опции HTML, такие как класс элемента form. - -Как в случае с хелпером `link_to`, аргумент пути не должен быть строкой. Это может быть хэш параметров URL, распознаваемый механизмом маршрутизации Rails, преобразующим хэш в валидный URL. Однако, если оба аргумента для `form_tag` хэши, можно легко получить проблему, если захотите определить оба. Например, вы напишите так: - -```ruby -form_tag(controller: "people", action: "search", method: "get", class: "nifty_form") -# => '
' -``` - -Здесь методы `method` и `class` присоединены к строке запроса сгенерированного URL, поскольку, хотя вы и хотели передать два хэша, фактически вы передали один. Чтобы сообщить об этом Ruby, следует выделить первый хэш (или оба хэша) фигурными скобками. Это сгенерирует HTML, который вы ожидаете: +TIP: Для каждого поля ввода формы генерируется атрибут ID из его имени (`"q"` в примере). Эти ID могут быть очень полезны для стилизации CSS или управления элементами форм с помощью JavaScript. -```ruby -form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form") -# => '' -``` +IMPORTANT: Используйте "GET" как метод для форм поиска. Это позволяет пользователям добавлять в закладки определенный поиск и потом возвращаться к нему. В более общем смысле Rails призывает вас использовать правильный метод HTTP для экшна. ### Хелперы для генерации элементов формы Rails предоставляет ряд хелперов для генерации элементов формы, таких как чекбоксы, текстовые поля, радиокнопки и так далее. Эти простые хелперы с именами, оканчивающимися на `_tag` (такие как `text_field_tag` и `check_box_tag`), генерируют только один элемент ``. Первый параметр у них это всегда имя поля ввода. Когда форма будет отправлена, имя будет передано вместе с данными формы, и, в свою очередь, помещено в `params` в контроллере со значением, введенным пользователем для этого поля. Например, если форма содержит `<%= text_field_tag(:query) %>`, то значение этого поля можно получить в контроллере с помощью `params[:query]`. -При именовании полей ввода Rails использует определенные соглашения, делающие возможным отправлять параметры с нескалярными величинами, такими как массивы и хэши, которые также будут доступны в `params`. Подробнее об этом можно прочесть в разделе [про именование параметров](#understanding-parameter-naming-conventions). Для подробностей по точному использованию этих хелперов, обратитесь к [документации по API](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html). +При именовании полей ввода Rails использует определенные соглашения, делающие возможным отправлять параметры с нескалярными величинами, такими как массивы и хэши, которые также будут доступны в `params`. Подробнее об этом можно прочесть в разделе [разделе про именование параметров](#understanding-parameter-naming-conventions). Для подробностей по точному использованию этих хелперов, обратитесь к [документации по API](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html). #### Чекбоксы @@ -130,7 +108,7 @@ Rails предоставляет ряд хелперов для генераци <%= radio_button_tag(:age, "child") %> <%= label_tag(:age_child, "I am younger than 21") %> <%= radio_button_tag(:age, "adult") %> -<%= label_tag(:age_adult, "I'm over 21") %> +<%= label_tag(:age_adult, "I am over 21") %> ``` Результат: @@ -139,7 +117,7 @@ Rails предоставляет ряд хелперов для генераци - + ``` Как и у `check_box_tag`, второй параметр для `radio_button_tag` - это значение поля ввода. Так как эти две радиокнопки имеют одинаковое имя (`age`), пользователь может выбрать одну, и `params[:age]` будет содержать или `"child"`, или `"adult"`. @@ -210,10 +188,10 @@ TIP: Если используются поля для ввода пароля ( выдаст подобный результат ```erb - + ``` -После подтверждения формы, значение, введенное пользователем, будет храниться в `params[:person][:name]`. Хэш `params[:person]` подходит для передачи в `Person.new` или, если `@person` - это экземпляр Person, в `@person.update`. Хотя имя атрибута - наиболее распространен как второй параметр для этих хелперов, он не является обязательным. В вышеприведенном примере, до тех пор пока объекты person имеют методы `name` и `name=`, Rails будет удовлетворен. +После подтверждения формы, значение, введенное пользователем, будет храниться в `params[:person][:name]`. WARNING: Необходимо передавать имя переменной экземпляра, т.е. `:person` или `"person"`, а не фактический экземпляр объекта вашей модели. @@ -221,7 +199,7 @@ Rails предоставляет хелперы для отображения о ### Привязывание формы к объекту -Хотя комфортность несколько улучшилась, она еще далека от совершенства. Если у `Person` много атрибутов для редактирования, тогда мы должны повторить имя редактируемого объекта много раз. То, что мы хотим сделать, - это как-то привязать форму к объекту модели, что как раз осуществляется с помощью `form_for`. +Хотя комфортность несколько улучшилась, она еще далека от совершенства. Если у `Person` много атрибутов для редактирования, тогда мы должны повторить имя редактируемого объекта много раз. То, что мы хотим сделать, - это как-то привязать форму к объекту модели, что как раз осуществляется с помощью `form_with` с параметром `:model`. Допустим у нас есть контроллер для работы со статьями `app/controllers/articles_controller.rb`: @@ -231,10 +209,10 @@ def new end ``` -Соответствующая вьюха `app/views/articles/new.html.erb`, использующая `form_for`, выглядит так: +Соответствующая вьюха `app/views/articles/new.html.erb`, использующая `form_with`, выглядит так: ```erb -<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %> +<%= form_with model: @article, class: "nifty_form" do |f| %> <%= f.text_field :title %> <%= f.text_area :body, size: "60x12" %> <%= f.submit "Create" %> @@ -244,15 +222,15 @@ end Следует отметить несколько вещей: * `@article` - это фактический объект, который редактируется. -* Здесь есть одиночный хэш опций. Опции маршрутизации передаются в хэше `:url`, опции HTML передаются в хэше `:html`. Также для формы можно предоставить опцию `:namespace`, чтобы быть уверенным в уникальности атрибутов id элементов формы. Генерируемые для HTML id будут начинаться с префикса, заданного атрибутом namespace, плюс подчеркивание. -* Метод `form_for` предоставляет объект **form builder** (переменная `f`). +* Здесь есть одиночный хэш опций. Опции HTML передаются в хэше `:html`. Также для формы можно предоставить опцию `:namespace`, чтобы быть уверенным в уникальности атрибутов id элементов формы. Генерируемые для HTML id будут начинаться с префикса, заданного атрибутом пространства имен, плюс подчеркивание. +* Метод `form_with` предоставляет объект **form builder** (переменная `f`). +* Если хотите направить запрос формы на определенный url, вместо этого следует использовать `form_with url: my_nifty_url_path`. Подробнее об опциях, которые принимает `form_with`, можно узнать в [документации API](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with). * Методы создания элементов управления формой вызываются *для* объекта form builder `f`. Итоговый HTML: ```html - - + @@ -260,16 +238,18 @@ end
``` -Имя, переданное в `form_for`, контролирует ключ, используемый в `params` для доступа к значениям формы. В примере имя `article`, и поэтому все поля ввода имеют имена формы `article[attribute_name]`. Соответственно, в экшне `create` хэш `params[:article]` имеет ключи `:title` и `:body`. Подробнее о значении имен полей ввода можно прочитать в разделе [про именование параметров](#understanding-parameter-naming-conventions). +Объект, переданный как `:model` в `form_with`, контролирует ключ, используемый в `params` для доступа к значениям формы. В примере имя `article`, и поэтому все поля ввода имеют имена формы `article[attribute_name]`. Соответственно, в экшне `create` хэш `params[:article]` имеет ключи `:title` и `:body`. Подробнее о значении имен полей ввода можно прочитать в разделе [про именование параметров](#understanding-parameter-naming-conventions) этого руководства. + +TIP: По соглашению, поля ввода будут отражать атрибуты модели. Однако, это необязательно! Если имеется иная необходимая информация, ее можно включить в форму, также как атрибут, и она будет доступна как `params[:article][:my_nifty_non_attribute_input]`. Методы хелпера, вызываемые из form builder, идентичны хелперам объекта модели, за исключением того, что не нужно указывать, какой объект будет редактироваться, так как это уже регулируется в form builder. Можно создать подобное привязывание без фактического создания тега `
` с помощью хелпера `fields_for`. Это полезно для редактирования дополнительных объектов модели в той же форме. Например, если имеется модель `Person` со связанной моделью `ContactDetail`, можно создать форму для создания обеих моделей подобным образом: ```erb -<%= form_for @person, url: {action: "create"} do |person_form| %> +<%= form_with model: @person do |person_form| %> <%= person_form.text_field :name %> - <%= fields_for @person.contact_detail do |contact_detail_form| %> + <%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %> <%= contact_detail_form.text_field :phone_number %> <% end %> <% end %> @@ -278,15 +258,14 @@ end которая выдаст такой результат: ```html - - +
``` -Объект, предоставляемый `fields_for` - это form builder, подобный тому, который предоставляется `form_for` (фактически `form_for` внутри себя вызывает `fields_for`). +Объект, предоставляемый `fields_for` - это form builder, подобный тому, который предоставляется `form_with`. ### Положитесь на идентификацию записи @@ -296,62 +275,59 @@ end resources :articles ``` -TIP: Объявление ресурса имеет несколько побочных эффектов. Смотрите [Роутинг в Rails](/rails-routing) для получения более подробной информации по настройке и использованию ресурсов. +TIP: Объявление ресурса имеет несколько побочных эффектов. Смотрите руководство [Роутинг в Rails](/rails-routing) для получения более подробной информации по настройке и использованию ресурсов. -Когда работаем с ресурсами RESTful, вызовы `form_for` становятся значительно проще, если они основываются на **идентификации записи**. Вкратце, вы должны всего лишь передать экземпляр модели и позволить Rails выяснить имя модели и остальное: +Когда работаем с ресурсами RESTful, вызовы `form_with` становятся значительно проще, если они основываются на **идентификации записи**. Вкратце, вы должны всего лишь передать экземпляр модели и позволить Rails выяснить имя модели и остальное: ```ruby ## Создание новой статьи # длинный стиль: -form_for(@article, url: articles_path) -# то же самое, короткий стиль (используется идентификация записи): -form_for(@article) +form_with(model: @article, url: articles_path) +# короткий стиль: +form_with(model: @article) ## Редактирование существующей статьи # длинный стиль: -form_for(@article, url: article_path(@article), html: {method: "patch"}) +form_with(model: @article, url: article_path(@article), method: "patch") # короткий стиль: -form_for(@article) +form_with(model: @article) ``` -Отметьте, что вызов короткого стиля `form_for` является практичным, независимо от того, запись новая или уже существует. Идентификация записи достаточно сообразительная, чтобы выяснить, новая ли запись, запрашивая `record.new_record?`. Она также выбирает правильный путь для подтверждения и имя, основанное на классе объекта. - -Rails также автоматически установит надлежащие атрибуты формы `class` и `id`: форма, создающая article, будет иметь `id` и `class` равные `new_article`. Если редактируется статья с id=23, то `class` будет установлен как `edit_article`, и id - как `edit_article_23`. Эти атрибуты будут опускаться для краткости далее в остальной части этого руководства. +Отметьте, что вызов короткого стиля `form_with` является идентичным, независимо от того, запись новая или уже существует. Идентификация записи достаточно сообразительная, чтобы выяснить, новая ли запись, запрашивая `record.new_record?`. Она также выбирает правильный путь для подтверждения и имя, основанное на классе объекта. -WARNING: Когда используется STI (наследование с единой таблицей) с вашими моделями, нельзя полагаться на идентификацию записей подкласса, если только их родительский класс определен ресурсом. Необходимо явно указывать имя модели, `:url` и `:method`. +WARNING: Когда используется STI (наследование с единой таблицей) с вашими моделями, нельзя полагаться на идентификацию записей подкласса, если только их родительский класс определен ресурсом. Необходимо явно указывать `:url` и `:scope` (имя модели). #### Работаем с пространствами имен -Если создать пространство имен для маршрутов, `form_for` также можно изящно сократить. Если в приложении есть пространство имен admin, то +Если создать пространство имен для маршрутов, `form_with` также можно изящно сократить. Если в приложении есть пространство имен admin, то ```ruby -form_for [:admin, @article] +form_with model: [:admin, @article] ``` создаст форму, которая передается `ArticlesController` в пространстве имен admin (передача в `admin_article_path(@article)` в случае с обновлением). Если у вас несколько уровней пространства имен, тогда синтаксис подобный: ```ruby -form_for [:admin, :management, @article] +form_with model: [:admin, :management, @article] ``` -Более подробно о системе маршрутизации Rails и связанным соглашениям смотрите [Роутинг в Rails](/rails-routing). +Более подробно о системе маршрутизации Rails и связанным соглашениям смотрите руководство [Роутинг в Rails](/rails-routing). ### Как формы работают с методами PATCH, PUT или DELETE? -Фреймворк Rails поддерживает стиль RESTful в ваших приложениях, что подразумевает частое использование запросов "PATCH" и "DELETE" (помимо "GET" и "POST"). Однако, большинство браузеров _не поддерживают_ методы, отличные от "GET" и "POST", когда дело доходит до подтверждения форм. +Фреймворк Rails поддерживает стиль RESTful в ваших приложениях, что подразумевает частое использование запросов "PATCH", "PUT" и "DELETE" (помимо "GET" и "POST"). Однако, большинство браузеров _не поддерживают_ методы, отличные от "GET" и "POST", когда дело доходит до подтверждения форм. Rails работает с этой проблемой, эмулируя другие методы с помощью POST со скрытым полем, названным `"_method"`, который установлен для отображение желаемого метода: ```ruby -form_tag(search_path, method: "patch") +form_with(url: search_path, method: "patch") ``` -результат: +Результат: ```html -
+ - ...
@@ -359,6 +335,8 @@ form_tag(search_path, method: "patch") При парсинге данных, отправленных с помощью POST, Rails принимает во внимание специальный параметр `_method` и ведет себя так, как будто бы в нем был определен этот метод HTTP ("PATCH" в этом примере). +IMPORTANT: Все формы с использованием `form_with` по умолчанию реализуют `remote: true`. Эти формы будут отправлять данные с помощью запроса XHR (Ajax). Чтобы это отключить, добавьте `local: true`. Подробности смотрите в руководстве [Работа с JavaScript в Rails](/working-with-javascript-in-rails#remote-elements). + Легкое создание списков выбора ------------------------------ @@ -370,8 +348,7 @@ form_tag(search_path, method: "patch") ``` @@ -382,19 +359,21 @@ form_tag(search_path, method: "patch") Наиболее простой хелпер - это `select_tag`, который, как следует из имени, просто сгенерирует тег `SELECT`, инкапсулирующий строку option (пунктов списка): ```erb -<%= select_tag(:city_id, '...') %> +<%= select_tag(:city_id, raw('')) %> ``` Это закладывает начало, но пока еще динамически не создает теги пунктов списка. Можно сгенерировать теги пунктов с помощью хелпера `options_for_select`: ```html+erb -<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %> +<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %> +``` -результат: +Результат: +```html -... + ``` Первый аргумент для `options_for_select` - это вложенный массив, в котором каждый элемент содержит два элемента: текст пункта списка (название города) и значение пункта списка (id города). Значение пункта списка - это то, что будет передано в контроллер. Часто бывает, что значение - это id соответствующего объекта базы данных, но это не всегда так. @@ -408,48 +387,61 @@ form_tag(search_path, method: "patch") `options_for_select` позволяет предварительно выбрать пункт списка, передав его значение. ```html+erb -<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %> +<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]], 2) %> +``` -результат: +Результат: +```html -... + ``` Всякий раз, когда Rails видит, что внутреннее значение сгенерированного пункта списка соответствует этому значению, он добавит атрибут `selected` к этому пункту. -WARNING: Когда отсутствует `:include_blank` или `:prompt`, `:include_blank` становится true, если атрибут `required` - true, отображаемый `size` - 1 и `multiple` не равен true. - С помощью хэшей можно добавить произвольные атрибуты в option: ```html+erb <%= options_for_select( [ ['Lisbon', 1, { 'data-size' => '2.8 million' }], - ['Madrid', 2, { 'data-size' => '3.2 million' }] + ['Madrid', 2, { 'data-size' => '3.2 million' }], + ['Berlin', 3, { 'data-size' => '3.4 million' }] ], 2 ) %> +``` -результат: +Результат: +```html -... + ``` -### Списки выбора для работы с моделями +### Списки выбора для работы с объектами модели -В большинстве случаев элементы управления формой будут связаны с определенной моделью базы данных, и, как вы, наверное, и ожидали, Rails предоставляет хелперы, предназначенные для этой цели. Как в случае с другими хелперами форм, когда работаете с моделями, суффикс `_tag` отбрасывается от `select_tag`: +В большинстве случаев элементы управления формой будут связаны с определенной моделью, и, как вы, наверное, и ожидали, Rails предоставляет хелперы, предназначенные для этой цели. Как в случае с другими хелперами форм, когда работаете с объектами модели, суффикс `_tag` отбрасывается от `select_tag`: + +Если ваш контроллер определил `@person`, и city_id этого person равен 2: ```ruby -# контроллер: @person = Person.new(city_id: 2) ``` ```erb -# вьюха: -<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %> ++<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %> +``` + +Создаст результат, подобный следующему + +```html + ``` Отметьте, что третий параметр - массив пунктов списка - имеет тот же самый тип аргумента, что мы передавали в `options_for_select`. Одно из преимуществ заключается в том, что не стоит беспокоиться об предварительном выборе правильного города, если пользователь уже выбрал его - Rails сделает это за вас, прочитав из атрибута `@person.city_id`. @@ -457,21 +449,26 @@ WARNING: Когда отсутствует `:include_blank` или `:prompt`, `: Как и в других хелперах, если хотите использовать хелпер `select` в form builder с областью видимостью объекта `@person`, синтаксис будет такой: ```erb -# select в form builder -<%= f.select(:city_id, ...) %> +<%= form_with model: @person do |person_form| %> + <%= person_form.select(:city_id, [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %> +<% end %> ``` В хелпер `select` также можно передать блок: ```erb -<%= f.select(:city_id) do %> - <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%> - <%= content_tag(:option, c.first, value: c.last) %> +<%= form_with model: @person do |person_form| %> + <%= person_form.select(:city_id) do %> + <% [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]].each do |c| %> + <%= content_tag(:option, c.first, value: c.last) %> + <% end %> <% end %> <% end %> ``` -WARNING: При использовании `select` (или подобных хелперов, таких как `collection_select`, `select_tag`), чтобы установить связь `belongs_to`, необходимо передать имя внешнего ключа (в примере выше `city_id`), а не само имя связи. Если указать `city` вместо `city_id`, Active Record вызовет ошибку в строчках `ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)`, когда будет передаваться хэш `params` в `Person.new` или `update`. Можно взглянуть на это по-другому, что хелперы форм только редактируют атрибуты. Также стоит побеспокоиться о возможных последствиях безопасности, если разрешить пользователям редактировать внешние ключи напрямую. +WARNING: При использовании `select`, или подобных хелперов, чтобы установить связь `belongs_to`, необходимо передать имя внешнего ключа (в примере выше `city_id`), а не само имя связи. + +WARNING: Когда отсутствует `:include_blank` или `:prompt`, `:include_blank` принудительно становится true, если атрибут `required` - true, отображаемый `size` - 1 и `multiple` не равен true. ### Теги пункта списка из коллекции произвольных объектов @@ -488,7 +485,7 @@ WARNING: При использовании `select` (или подобных х <%= options_from_collection_for_select(City.all, :id, :name) %> ``` -Как следует из имени хелпера, это генерирует только теги пункта списка. Для генерации работающего списка выбора, его необходимо использовать в сочетании с `select_tag`, как это делалось для `options_for_select`. Когда работаем с объектами модели, так же как `select` комбинирует `select_tag` и `options_for_select`, `collection_select` комбинирует `select_tag` с `options_from_collection_for_select`. +Как следует из имени хелпера, это генерирует только теги пункта списка. Для генерации работающего списка выбора, необходимо использовать `collection_select`. ```erb <%= collection_select(:person, :city_id, City.all, :id, :name) %> @@ -497,16 +494,16 @@ WARNING: При использовании `select` (или подобных х Как и с другими хелперами, если нужно использовать хелпер `collection_select` в связке с form builder, привязанным к объекту `@person`, синтаксис будет следующим: ```erb -<%= f.collection_select(:city_id, City.all, :id, :name) %> +<%= form_with model: @person do |person_form| %> + <%= person_form.collection_select(:city_id, City.all, :id, :name) %> +<% end %> ``` -Напомним, что `options_from_collection_for_select` в `collection_select` - то же самое, что и `options_for_select` в `select`. - -NOTE: Пары, переданные в `options_for_select` должны сперва иметь имя, затем id, однако для `options_from_collection_for_select` первый аргумент - это метод value, а второй аргумент - метод text. +NOTE: Пары, переданные в `options_for_select` должны сперва иметь текст, затем значение, однако для `options_from_collection_for_select` первый аргумент - это метод для значения, а второй аргумент - метод для текста. ### Выбор часового пояса и страны -Для управления поддержкой часовых поясов в Rails, можно спрашивать своих пользователей, в какой зоне они находятся. Это потребует сгенерировать пункты списка из списка предопределенных объектов TimeZone, используя `collection_select`, но можно просто использовать хелпер `time_zone_select`, который уже все это содержит: +Для управления поддержкой часовых поясов в Rails, можно спрашивать своих пользователей, в какой зоне они находятся. Это потребует сгенерировать пункты списка из списка предопределенных объектов [`ActiveSupport::TimeZone`](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html), используя `collection_select`, но можно просто использовать хелпер `time_zone_select`, который уже все это содержит: ```erb <%= time_zone_select(:person, :time_zone) %> @@ -514,7 +511,7 @@ NOTE: Пары, переданные в `options_for_select` должны спе Также есть хелпер `time_zone_options_for_select` для ручного (и поэтому гибко настраиваемого) способа осуществления этого. Читайте [документацию по API](http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-time_zone_options_for_select), чтобы узнать о доступных аргументах для этих двух методов. -В Rails _раньше_ был хелпер `country_select` для выбора стран, но сейчас он вынесен во внешний [плагин country_select](https://github.com/stefanpenner/country_select). При его использовании убедитесь, что включение или исключение определенных имен из списка может быть несколько спорным (это и послужило причиной извлечения этой функциональности из Rails). +В Rails _раньше_ был хелпер `country_select` для выбора стран, но сейчас он вынесен во внешний [плагин country_select](https://github.com/stefanpenner/country_select). Использование хелперов даты и времени ------------------------------------- @@ -537,9 +534,12 @@ NOTE: Пары, переданные в `options_for_select` должны спе выведет (с опущенными для краткости начальными значениями пунктов списка) ```html - - - + + + ``` Эти элементы ввода приведут к тому, что `params[:start_date]` будет хэшем с ключами `:year`, `:month`, `:day`. Чтобы получить фактический объект `Date`, `Time` или `DateTime`, необходимо извлечь эти значения и передать их в подходящий конструктор, например: @@ -561,9 +561,12 @@ Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, pa выдаст (с опущенными для краткости начальными значениями пунктов списка) ```html - - - + + + ``` что приведет к такому результату в хэше `params` @@ -580,8 +583,6 @@ Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, pa Как правило, следует использовать `date_select` при работе с объектами модели и `select_date` в иных случаях, например, таких как форма поиска, которая фильтрует результаты по дате. -NOTE: В большинстве случаев встроенные элементы подбора дат неуклюжи в использовании, так как не позволяют пользователю видеть отношения между датой и днями недели. - ### Индивидуальные компоненты Иногда необходимо отобразить лишь одиночный компонент даты, такой как год или месяц. Rails предоставляет ряд хелперов для этого, по одному для каждого компонента `select_year`, `select_month`, `select_day`, `select_hour`, `select_minute`, `select_second`. Эти хелперы достаточно простые. По умолчанию они сгенерируют поле ввода, названное по имени компонента времени (например, "year" для `select_year`, "month" для `select_month` и т.д.), хотя это может быть переопределено в опции `:field_name`. Опция `:prefix` работает так же, как работает для `select_date` и `select_time` и имеет такое же значение по умолчанию. @@ -589,59 +590,53 @@ NOTE: В большинстве случаев встроенные элемен Первый параметр определяет значение даты, которое будет предложено для выбора (соответствующий элемент может быть извлечен из экземпляра `Date`, `Time` или `DateTime`), либо сразу задается числовым значением. Например: ```erb -<%= select_year(2011) %> -<%= select_year(Time.now) %> +<%= select_year(2009) %> +<%= select_year(Time.new(2009)) %> ``` -создаст такой же результат, если сейчас 2011 год, и значение, выбранное пользователем, может быть получено как `params[:date][:year]`. +создадут одинаковый результат, и значение, выбранное пользователем, может быть получено как `params[:date][:year]`. Загрузка файлов --------------- -Частой задачей является загрузка некоторого файла, аватарки или файла CSV, содержащего информацию для обработки. Самое важное, это помнить при загрузке файла, что кодирование формы *ДОЛЖНО* быть установлено как "multipart/form-data". Если используете `form_for`, это будет выполнено автоматически. Если используете `form_tag`, нужно установить это самому, как в следующем примере. +Частой задачей является загрузка некоторого файла, аватарки или файла CSV, содержащего информацию для обработки. Самое важное, это помнить при загрузке файла, что атрибут enctype формы *должен* быть установлен как "multipart/form-data". Если используете `form_with` с `:model`, это будет выполнено автоматически. Если используете `form_with` без `:model`, нужно установить это самому, как в следующем примере. Следующие две формы обе загружают файл. ```erb -<%= form_tag({action: :upload}, multipart: true) do %> +<%= form_with(url: {action: :upload}, multipart: true) do %> <%= file_field_tag 'picture' %> <% end %> -<%= form_for @person do |f| %> +<%= form_with model: @person do |f| %> <%= f.file_field :picture %> <% end %> ``` -Rails предоставляет обычную пару хелперов: скелетный `file_field_tag` и модельно-ориентированный `file_field`. Единственное отличие от других хелперов в том, что нельзя установить значение по умолчанию для поля загрузки файла, так как в этом нет смысла. Как и следует ожидать, в первом случае загруженный файл находится в `params[:picture]`, а во втором случае - в `params[:person][:picture]`. +Rails предоставляет обычную пару хелперов: скелетный `file_field_tag` и модельно-ориентированный `file_field`. Как и следует ожидать, в первом случае загруженный файл находится в `params[:picture]`, а во втором случае - в `params[:person][:picture]`. ### Что имеем загруженным -Объект в хэше `params` - это экземпляр подкласса IO. В зависимости от размера загруженного файла, это может оказаться либо `StringIO`, либо `File`, отраженный на временный файл. В обоих случаях объект будет иметь атрибут `original_filename`, содержащий имя файла на компьютере пользователя, и атрибут `content_type`, содержащий тип MIME загруженного файла. Следующий отрывок сохраняет загруженное содержимое в `#{Rails.root}/public/uploads` под тем же именем, что и исходный файл (при условии, что эта была одна из форм предыдущего примера). +Объект в хэше `params` - это экземпляр [`ActionDispatch::Http::UploadedFile`](http://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html). Следующий образец кода сохраняет загруженное содержимое в `#{Rails.root}/public/uploads` под тем же именем, что и исходный файл. ```ruby def upload - uploaded_io = params[:person][:picture] - File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file| - file.write(uploaded_io.read) + uploaded_file = params[:picture] + File.open(Rails.root.join('public', 'uploads', uploaded_file.original_filename), 'wb') do |file| + file.write(uploaded_file.read) end end ``` -Как только файл был загружен, появляется множество потенциальных задач, начиная от того, где хранить файлы (на диске, Amazon S3 и т.д.), и как связать их с моделями, до изменения размера файлов изображений и генерации миниатюр. Такие частности выходят за рамки данного руководства, но имеется несколько библиотек, разработанных для содействия этому. Две лучших из них - это [CarrierWave](https://github.com/jnicklas/carrierwave) и [Paperclip](https://www.thoughtbot.com/projects/paperclip). - -NOTE: Если пользователь не выбрал файл, соответствующий параметр останется пустой строкой. - -### Работа с Ajax - -В отличие от других форм, создание формы асинхронной загрузки файла - это не просто передача параметра `remote: true` в `form_for`. В Ajax-форме сериализация происходит посредством JavaScript, исполняющимся внутри браузера, и, поскольку JavaScript не может читать файлы с жесткого диска, файл не может быть загружен. Наиболее частым решением является использование невидимого iframe, который служит целью для отправки формы. +Как только файл был загружен, появляется множество потенциальных задач, начиная от того, где хранить файлы (на диске, Amazon S3 и т.д.), как связать их с моделями, изменить размер файлов изображений и сгенерировать миниатюры. Для помощи с такими задачами разработан [Active Storage](/active_storage_overview). Настройка Form Builder ---------------------- -Как ранее упоминалось, объект, который передается от `form_for` и `fields_for`, - это экземпляр `FormBuilder` (или его подкласса). Form builder инкапсулирует представление элементов формы для отдельного объекта. Хотя, конечно, можно писать хелперы для своих форм обычным способом, так же как можно объявить подкласс `FormBuilder` и добавить хелперы туда. Например: +Объект, который передается от `form_with` и `fields_for`, - это экземпляр [`ActionView::Helpers::FormBuilder`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html). Form builder инкапсулирует представление элементов формы для отдельного объекта. Хотя, конечно, можно писать хелперы для своих форм обычным способом, так же как можно объявить подкласс `ActionView::Helpers::FormBuilder` и добавить хелперы туда. Например: ```erb -<%= form_for @person do |f| %> +<%= form_with model: @person do |f| %> <%= text_field_with_label f, :first_name %> <% end %> ``` @@ -649,7 +644,7 @@ NOTE: Если пользователь не выбрал файл, соотве может быть заменено этим ```erb -<%= form_for @person, builder: LabellingFormBuilder do |f| %> +<%= form_with model: @person, builder: LabellingFormBuilder do |f| %> <%= f.text_field :first_name %> <% end %> ``` @@ -664,12 +659,12 @@ class LabellingFormBuilder < ActionView::Helpers::FormBuilder end ``` -Если это используется часто, можно определить хелпер `labeled_form_for` который автоматически определяет опцию `builder: LabellingFormBuilder`: +Если это используется часто, можно определить хелпер `labeled_form_with` который автоматически определяет опцию `builder: LabellingFormBuilder`: ```ruby -def labeled_form_for(record, options = {}, &block) +def labeled_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block) options.merge! builder: LabellingFormBuilder - form_for record, options, &block + form_with model: model, scope: scope, url: url, format: format, **options, &block end ``` @@ -679,12 +674,12 @@ Form builder также определяет, что произойдет, ес <%= render partial: f %> ``` -Если `f` - это экземпляр `FormBuilder`, тогда это отрендерит партиал `form`, установив объект партиала как form builder. Если у form builder есть класс `LabellingFormBuilder`, тогда вместо него будет отрендерен партиал `labelling_form`. +Если `f` - это экземпляр `ActionView::Helpers::FormBuilder`, тогда это отрендерит партиал `form`, установив объект партиала как form builder. Если у form builder есть класс `LabellingFormBuilder`, тогда вместо него будет отрендерен партиал `labelling_form`. (understanding-parameter-naming-conventions) Понимание соглашений по именованию параметров ------------------------------------------------------------------------------------------ -Как можно было заметить в предыдущих разделах, значения из форм могут быть на верхнем уровне хэша `params` или вложены в другой хэш. Например, в стандартном экшне `create` для модели Person, `params[:person]` будет обычно хэшем всех атрибутов для создания персоны. Хэш `params` может также содержать массивы, массивы хэшей и тому подобное. +Значения из форм могут быть на верхнем уровне хэша `params` или вложены в другой хэш. Например, в стандартном экшне `create` для модели Person, `params[:person]` будет обычно хэшем всех атрибутов для создания персоны. Хэш `params` может также содержать массивы, массивы хэшей и тому подобное. В основном формы HTML не знают о каких-либо структурировании данных, все, что они генерируют - это пары имя-значение, где пары являются обычными строками. Массивы и хэши, которые можно увидеть в своем приложении, - это результат некоторых соглашений по именованию параметров, которые использует Rails. @@ -731,28 +726,31 @@ Form builder также определяет, что произойдет, ес Можно смешивать и сочетать эти две концепции. Один из элементов хэша может быть массивом, как в предыдущем примере, или вы можете иметь массив хэшей. Например, форма может позволить вам создать любое количество адресов, повторяя следующий фрагмент кода ```html - - - + + + + + + ``` -Это приведет к тому, что `params[:addresses]` будет массивом хэшей с ключами `line1`, `line2` и `city`. Rails начинает накапливать значения в новый хэш, всякий раз, когда он встречает имя поля ввода, уже существующее в текущем хэше. +Это приведет к тому, что `params[:person][:addresses]` будет массивом хэшей с ключами `line1`, `line2` и `city`. Однако, имеется ограничение, в то время как хэши могут быть вложены произвольно, является допустимым только один уровень "массивности". Массивы обычно могут быть заменены хэшами; например, вместо массива объектов модели можно иметь хэш объектов модели с ключами, равными их id, индексу массива или любому другому параметру. -WARNING: Параметры массива не очень хорошо работают с хелпером `check_box`. В соответствии со спецификацией HTML, невыбранные чекбоксы не возвращают значения. Хелпер `check_box` обходит это, создавая вспомогательное скрытое поле с тем же именем. Если чекбокс не нажат, подтверждается только скрытое поле, а если он нажат, то они оба подтверждаются, но значение от чекбокса получает приоритет. При работе с параметрами массива эти дублирующиеся подтверждения будет путать Rails дублирующимися именами полей ввода, и непонятно, как он решит, где начать новый элемент массива. Предпочтительнее использовать или `check_box_tag`, или хэши вместо массивов. +WARNING: Параметры массива не очень хорошо работают с хелпером `check_box`. В соответствии со спецификацией HTML, невыбранные чекбоксы не возвращают значения. Хелпер `check_box` обходит это, создавая вспомогательное скрытое поле с тем же именем. Если чекбокс не нажат, подтверждается только скрытое поле, а если он нажат, то они оба подтверждаются, но значение от чекбокса получает приоритет. ### Использование хелперов форм -Предыдущие разделы совсем не использовали хелперы Rails. Хотя можно создавать имена полей ввода самому и передавать их напрямую хелперам, таким как `text_field_tag`, Rails также предоставляет поддержку на более высоком уровне. В вашем распоряжении имеются два инструмента: параметр имени для `form_for` и `fields_for`, и опция `:index`, принимаемая этими хелперами. +Предыдущие разделы совсем не использовали хелперы Rails. Хотя можно создавать имена полей ввода самому и передавать их напрямую хелперам, таким как `text_field_tag`, Rails также предоставляет поддержку на более высоком уровне. В вашем распоряжении имеются два инструмента: параметр имени для `form_with` и `fields_for`, и опция `:index`, принимаемая этими хелперами. Вы возможно захотите рендерить форму с набором полей ввода для каждого адреса человека. Например: ```erb -<%= form_for @person do |person_form| %> +<%= form_with model: @person do |person_form| %> <%= person_form.text_field :name %> <% @person.addresses.each do |address| %> - <%= person_form.fields_for address, index: address.id do |address_form|%> + <%= person_form.fields_for address, index: address.id do |address_form| %> <%= address_form.text_field :city %> <% end %> <% end %> @@ -762,7 +760,8 @@ WARNING: Параметры массива не очень хорошо рабо Предположим, у кого-то есть два адреса с id 23 и 45, это создаст что-то подобное: ```html -
+ + @@ -780,7 +779,7 @@ Rails знает, что все эти поля ввода должны быть Чтобы создать более замысловатые вложения, можно явно указать первую часть имени поля ввода (`person[address]` в предыдущем примере): ```erb -<%= fields_for 'person[address][primary]', address, index: address do |address_form| %> +<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %> <%= address_form.text_field :city %> <% end %> ``` @@ -788,12 +787,12 @@ Rails знает, что все эти поля ввода должны быть создаст такие поля ввода ```html - + ``` -Как правило, конечное имя поля ввода - это сцепление имени, переданного в `fields_for`/`form_for`, значения индекса и имени атрибута. Можно также передать опцию `:index` прямо в хелперы, такие как `text_field`, но обычно будет меньше повторов, если определить это на уровне form builder, а не для отдельных элементах управления input. +Как правило, конечное имя поля ввода - это сцепление имени, переданного в `fields_for`/`form_with`, значения индекса и имени атрибута. Можно также передать опцию `:index` прямо в хелперы, такие как `text_field`, но обычно будет меньше повторов, если определить это на уровне form builder, а не для отдельных элементах управления input. -Как ярлык вы можете добавить [] к имени и опустить опцию `:index`. Это то же самое, что определение `index: address`, таким образом +Как ярлык вы можете добавить [] к имени и опустить опцию `:index`. Это то же самое, что определение `index: address.id`, таким образом ```erb <%= fields_for 'person[address][primary][]', address do |address_form| %> @@ -806,10 +805,10 @@ Rails знает, что все эти поля ввода должны быть Формы к внешним ресурсам ------------------------- -Хелперы форм Rails можно использовать и для создания форм для передачи данных внешнему ресурсу. Однако, иногда необходимо установить `authenticity_token` для ресурса; это можно осуществить, передав параметр `authenticity_token: 'your_external_token'` в опциях `form_tag`: +Хелперы форм Rails можно использовать и для создания форм для передачи данных внешнему ресурсу. Однако, иногда необходимо установить `authenticity_token` для ресурса; это можно осуществить, передав параметр `authenticity_token: 'your_external_token'` в опциях `form_with`: ```erb -<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %> +<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %> Form contents <% end %> ``` @@ -817,23 +816,7 @@ Rails знает, что все эти поля ввода должны быть Иногда при отправке данных внешнему ресурсу, такому как платежный шлюз, поля, которые можно использовать в форме, ограничены внешним API, и генерация `authenticity_token` нежелательна. Чтобы не посылать токен, просто передайте `false` в опцию `:authenticity_token`: ```erb -<%= form_tag 'http://farfar.away/form', authenticity_token: false do %> - Form contents -<% end %> -``` - -Та же техника также доступна и для `form_for`: - -```erb -<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %> - Form contents -<% end %> -``` - -Или, если не хотите создавать поле `authenticity_token`: - -```erb -<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %> +<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %> Form contents <% end %> ``` @@ -866,7 +849,7 @@ end Следующая форма позволяет пользователю создать `Person` и связанные с ним адреса. ```html+erb -<%= form_for @person do |f| %> +<%= form_with model: @person do |f| %> Addresses: