액션 뷰 폼 헬퍼
웹 애플리케이션의 폼은 사용자 입력을 위한 필수적인 인터페이스입니다. 그러나 폼 마크업을 작성하고 유지하는 것은 폼 컨트롤 이름 지정과 다양한 속성을 처리해야 하기 때문에 번거로울 수 있습니다. Rails는 폼 마크업을 생성하는 뷰 헬퍼를 제공하여 이러한 복잡성을 해결합니다. 그러나 이러한 헬퍼들은 다양한 사용 사례가 있기 때문에 개발자들은 이를 사용하기 전에 헬퍼 메서드 간의 차이점을 알아야 합니다.
이 가이드를 읽고 나면 다음을 알 수 있습니다:
- 애플리케이션의 특정 모델을 나타내지 않는 검색 폼과 유사한 일반적인 폼을 어떻게 만들 수 있는지.
- 특정 데이터베이스 레코드를 생성하고 편집하는 모델 중심 폼을 어떻게 만들 수 있는지.
- 다양한 유형의 데이터에서 선택 상자를 어떻게 생성할 수 있는지.
- Rails가 제공하는 날짜 및 시간 헬퍼는 무엇인지.
- 파일 업로드 폼이 어떻게 다른지.
- 외부 리소스에 폼을 전송하고
authenticity_token
을 설정하는 방법. - 복잡한 폼을 어떻게 구축할 수 있는지.
주의: 이 가이드는 사용 가능한 모든 폼 헬퍼와 인수에 대한 완전한 문서화를 목적으로 하지 않습니다. 모든 사용 가능한 헬퍼에 대한 전체 참조는 Rails API 문서를 참조하십시오.
기본 폼 다루기
주요 폼 헬퍼는 form_with
입니다.
<%= form_with do |form| %> 폼 내용 <% end %>
이렇게 인수 없이 호출하면 현재 페이지에 POST 요청을 보내는 폼 태그를 생성합니다. 예를 들어 현재 페이지가 홈 페이지라고 가정하면 생성된 HTML은 다음과 같습니다:
<form accept-charset="UTF-8" action="/" method="post"> <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" /> 폼 내용 </form>
HTML에는 type="hidden"
인 input
요소가 포함되어 있는 것을 알 수 있습니다. 이 input
은 중요한데, GET 요청이 아닌 폼은 이 토큰 없이는 성공적으로 제출될 수 없기 때문입니다.
authenticity_token
이라는 이름의 숨겨진 input
요소는 Rails의 보안 기능인 크로스 사이트 요청 위조 보호이며, 폼 헬퍼는 모든 GET 요청이 아닌 폼에 대해 이를 생성합니다(이 보안 기능이 활성화된 경우). 이에 대해서는 Rails 애플리케이션 보안 가이드에서 자세히 알아볼 수 있습니다.
일반적인 검색 폼
웹에서 가장 기본적인 폼 중 하나는 검색 폼입니다. 이 폼에는 다음이 포함됩니다:
- “GET” 메서드를 사용하는 폼 요소,
- 입력에 대한 레이블,
- 텍스트 입력 요소, 그리고
- 제출 요소.
이 폼을 만들려면 form_with
와 이를 통해 생성된 폼 빌더 객체를 사용합니다. 다음과 같이:
<%= form_with url: "/search", method: :get do |form| %> <%= form.label :query, "검색:" %> <%= form.text_field :query %> <%= form.submit "검색" %> <% end %>
이렇게 하면 다음과 같은 HTML이 생성됩니다:
<form action="/search" method="get" accept-charset="UTF-8" > <label for="query">검색:</label> <input id="query" name="query" type="text" /> <input name="commit" type="submit" value="검색" data-disable-with="검색" /> </form>
팁: form_with
에 url: my_specified_path
를 전달하면 폼이 요청을 보낼 위치를 지정할 수 있습니다. 그러나 아래에서 설명하듯이 Active Record 객체를 폼에 전달할 수도 있습니다.
팁: 각 폼 입력에 대해 이름("query"
예제)에서 파생된 ID 속성이 생성됩니다. 이러한 ID는 CSS 스타일링 또는 JavaScript를 사용한 폼 컨트롤 조작에 매우 유용할 수 있습니다.
중요: 검색 폼의 메서드는 “GET"을 사용해야 합니다. 이렇게 하면 사용자가 특정 검색을 북마크하고 다시 돌아올 수 있습니다. 일반적으로 Rails는 작업에 적절한 HTTP 동사를 사용하도록 권장합니다.
폼 요소 생성을 위한 헬퍼
form_with
에 의해 생성된 폼 빌더 객체는 텍스트 필드, 체크박스, 라디오 버튼 등의 폼 요소를 생성하는 다양한 헬퍼 메서드를 제공합니다. 이러한 메서드의 첫 번째 인수는 항상 입력의 이름입니다. 폼이 제출되면 이 이름이 폼 데이터와 함께 전달되어 컨트롤러의 params
에 사용자가 해당 필드에 입력한 값이 포함됩니다. 예를 들어 폼에 <%= form.text_field :query %>
가 포함되어 있다면 컨트롤러에서 params[:query]
를 통해 이 필드의 값을 가져올 수 있습니다.
입력 이름을 지정할 때 Rails는 스칼라 값이 아닌 배열 또는 해시와 같은 매개변수를 제출할 수 있도록 하는 특정 규칙을 사용합니다. 이에 대해서는 이 가이드의 매개변수 명명 규칙 이해하기 섹션에서 자세히 설명합니다. 이러한 헬퍼의 정확한 사용 방법에 대해서는 API 문서를 참조하십시오.
체크박스
체크박스는 사용자가 활성화하거나 비활성화할 수 있는 옵션 집합을 제공하는 폼 컨트롤입니다:
<%= form.check_box :pet_dog %> <%= form.label :pet_dog, "I own a dog" %> <%= form.check_box :pet_cat %> <%= form.label :pet_cat, "I own a cat" %>
이렇게 생성되면 다음과 같습니다:
<input type="checkbox" id="pet_dog" name="pet_dog" value="1" /> <label for="pet_dog">I own a dog</label> <input type="checkbox" id="pet_cat" name="pet_cat" value="1" /> <label for="pet_cat">I own a cat</label>
check_box
의 첫 번째 인수는 입력의 이름입니다. 체크박스의 값(즉, params
에 나타나는 값)은 선택적으로 세 번째 및 네 번째 인수를 사용하여 지정할 수 있습니다. 자세한 내용은 API 문서를 참조하십시오.
라디오 버튼
라디오 버튼은 체크박스와 유사하지만, 상호 배타적인 옵션 집합(즉, 사용자가 하나만 선택할 수 있음)을 지정하는 컨트롤입니다:
<%= form.radio_button :age, "child" %> <%= form.label :age_child, "I am younger than 21" %> <%= form.radio_button :age, "adult" %> <%= form.label :age_adult, "I am over 21" %>
출력:
<input type="radio" id="age_child" name="age" value="child" /> <label for="age_child">I am younger than 21</label> <input type="radio" id="age_adult" name="age" value="adult" /> <label for="age_adult">I am over 21</label>
radio_button
의 두 번째 인수는 입력의 값입니다. 이 두 개의 라디오 버튼이 동일한 이름(age
)을 공유하므로 사용자는 둘 중 하나만 선택할 수 있으며, params[:age]
에는 "child"
또는 "adult"
가 포함됩니다.
주의: 항상 체크박스와 라디오 버튼에 레이블을 사용하십시오. 이는 특정 옵션과 텍스트를 연결하고 클릭 가능한 영역을 확장하여 사용자가 입력을 더 쉽게 클릭할 수 있게 합니다.
기타 주목할 만한 헬퍼
텍스트 영역, 숨겨진 필드, 비밀번호 필드, 숫자 필드, 날짜 및 시간 필드 등 다른 폼 컨트롤도 언급할 만합니다:
<%= form.text_area :message, size: "70x5" %> <%= form.hidden_field :parent_id, value: "foo" %> <%= form.password_field :password %> <%= form.number_field :price, in: 1.0..20.0, step: 0.5 %> <%= form.range_field :discount, in: 1..100 %> <%= form.date_field :born_on %> <%= form.time_field :started_at %> <%= form.datetime_local_field :graduation_day %> <%= form.month_field :birthday_month %> <%= form.week_field :birthday_week %> <%= form.search_field :name %> <%= form.email_field :address %> <%= form.telephone_field :phone %> <%= form.url_field :homepage %> <%= form.color_field :favorite_color %>
출력:
<textarea name="message" id="message" cols="70" rows="5"></textarea> <input type="hidden" name="parent_id" id="parent_id" value="foo" /> <input type="password" name="password" id="password" /> <input type="number" name="price" id="price" step="0.5" min="1.0" max="20.0" /> <input type="range" name="discount" id="discount" min="1" max="100" /> <input type="date" name="born_on" id="born_on" /> <input type="time" name="started_at" id="started_at" /> <input type="datetime-local" name="graduation_day" id="graduation_day" /> <input type="month" name="birthday_month" id="birthday_month" /> <input type="week" name="birthday_week" id="birthday_week" /> <input type="search" name="name" id="name" /> <input type="email" name="address" id="address" /> <input type="tel" name="phone" id="phone" /> <input type="url" name="homepage" id="homepage" /> <input type="color" name="favorite_color" id="favorite_color" value="#000000" />
숨겨진 입력은 사용자에게 표시되지 않지만 다른 텍스트 입력과 마찬가지로 데이터를 보유할 수 있습니다. 이 값은 JavaScript를 사용하여 변경할 수 있습니다.
중요: 검색, 전화, 날짜, 시간, 색상, 날짜 및 시간, 월, 주, URL, 이메일, 숫자 및 범위 입력은 HTML5 컨트롤입니다.모델 객체 다루기
객체에 폼 바인딩하기
form_with
의 :model
인수를 사용하면 폼 빌더 객체를 모델 객체에 바인딩할 수 있습니다. 이렇게 하면 폼이 해당 모델 객체의 범위 내에 있게 되고, 폼의 필드가 해당 모델 객체의 값으로 채워집니다.
예를 들어 @article
이라는 모델 객체가 있다고 가정해 봅시다:
@article = Article.find(42) # => #<Article id: 42, title: "My Title", body: "My Body">
다음과 같은 폼:
<%= form_with model: @article do |form| %> <%= form.text_field :title %> <%= form.text_area :body, size: "60x10" %> <%= form.submit %> <% end %>
출력:
<form action="/articles/42" method="post" accept-charset="UTF-8" > <input name="authenticity_token" type="hidden" value="..." /> <input type="text" name="article[title]" id="article_title" value="My Title" /> <textarea name="article[body]" id="article_body" cols="60" rows="10"> My Body </textarea> <input type="submit" name="commit" value="Update Article" data-disable-with="Update Article"> </form>
여기에는 몇 가지 주목할 만한 사항이 있습니다:
- 폼
action
이 자동으로@article
에 적합한 값으로 채워집니다. - 폼 필드가 자동으로
@article
의 해당 값으로 채워집니다. - 폼 필드 이름이
article[...]
로 범위가 지정됩니다. 이는params[:article]
이 이러한 필드의 값을 포함하는 해시가 될 것임을 의미합니다. 입력 이름의 중요성에 대해서는 이 가이드의 매개변수 명명 규칙 이해하기 장에서 자세히 설명합니다. - 제출 버튼의 텍스트 값이 자동으로 적절하게 지정됩니다.
팁: 관례상 입력은 모델 속성을 반영하지만, 그렇지 않아도 됩니다! 속성 이외의 정보가 필요한 경우 속성과 마찬가지로 폼에 포함하고 params[:article][:my_nifty_non_attribute_input]
을 통해 액세스할 수 있습니다.
복합 기본 키 폼
복합 기본 키 모델로 폼을 구축할 수도 있습니다. 이 경우 폼 구축 구문은 동일하지만 출력이 약간 다릅니다.
복합 키 [:author_id, :id]
를 가진 @book
모델 객체가 있다고 가정해 봅시다:
@book = Book.find([2, 25]) # => #<Book id: 25, title: "Some book", author_id: 2>
다음과 같은 폼:
<%= form_with model: @book do |form| %> <%= form.text_field :title %> <%= form.submit %> <% end %>
출력:
<form action="/books/2_25" method="post" accept-charset="UTF-8" > <input name="authenticity_token" type="hidden" value="..." /> <input type="text" name="book[title]" id="book_title" value="Some book" /> <input type="submit" name="commit" value="Update Book" data-disable-with="Update Book"> </form>
생성된 URL에는 밑줄로 구분된 author_id
와 id
가 포함되어 있습니다.
제출되면 컨트롤러는 매개변수에서 각 기본 키 값을 추출하고 단일 기본 키를 사용하는 것과 마찬가지로 레코드를 업데이트할 수 있습니다.
fields_for
헬퍼
fields_for
헬퍼는 <form>
태그를 렌더링하지 않고 유사한 바인딩을 생성합니다. 이를 사용하여 동일한 폼 내에서 추가 모델 객체의 필드를 렌더링할 수 있습니다. 예를 들어 Person
모델에 관련된 ContactDetail
모델이 있다면 다음과 같이 단일 폼을 만들 수 있습니다:
<%= form_with model: @person do |person_form| %> <%= person_form.text_field :name %> <%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %> <%= contact_detail_form.text_field :phone_number %> <% end %> <% end %>
이렇게 하면 다음과 같은 출력이 생성됩니다:
<form action="/people" accept-charset="UTF-8" method="post"> <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" /> <input type="text" name="person[name]" id="person_name" /> <input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" /> </form>
fields_for
에 의해 생성된 객체는 form_with
에 의해 생성된 것과 같은 폼 빌더입니다.
레코드 식별에 의존하기
Article 모델은 애플리케이션 사용자에게 직접 노출되므로 - Rails로 개발할 때의 모범 사례를 따르면 - 리소스로 선언해야 합니다:
resources :articles
팁: 리소스를 선언하면 여러 가지 부작용이 있습니다. 리소스 설정 및 사용에 대한 자세한 내용은 Rails 라우팅 - 외부에서 내부로 가이드를 참조하십시오.
RESTful 리소스를 다룰 때 레코드 식별에 의존하면 form_with
호출을 크게 간소화할 수 있습니다. 간단히 말해, 모델 인스턴스를 전달하면 Rails가 모델 이름과 나머지를 파악할 수 있습니다. 이 두 가지 예에서 긴 스타일과 짧은 스타일은 동일한 결과를 가집니다:
## 새 article 생성 # 긴 스타일: form_with(model: @article, url: articles_path) # 짧은 스타일: form_with(model: @article) ## 기존 article 편집 # 긴 스타일: form_with(model: @article, url: article_path(@article), method: "patch") # 짧은 스타일: form_with(model: @article)
짧은 스타일 form_with
호출이 레코드가 새로운지 기존인지에 관계없이 동일하다는 점에 주목하십시오. 레코드 식별은 record.persisted?
를 호출하여 레코드가 새로운지 여부를 파악할 수 있습니다. 또한 제출할 올바른 경로와 객체 클래스를 기반으로 이름을 선택합니다.
단일 리소스를 사용하는 경우 resource
및 resolve
를 호출해야 form_with
에서 작동합니다:
resource :geocoder resolve('Geocoder') { [:geocoder] }
경고: 모델에 STI(단일 테이블 상속)를 사용하는 경우 부모 클래스만 리소스로 선언된 경우 하위 클래스에서 레코드 식별을 사용할 수 없습니다. 명시적으로 :url
및 :scope
(모델 이름)를 지정해야 합니다.
네임스페이스 처리하기
애플리케이션에 네임스페이스화된 경로를 생성한 경우 form_with
에도 편리한 단축 구문이 있습니다. 애플리케이션에 admin 네임스페이스가 있다면
form_with model: [:admin, @article]
는 admin 네임스페이스 내의 ArticlesController
에 폼을 제출합니다(업데이트의 경우 admin_article_path(@article)
). 네임스페이스가 여러 레벨인 경우 구문은 유사합니다:
form_with model: [:admin, :management, @article]
Rails의 라우팅 시스템과 관련 규칙에 대한 자세한 내용은 Rails 라우팅 - 외부에서 내부로 가이드를 참조하십시오.
PATCH, PUT 또는 DELETE 메서드를 사용하는 폼은 어떻게 작동합니까?
Rails 프레임워크는 애플리케이션의 RESTful 설계를 권장하므로 "PATCH”, “PUT” 및 “DELETE” 요청(GET 및 POST 외에)을 많이 사용하게 됩니다. 그러나 대부분의 브라우저는 “GET” 및 “POST” 이외의 메서드로 폼을 제출하는 것을 지원하지 않습니다.
Rails는 이 문제를 해결하기 위해 "_method"
라는 숨겨진 입력을 통해 POST 요청 위에 다른 메서드를 에뮬레이션합니다:
form_with(url: search_path, method: "patch")
출력:
<form accept-charset="UTF-8" action="/search" method="post"> <input name="_method" type="hidden" value="patch" /> <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /> <!-- ... --> </form>
POST된 데이터를 구문 분석할 때 Rails는 특수 _method
매개변수를 고려하고 해당 매개변수에 지정된 HTTP 메서드(이 예에서는 “PATCH”)로 처리합니다.
폼을 렌더링할 때 제출 버튼은 formmethod:
키워드를 통해 선언된 method
속성을 재정의할 수 있습니다:
<%= form_with url: "/posts/1", method: :patch do |form| %> <%= form.button "삭제", formmethod: :delete, data: { confirm: "정말 삭제하시겠습니까?" } %> <%= form.button "업데이트" %> <% end %>
<form>
요소와 마찬가지로 대부분의 브라우저는 “GET” 및 “POST” 이외의 formmethod를 통해 선언된 폼 메서드를 지원하지 않습니다.
Rails는 이 문제를 formmethod, value 및 name 속성의 조합을 통해 POST 위에 다른 메서드를 에뮬레이션하여 해결합니다:
<form accept-charset="UTF-8" action="/posts/1" method="post"> <input name="_method" type="hidden" value="patch" /> <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /> <!-- ... --> <button type="submit" formmethod="post" name="_method" value="delete" data-confirm="정말 삭제하시겠습니까?">삭제</button> <button type="submit" name="button">업데이트</button> </form>
선택 상자 쉽게 만들기
HTML의 선택 상자는 선택할 수 있는 각 옵션에 대해 <option>
요소가 필요하므로 상당한 양의 마크업이 필요합니다. 따라서 Rails는 이 부담을 줄이기 위해 헬퍼 메서드를 제공합니다.
예를 들어 사용자가 선택할 수 있는 도시 목록이 있다고 가정해 봅시다. [select
](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder선택 상자 쉽게 만들기
HTML의 선택 상자는 선택할 수 있는 각 옵션에 대해 <option>
요소가 필요하므로 상당한 양의 마크업이 필요합니다. 따라서 Rails는 이 부담을 줄이기 위해 헬퍼 메서드를 제공합니다.
예를 들어 사용자가 선택할 수 있는 도시 목록이 있다고 가정해 봅시다. select
헬퍼를 다음과 같이 사용할 수 있습니다:
<%= form.select :city, ["Berlin", "Chicago", "Madrid"] %>
출력:
<select name="city" id="city"> <option value="Berlin">Berlin</option> <option value="Chicago">Chicago</option> <option value="Madrid">Madrid</option> </select>
<option>
값이 레이블과 다르게 지정할 수도 있습니다:
<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]] %>
출력:
<select name="city" id="city"> <option value="BE">Berlin</option> <option value="CHI">Chicago</option> <option value="MD">Madrid</option> </select>
이 경우 사용자는 전체 도시 이름을 볼 수 있지만 params[:city]
에는 "BE"
, "CHI"
또는 "MD"
가 포함됩니다.
마지막으로 :selected
인수를 사용하여 선택 상자의 기본 선택 항목을 지정할 수 있습니다:
<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]], selected: "CHI" %>
출력:
<select name="city" id="city"> <option value="BE">Berlin</option> <option value="CHI" selected="selected">Chicago</option> <option value="MD">Madrid</option> </select>
옵션 그룹
때로는 관련 옵션을 그룹화하여 사용자 경험을 개선할 수 있습니다. select
에 Hash
(또는 비교 가능한 Array
)를 전달하여 이를 수행할 수 있습니다:
<%= form.select :city, { "Europe" => [ ["Berlin", "BE"], ["Madrid", "MD"] ], "North America" => [ ["Chicago", "CHI"] ], }, selected: "CHI" %>
출력:
<select name="city" id="city"> <optgroup label="Europe"> <option value="BE">Berlin</option> <option value="MD">Madrid</option> </optgroup> <optgroup label="North America"> <option value="CHI" selected="selected">Chicago</option> </optgroup> </select>
모델 객체와 선택 상자
다른 폼 컨트롤과 마찬가지로 선택 상자를 모델 속성에 바인딩할 수 있습니다. 예를 들어 @person
모델 객체가 다음과 같다고 가정해 봅시다:
@person = Person.new(city: "MD")
다음과 같은 폼:
<%= form_with model: @person do |form| %> <%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]] %> <% end %>
다음과 같은 선택 상자를 출력합니다:
<select name="person[city]" id="person_city"> <option value="BE">Berlin</option> <option value="CHI">Chicago</option> <option value="MD" selected="selected">Madrid</option> </select>
적절한 옵션이 자동으로 selected="selected"
로 표시되었음에 주목하십시오. 이 선택 상자가 모델에 바인딩되었기 때문에 :selected
인수를 지정할 필요가 없었습니다!
시간대 및 국가 선택
Rails의 시간대 지원을 활용하려면 사용자에게 자신의 시간대를 묻는 것이 필요합니다. 이렇게 하려면 미리 정의된 ActiveSupport::TimeZone
객체 목록에서 선택 옵션을 생성해야 하지만, time_zone_select
헬퍼를 사용하면 이를 간단히 처리할 수 있습니다:
<%= form.time_zone_select :time_zone %>
Rails는 이전에 국가를 선택하는 country_select
헬퍼를 제공했지만, 이는 country_select 플러그인으로 추출되었습니다.
날짜 및 시간 폼 헬퍼 사용하기
HTML5 날짜 및 시간 입력을 사용하고 싶지 않은 경우 Rails는 일반 선택 상자를 렌더링하는 대체 날짜 및 시간 폼 헬퍼를 제공합니다. 이 헬퍼는 각 시간 구성 요소(예: 연도, 월, 일 등)에 대한 선택 상자를 렌더링합니다. 예를 들어 @person
모델 객체가 다음과 같다고 가정해 봅시다:
@person = Person.new(birth_date: Date.new(1995, 12, 21))
다음과 같은 폼:
<%= form_with model: @person do |form| %> <%= form.date_select :birth_date %> <% end %>
다음과 같은 선택 상자를 출력합니다:
<select name="person[birth_date(1i)]" id="person_birth_date_1i"> <option value="1990">1990</option> <option value="1991">1991</option> <option value="1992">1992</option> <option value="1993">1993</option> <option value="1994">1994</option> <option value="1995" selected="selected">1995</option> <option value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> <option value="1999">1999</option> <option value="2000">2000</option> </select> <select name="person[birth_date(2i)]" id="person_birth_date_2i"> <option value="1">January</option> <option value="2">February</option> <option value="3">March</option> <option value="4">April</option> <option value="5">May</option> <option value="6">June</option> <option value="7">July</option> <option value="8">August</option> <option value="9">September</option> <option value="10">October</option> <option value="11">November</option> <option value="12" selected="selected">December</option> </select> <select name="person[birth_date(3i)]" id="person_birth_date_3i"> <option value="1">1</option> ... <option value="21" selected="selected">21</option> ... <option value="31">31</option> </select>
제출되면 params
해시에 전체 날짜를 포함하는 단일 값이 없다는 점에 유의하십시오. 대신 "birth_date(1i)"
등의 특수 이름을 가진 여러 값이 있습니다. Active Record는 모델 속성의 선언된 유형을 기반으로 이러한 특수 이름의 값을 전체 날짜 또는 시간으로 조립할 수 있습니다. 따라서 params[:person]
을 Person.new
또는 Person#update
에 전달할 수 있습니다.
date_select
헬퍼 외에도 Rails는 time_select
및 datetime_select
를 제공합니다.
개별 시간 구성 요소에 대한 선택 상자
Rails는 개별 시간 구성 요소에 대한 선택 상자를 렌더링하는 헬퍼도 제공합니다: select_year
, select_month
, select_day
, select_hour
, select_minute
및 select_second
. 이러한 헬퍼는 폼 빌더 인스턴스에 대해 호출되지 않는 “bare” 메서드입니다. 예를 들면 다음과 같습니다:
<%= select_year 1999, prefix: "party" %>
다음과 같은 선택 상자를 출력합니다:
<select name="party[year]" id="party_year"> <option value="1994">1994</option> <option value="1995">1995</option> <option value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> <option value="1999" selected="selected">1999</option> <option value="2000">2000</option> <option value="2001">2001</option> <option value="2002">2002</option> <option value="2003">2003</option> <option value="2004">2004</option> </select>
이러한 각 헬퍼에 대해 숫자 대신 날짜 또는 시간 객체를 기본값으로 지정할 수 있으며, 적절한 시간 구성 요소가 추출되어 사용됩니다.
임의 객체 컬렉션에서 선택 만들기
때로는 임의 객체 컬렉션에서 선택 집합을 생성해야 합니다. 예를 들어 City
모델과 해당 belongs_to :city
연결이 있다고 가정해 봅시다:
class City < ApplicationRecord end class Person < ApplicationRecord belongs_to :city end
City.order(:name).map { |city| [city.name, city.id] } # => [["Berlin", 3], ["Chicago", 1], ["Madrid", 2]]
그러면 다음과 같은 폼을 사용하여 데이터베이스에서 도시를 선택할 수 있습니다:
<%= form_with model: @person do |form| %> <%= form.select :city_id, City.order(:name).map { |city| [city.name, city.id] } %> <% end %>
주의: belongs_to
연결에 대한 필드를 렌더링할 때 연결 이름(city
예제) 대신 외래 키 이름(city_id
)을 지정해야 합니다.
그러나 Rails는 명시적으로 컬렉션을 반복하지 않고도 컬렉션에서 선택을 생성하는 헬퍼를 제공합니다. 이 헬퍼는 각 객체에서 지정된 메서드를 호출하여 각 선택의 값과 텍스트 레이블을 결정합니다.
collection_select
헬퍼
선택 상자를 생성하려면 collection_select
를 사용할 수 있습니다:
<%= form.collection_select :city_id, City.order(:name), :id, :name %>
출력:
<select name="person[city_id]" id="person_city_id"> <option value="3">Berlin</option> <option value="1">Chicago</option> <option value="2">Madrid</option> </select>
주의: collection_select
에서는 먼저 값 메서드(위 예에서는 :id
)를 지정하고 텍스트 레이블 메서드(위 예에서는 :name
)를 지정합니다. 이는 select
헬퍼에서 사용되는 순서와 반대입니다.
collection_radio_buttons
헬퍼
라임의 객체 컬렉션에서 선택 만들기
때로는 임의 객체 컬렉션에서 선택 집합을 생성해야 합니다. 예를 들어 City
모델과 해당 belongs_to :city
연결이 있다고 가정해 봅시다:
class City < ApplicationRecord end class Person < ApplicationRecord belongs_to :city end
City.order(:name).map { |city| [city.name, city.id] } # => [["Berlin", 3], ["Chicago", 1], ["Madrid", 2]]
그러면 다음과 같은 폼을 사용하여 데이터베이스에서 도시를 선택할 수 있습니다:
<%= form_with model: @person do |form| %> <%= form.select :city_id, City.order(:name).map { |city| [city.name, city.id] } %> <% end %>
주의: belongs_to
연결에 대한 필드를 렌더링할 때 연결 이름(city
예제) 대신 외래 키 이름(city_id
)을 지정해야 합니다.
그러나 Rails는 명시적으로 컬렉션을 반복하지 않고도 컬렉션에서 선택을 생성하는 헬퍼를 제공합니다. 이 헬퍼는 각 객체에서 지정된 메서드를 호출하여 각 선택의 값과 텍스트 레이블을 결정합니다.
collection_select
헬퍼
선택 상자를 생성하려면 collection_select
를 사용할 수 있습니다:
<%= form.collection_select :city_id, City.order(:name), :id, :name %>
출력:
<select name="person[city_id]" id="person_city_id"> <option value="3">Berlin</option> <option value="1">Chicago</option> <option value="2">Madrid</option> </select>
주의: collection_select
에서는 먼저 값 메서드(위 예에서는 :id
)를 지정하고 텍스트 레이블 메서드(위 예에서는 :name
)를 지정합니다. 이는 select
헬퍼에서 사용되는 순서와 반대입니다.
collection_radio_buttons
헬퍼
라디오 버튼 집합을 생성하려면 collection_radio_buttons
를 사용할 수 있습니다:
<%= form.collection_radio_buttons :city_id, City.order(:name), :id, :name %>
출력:
<input type="radio" name="person[city_id]" value="3" id="person_city_id_3"> <label for="person_city_id_3">Berlin</label> <input type="radio" name="person[city_id]" value="1" id="person_city_id_1"> <label for="person_city_id_1">Chicago</label> <input type="radio" name="person[city_id]" value="2" id="person_city_id_2"> <label for="person_city_id_2">Madrid</label>
collection_check_boxes
헬퍼
체크박스 집합을 생성하려면 - 예를 들어 has_and_belongs_to_many
연결을 지원하려면 - collection_check_boxes
를 사용할 수 있습니다:
<%= form.collection_check_boxes :interest_ids, Interest.order(:name), :id, :name %>
출력:
<input type="checkbox" name="person[interest_id][]" value="3" id="person_interest_id_3"> <label for="person_interest_id_3">Engineering</label> <input type="checkbox" name="person[interest_id][]" value="4" id="person_interest_id_4"> <label for="person_interest_id_4">Math</label> <input type="checkbox" name="person[interest_id][]" value="1" id="person_interest_id_1"> <label for="person_interest_id_1">Science</label> <input type="checkbox" name="person[interest_id][]" value="2" id="person_interest_id_2"> <label for="person_interest_id_2">Technology</label>
파일 업로드
일반적인 작업 중 하나는 파일을 업로드하는 것입니다. 이는 사람의 사진이나 데이터를 처리할 CSV 파일일 수 있습니다. 파일 업로드 필드는 file_field
헬퍼를 사용하여 렌더링할 수 있습니다.
<%= form_with model: @person do |form| %> <%= form.file_field :picture %> <% end %>
파일 업로드와 관련된 가장 중요한 사항은 렌더링된 폼의 enctype
속성이 반드시 “multipart/form-data"로 설정되어야 한다는 것입니다. 이는 file_field
를 form_with
내부에 사용하면 자동으로 수행됩니다. 또한 수동으로 속성을 설정할 수 있습니다:
<%= form_with url: "/uploads", multipart: true do |form| %> <%= file_field_tag :picture %> <% end %>
form_with
규칙에 따라 두 폼의 필드 이름이 다르다는 점에 유의하십시오. 첫 번째 폼의 필드 이름은 person[picture]
(params[:person][:picture]
를 통해 액세스 가능)이고, 두 번째 폼의 필드 이름은 picture
(params[:picture]
를 통해 액세스 가능)입니다.
무엇이 업로드되는가
params
해시의 객체는 ActionDispatch::Http::UploadedFile
의 인스턴스입니다. 다음 스니펫은 업로드된 파일을 #{Rails.root}/public/uploads
에 원래 파일 이름으로 저장합니다.
def upload 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 등), 모델과 연결, 이미지 파일 크기 조정, 썸네일 생성 등 다양한 작업을 수행할 수 있습니다. Active Storage는 이러한 작업을 지원하도록 설계되었습니다.
폼 빌더 사용자 정의하기
form_with
와 fields_for
에 의해 생성된 객체는 ActionView::Helpers::FormBuilder
의 인스턴스입니다.
폼 빌더는 단일 객체에 대한 폼 요소를 표시하는 개념을 캡슐화합니다. 일반적인 방식으로 폼에 대한 헬퍼를 작성할 수 있지만, ActionView::Helpers::FormBuilder
의 하위 클래스를 만들고 거기에 헬퍼를 추가할 수도 있습니다. 예를 들어 애플리케이션에 정의된 text_field_with_label
이라는 헬퍼 메서드가 다음과 같다고 가정해 봅시다.
module ApplicationHelper def text_field_with_label(form, attribute) form.label(attribute) + form.text_field(attribute) end end
<%= form_with model: @person do |form| %> <%= text_field_with_label form, :first_name %> <% end %>
다음과 같이 대체할 수 있습니다:
<%= form_with model: @person, builder: LabellingFormBuilder do |form| %> <%= form.text_field :first_name %> <% end %>
LabellingFormBuilder
클래스를 다음과 같이 정의하여:
class LabellingFormBuilder < ActionView::Helpers::FormBuilder def text_field(attribute, options = {}) label(attribute) + super end end
이를 자주 재사용하려면 builder: LabellingFormBuilder
옵션을 자동으로 적용하는 labeled_form_with
헬퍼를 정의할 수 있습니다:
module ApplicationHelper def labeled_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block) options[:builder] = LabellingFormBuilder form_with model: model, scope: scope, url: url, format: format, **options, &block end end
사용된 폼 빌더는 다음과 같은 경우에도 결정합니다:
<%= render partial: f %>
f
가 ActionView::Helpers::FormBuilder
의 인스턴스인 경우 이 코드는 form
부분을 렌더링하고 부분의 객체를 폼 빌더로 설정합니다. 폼 빌더가 LabellingFormBuilder
클래스인 경우 labelling_form
부분이 대신 렌더링됩니다.
매개변수 명명 규칙 이해하기
폼에서 값은 params
해시의 최상위 수준 또는 다른 해시 내에 있을 수 있습니다. 예를 들어 Person 모델에 대한 표준 create
작업에서 params[:person]
은 일반적으로 생성할 사람의 모든 속성이 포함된 해시입니다. params
해시에는 배열, 해시 배열 등도 포함될 수 있습니다.
근본적으로 HTML 폼은 구조화된 데이터에 대해 알지 못하며, 모든 것이 이름-값 쌍으로 생성됩니다. 애플리케이션에서 볼 수 있는 배열과 해시는 Rails가 사용하는 매개변수 명명 규칙의 결과입니다.
기본 구조
두 가지 기본 구조는 배열과 해시입니다. 해시는 params
에서 값에 액세스하는 구문을 반영합니다. 예를 들어 폼에 다음이 포함되어 있다면:
<input id="person_name" name="person[name]" type="text" value="Henry"/>
params
해시에는 다음이 포함됩니다:
{ 'person' => { 'name' => 'Henry' } }
그리고 params[:person][:name]
을 사용하여 컨트롤러에서 제출된 값을 검색할 수 있습니다.
해시는 필요한 만큼 깊게 중첩될 수 있습니다. 예를 들어:
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
이렇게 하면 params
해시가 다음과 같이 됩니다:
{ 'person' => { 'address' => { 'city' => 'New York' } } }
일반적으로 Rails는 중복된 매개변수 이름을 무시합니다. 매개변수 이름이 빈 대괄호 []
로 끝나면 배열에 누적됩니다. 사용자가 여러 개의 전화 번호를 입력할 수 있게 하려면 다음과 같이 폼에 배치할 수 있습니다:
<input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/>
그러면 params[:person][:phone_number]
가 입력된 전화 번호가 포함된 배열이 됩니다.
이들을 결합하기
이 두 가지 개념을 혼합할 수 있습니다. 해시의 한 요소가 배열일 수 있고(이전 예제와 같이), 또는 해시 배열일 수 있습니다. 예를 들어 폼에서 다음 폼 조각을 반복하여 임의 수의 주소를 만들 수 있습니다:
<input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/> <input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/>
그러면 params[:person][:addresses]
가 line1
, line2
및매개변수 명명 규칙 이해하기
폼에서 값은 params
해시의 최상위 수준 또는 다른 해시 내에 있을 수 있습니다. 예를 들어 Person 모델에 대한 표준 create
작업에서 params[:person]
은 일반적으로 생성할 사람의 모든 속성이 포함된 해시입니다. params
해시에는 배열, 해시 배열 등도 포함될 수 있습니다.
근본적으로 HTML 폼은 구조화된 데이터에 대해 알지 못하며, 모든 것이 이름-값 쌍으로 생성됩니다. 애플리케이션에서 볼 수 있는 배열과 해시는 Rails가 사용하는 매개변수 명명 규칙의 결과입니다.
기본 구조
두 가지 기본 구조는 배열과 해시입니다. 해시는 params
에서 값에 액세스하는 구문을 반영합니다. 예를 들어 폼에 다음이 포함되어 있다면:
<input id="person_name" name="person[name]" type="text" value="Henry"/>
params
해시에는 다음이 포함됩니다:
{ 'person' => { 'name' => 'Henry' } }
그리고 params[:person][:name]
을 사용하여 컨트롤러에서 제출된 값을 검색할 수 있습니다.
해시는 필요한 만큼 깊게 중첩될 수 있습니다. 예를 들어:
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
이렇게 하면 params
해시가 다음과 같이 됩니다:
{ 'person' => { 'address' => { 'city' => 'New York' } } }
일반적으로 Rails는 중복된 매개변수 이름을 무시합니다. 매개변수 이름이 빈 대괄호 []
로 끝나면 배열에 누적됩니다. 사용자가 여러 개의 전화 번호를 입력할 수 있게 하려면 다음과 같이 폼에 배치할 수 있습니다:
<input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/>
그러면 params[:person][:phone_number]
가 입력된 전화 번호가 포함된 배열이 됩니다.
이들을 결합하기
이 두 가지 개념을 혼합할 수 있습니다. 해시의 한 요소가 배열일 수 있고(이전 예제와 같이), 또는 해시 배열일 수 있습니다. 예를 들어 폼에서 다음 폼 조각을 반복하여 임의 수의 주소를 만들 수 있습니다:
<input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/> <input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/>
그러면 params[:person][:addresses]
가 line1
, line2
및 city
키를 가진 해시 배열이 됩니다.
그러나 배열은 일반적으로 해시로 대체될 수 있습니다. 예를 들어 모델 객체의 배열 대신 ID, 인덱스 또는 다른 매개변수를 키로 하는 모델 객체의 해시를 가질 수 있습니다.
경고: 배열 매개변수는 check_box
헬퍼와 잘 작동하지 않습니다. HTML 사양에 따르면 체크되지 않은 체크박스는 값을 제출하지 않습니다. 그러나 체크박스가 항상 값을 제출하도록 하는 것이 편리한 경우가 많습니다. check_box
헬퍼는 이를 위해 동일한 이름의 보조 숨겨진 입력을 만듭니다. 체크박스가 체크되지 않은 경우 숨겨진 입력만 제출되고, 체크된 경우 둘 다 제출되지만 체크박스의 값이 우선합니다.
fields_for
헬퍼의 :index
옵션
사람의 주소 집합에 대한 필드를 렌더링하는 폼을 만들고 싶다고 가정해 봅시다. fields_for
의 :index
옵션을 사용하면 도움이 됩니다:
<%= 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| %> <%= address_form.text_field :city %> <% end %> <% end %> <% end %>
사람이 ID 23과 45의 두 개의 주소를 가지고 있다고 가정하면 위의 폼은 다음과 유사한 출력을 렌더링합니다:
<form accept-charset="UTF-8" action="/people/1" method="post"> <input name="_method" type="hidden" value="patch" /> <input id="person_name" name="person[name]" type="text" /> <input id="person_address_23_city" name="person[address][23][city]" type="text" /> <input id="person_address_45_city" name="person[address][45][city]" type="text" /> </form>
이렇게 하면 다음과 같은 params
해시가 생성됩니다:
{ "person" => { "name" => "Bob", "address" => { "23" => { "city" => "Paris" }, "45" => { "city" => "London" } } } }
fields_for
를 person_form
폼 빌더에서 호출했기 때문에 모든 폼 입력이 "person” 해시에 매핑됩니다. 또한 index: address.id
를 지정함으로써 각 도시 입력의 name
속성을 person[address][#{address.id}][city]
로 렌더링했습니다. 따라서 params
해시를 처리할 때 어떤 Address 레코드를 수정해야 할지 알 수 있습니다.
중요한 숫자 또는 문자열을 :index
옵션을 통해 전달할 수 있습니다. nil
을 전달할 수도 있는데, 이 경우 배열 매개변수가 생성됩니다.
더 복잡한 중첩을 만들려면 입력 이름의 선행 부분을 명시적으로 지정할 수 있습니다. 예를 들어:
<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %> <%= address_form.text_field :city %> <% end %>
다음과 같은 입력을 생성합니다:
<input id="person_address_primary_23_city" name="person[address][primary][23][city]" type="text" value="Paris" />
또한 text_field
등의 개별 입력 필드에 :index
옵션을 직접 전달할 수 있지만, 폼 빌더 수준에서 지정하는 것이 반복을 줄이는 데 일반적으로 더 효과적입니다.
일반적으로 최종 입력 이름은 fields_for
/ form_with
에 제공된 이름, :index
옵션 값 및 속성 이름의 연결입니다.
마지막으로, ID(예: index: address.id
)를 지정하는 대신 제공된 이름에 "[]"
를 추가하는 것이 단축 방법입니다. 예를 들어:
<%= fields_for 'person[address][primary][]', address do |address_form| %> <%= address_form.text_field :city %> <% end %>
이는 처음 예제와 정확히 동일한 출력을 생성합니다.
외부 리소스에 폼 전송하기
Rails의 폼 헬퍼는 외부 리소스에 데이터를 게시하는 폼을 구축하는 데에도 사용할 수 있습니다. 그러나 때때로 외부 리소스에 authenticity_token
을 설정해야 할 필요가 있습니다. 이는 form_with
옵션에 authenticity_token: 'your_external_token'
매개변수를 전달하여 수행할 수 있습니다:
<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %> 폼 내용 <% end %>
때때로 외부 리소스(예: 결제 게이트웨이)에 데이터를 제출할 때 외부 API에 의해 폼에 사용할 수 있는 필드가 제한되며 authenticity_token
을 생성하지 않는 것이 바람직할 수 있습니다. 토큰을 보내지 않으려면 :authenticity_token
옵션에 false
를 전달하면 됩니다:
<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %> 폼 내용 <% end %>
복잡한 폼 구축하기
많은 앱이 단순한 단일 객체 편집 폼을 넘어서 성장합니다. 예를 들어 Person
을 생성할 때 사용자가 (동일한 폼에서) 여러 주소 레코드(집, 직장 등)를 만들 수 있게 하려고 할 수 있습니다. 나중에 해당 사람을 편집할 때 사용자가 필요에 따라 주소를 추가, 제거 또는 수정할 수 있어야 합니다.
모델 구성
Active Record는 accepts_nested_attributes_for
메서드를 통해 모델 수준 지원을 제공합니다:
class Person < ApplicationRecord has_many :addresses, inverse_of: :person accepts_nested_attributes_for :addresses end class Address < ApplicationRecord belongs_to :person end
이렇게 하면 Person
에 addresses_attributes=
메서드가 생성되어 주소를 생성, 업데이트 및(선택적으로) 삭제할 수 있습니다.
중첩 폼
다음 폼을 사용하면 사용자가 Person
과 관련 주소를 생성할 수 있습니다.
<%= form_with model: @person do |form| %> 주소: <ul> <%= form.fields_for :addresses do |addresses_form| %> <li> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> <%= addresses_form.label :street %> <%= addresses_form.text_field :street %> ... </li> <% end %> </ul> <% end %>
중첩 속성을 허용하는 경우 fields_for
는 연결의 각 요소에 대해 한 번씩 블록을 렌더링합니다. 특히 사람에게 주소가 없는 경우 아무것도 렌더링하지 않습니다. 일반적인 패턴은 컨트롤러에서 하나 이상의 빈 자식을 빌드하여 사용자에게 최소한 하나의 필드 집합이 표시되도록 하는 것입니다. 아래 예제에서는 새 사람 폼에 2개의 주소 필드 집합이 렌더링됩니다.
def new @person = Person.new 2.times { @person.addresses.build } end
fields_for
는 폼 빌더를 생성합니다. 매개변수의 이름은 accepts_nested_attributes_for
가 예상하는 것과 일치합니다. 예를 들어 2개의 주소가 있는 사용자를 생성할 때 제출된 매개변수는 다음과 같습니다:
{ 'person' => { 'name' => 'John Doe', 'addresses_attributes' => { '0' => { 'kind' => 'Home', 'street' => '221b Baker Street' }, '1' => { 'kind' => 'Office', 'street' => '31 Spooner Street' } } } }
`addresses복잡한 폼 구축하기
많은 앱이 단순한 단일 객체 편집 폼을 넘어서 성장합니다. 예를 들어 Person
을 생성할 때 사용자가 (동일한 폼에서) 여러 주소 레코드(집, 직장 등)를 만들 수 있게 하려고 할 수 있습니다. 나중에 해당 사람을 편집할 때 사용자가 필요에 따라 주소를 추가, 제거 또는 수정할 수 있어야 합니다.
모델 구성
Active Record는 accepts_nested_attributes_for
메서드를 통해 모델 수준 지원을 제공합니다:
class Person < ApplicationRecord has_many :addresses, inverse_of: :person accepts_nested_attributes_for :addresses end class Address < ApplicationRecord belongs_to :person end
이렇게 하면 Person
에 addresses_attributes=
메서드가 생성되어 주소를 생성, 업데이트 및(선택적으로) 삭제할 수 있습니다.
중첩 폼
다음 폼을 사용하면 사용자가 Person
과 관련 주소를 생성할 수 있습니다.
<%= form_with model: @person do |form| %> 주소: <ul> <%= form.fields_for :addresses do |addresses_form| %> <li> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> <%= addresses_form.label :street %> <%= addresses_form.text_field :street %> ... </li> <% end %> </ul> <% end %>
중첩 속성을 허용하는 경우 fields_for
는 연결의 각 요소에 대해 한 번씩 블록을 렌더링합니다. 특히 사람에게 주소가 없는 경우 아무것도 렌더링하지 않습니다. 일반적인 패턴은 컨트롤러에서 하나 이상의 빈 자식을 빌드하여 사용자에게 최소한 하나의 필드 집합이 표시되도록 하는 것입니다. 아래 예제에서는 새 사람 폼에 2개의 주소 필드 집합이 렌더링됩니다.
def new @person = Person.new 2.times { @person.addresses.build } end
fields_for
는 폼 빌더를 생성합니다. 매개변수의 이름은 accepts_nested_attributes_for
가 예상하는 것과 일치합니다. 예를 들어 2개의 주소가 있는 사용자를 생성할 때 제출된 매개변수는 다음과 같습니다:
{ 'person' => { 'name' => 'John Doe', 'addresses_attributes' => { '0' => { 'kind' => 'Home', 'street' => '221b Baker Street' }, '1' => { 'kind' => 'Office', 'street' => '31 Spooner Street' } } } }
addresses_attributes
해시의 실제 키 값은 중요하지 않습니다. 그러나 각 주소에 대해 다른 문자열 정수가 필요합니다.
이미 저장된 관련 객체의 경우 fields_for
는 자동으로 저장된 레코드의 id
가 포함된 숨겨진 입력을 생성합니다. include_id: false
를 fields_for
에 전달하여 이를 비활성화할 수 있습니다.
컨트롤러
일반적으로 강력한 매개변수를 통해 컨트롤러에서 허용된 매개변수를 선언해야 합니다:
def create @person = Person.new(person_params) # ... end private def person_params params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street]) end
객체 제거
사용자가 관련 객체를 삭제할 수 있게 하려면 accepts_nested_attributes_for
에 allow_destroy: true
를 전달할 수 있습니다.
class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, allow_destroy: true end
속성 해시에 _destroy
키가 포함되어 있고 해당 값이 true
로 평가되면(예: 1, ‘1’, true 또는 ‘true’) 객체가 삭제됩니다.
이 폼을 사용하면 사용자가 주소를 제거할 수 있습니다:
<%= form_with model: @person do |form| %> 주소: <ul> <%= form.fields_for :addresses do |addresses_form| %> <li> <%= addresses_form.check_box :_destroy %> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> ... </li> <% end %> </ul> <% end %>
컨트롤러의 허용된 매개변수에 _destroy
필드를 포함하는 것을 잊지 마세요:
def person_params params.require(:person). permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy]) end
빈 레코드 방지하기
사용자가 작성하지 않은 필드 집합을 무시하는 것이 유용한 경우가 많습니다. accepts_nested_attributes_for
에 :reject_if
프로시저를 전달하여 이를 제어할 수 있습니다. 이 프로시저는 폼에서 제출된 각 속성 해시와 함께 호출됩니다. 프로시저가 true
를 반환하면 Active Record는 해당 해시에 대한 관련 객체를 빌드하지 않습니다. 다음 예에서는 kind
속성이 설정된 경우에만 주소를 빌드합니다.
class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, reject_if: lambda { |attributes| attributes['kind'].blank? } end
편의를 위해 :all_blank
기호를 전달할 수도 있는데, 이는 _destroy
값을 제외한 모든 속성이 비어 있는 경우 레코드를 거부하는 프로시저를 생성합니다.
실시간으로 필드 추가하기
여러 개의 필드 집합을 미리 렌더링하는 대신 사용자가 “새 주소 추가” 버튼을 클릭할 때만 추가하려고 할 수 있습니다. Rails에는 이를 지원하는 내장 기능이 없습니다. 새 필드 집합을 생성할 때는 관련 배열의 키가 고유해야 합니다. 현재 JavaScript 날짜(epoch 이후 밀리초)가 일반적인 선택입니다.
폼 빌더 없이 태그 헬퍼 사용하기
폼 빌더 컨텍스트 외부에서 폼 필드를 렌더링해야 하는 경우, Rails는 일반적인 폼 요소에 대한 태그 헬퍼를 제공합니다. 예를 들어 check_box_tag
:
<%= check_box_tag "accept" %>
출력:
<input type="checkbox" name="accept" id="accept" value="1" />
일반적으로 이러한 헬퍼는 폼 빌더 대응 부분과 동일한 이름에 _tag
접미사가 붙습니다. 전체 목록은 FormTagHelper
API 문서를 참조하십시오.
form_tag
및 form_for
사용하기
Rails 5.1에서 form_with
가 도입되기 전에는 그 기능이 form_tag
및 form_for
사이에 분할되었습니다. 이제 둘 다 soft-deprecated되었습니다. 사용 방법에 대한 문서는 이 가이드의 이전 버전에서 찾을 수 있습니다.