RailsとFormData

![eyecatch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/871827/9972a2a9-c7ff-48fe-abdc-39550d742499.jpeg)
# Rails and FormData

Railsと[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)についての記事です。英語と日本語で両方書きます。

This post talks about using WebAPI's  [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) with Rails.

最近[RailsのForm](https://railsguides.jp/form_helpers.html)からAPIを基にしたシステムに交換しました。つまり、Formの代わりにXHRのリクエスト（[axios](https://github.com/axios/axios))でバックエンドと連携しました。

We recently migrated from using [Rails forms](https://railsguides.jp/form_helpers.html) to an API based app where data is sent to the backend using XHR requests (in our case, using [axios](https://github.com/axios/axios)).

この記事で典型的なユーザモデルを使用します。

For the following example I will be using a generic User model.

## 問題・Problem 

### RailsのFormを使用・Using Rails Form

```
<%= form_for @user, url: {action: "create"} do |f| %>
  <%= f.text_field :name %> # "Yokoyama"
  <%= f.text_field :role %> # "Developer"
  <%= f.submit "Create" %>
<% end %>
```

RailsのFormを利用したら、controllerに以下のparamsがあります。

If we were using Rails forms then we'd normally get:

```ruby
def create
 # params = <ActionController::Parameters
 # {
 #  "user" => {
 #    "name"=>"Yokoyama",
 #	  "role"=>"Developer"
 #  },
 #	"controller"=>"...",
 #	"action"=>"create",
 # }
 # permitted: false>
  ...
end
```

### FormDataを使用・Using FormData

リクエストボディーを作ると[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)を利用しました。

When packaging a body for a POST/PATCH request we used [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData).

```js

const formData = new FormData();

formData.append('name','Yokoyama');
formData.append('role','Developer');

const url = 'user create link';
axios.post(url, formData, {
            headers: {
              'content-type': 'multipart/form-data',
            },
          })
...
    
```

RailsのControllerに下記レスポンスがあります。

On the Rails backend we get:

```ruby
def create
 # params = <ActionController::Parameters
 # {
 #  "name"=>"Yokoyama",
 #	"role"=>"Developer",
 #	"controller"=>"...",
 #	"action"=>"create",
 # }
 # permitted: false>
  ...
end
```
キーポイント：
- 属性はトップレベルのため、同名前の問題が起こる可能性があります。
- Railsのパタンではありません。

As you can see: 
- All attributes are scoped to the top level, which will cause issues if there are name conflicts
- It's also just against the typical Rails pattern

## 解決・Solution

もともと[@jugtuttle on Medium](https://medium.com/@jugtuttle/formdata-and-strong-params-ruby-on-rails-react-c230d050e26e)が書いた解決ですが、モデルの名前をキーに含まれたら成功です。

[@jugtuttle on Medium](https://medium.com/@jugtuttle/formdata-and-strong-params-ruby-on-rails-react-c230d050e26e) pointed out that you can simply use a key that has the model name. So in our case:

```jsx
const formData = new FormData();

formData.append('user[name]','Yokoyama');
formData.append('user[role]','Developer');

axios.post('insert user path here', formData, {
            headers: {
              'content-type': 'multipart/form-data',
            },
          })
...
    
```

より簡単にするために：

To make it even easier:

```js

// rails_form_data.js

class RailsFormData extends FormData {
  constructor(model) {
    super();
    this.model = model;
  }

  append(key, value) {
    super.append(`${this.model}[${key}]`, value);
  }
}

export default RailsFormData;
```

```jsx
import RailsFormData from '...'

const formData = new RailsFormData('user');

formData.append('name','Yokoyama');
formData.append('role','Developer');

axios.post('insert user path here', formData, {
            headers: {
              'content-type': 'multipart/form-data',
            },
          })
...
    
```

## 結果・Result

Railsのcontrollerには以下状態があります。

Then on the Rails backend side you'll get: 

```ruby
def create
  #  params = <ActionController::Parameters
  #  {
  #    "user" => {
  #      "name"=>"Yokoyama",
  #	     "role"=>"Developer"
  #    },
  #	   "controller"=>"...",
  #	   "action"=>"create",
  # }
  # permitted: false>
  ...
end
```