Rails hidden_field について調べる(確認画面を作成して挙動確認)

こちらもなんとなくで意味を理解している、具体的な使い方のイメージができていない
の理由からしらべて記事を書くことにしました

##「hidden_field」とは
form_forやform_withの中で使用する
目に見えない値を送るイメージ

どうやら、「hidden_field_tag」ヘルパーもあるみたいだが
これらの違いはまた別の日に記事にする

##とりあえず、手を動かして使ってみる、準備編
ユーザーが投稿する画面を作って
投稿するまえに**投稿の内容の確認画面**を作成してみる

UserとPostのそれぞれのコントローラーとモデルを
作成してみる

```
~/environment/rails/sample_app
❯ rails g controller users 

~/environment/rails/sample_app
❯ rails g model User

~/environment/rails/sample_app
❯ rails g controller posts index new create

~/environment/rails/sample_app
❯ rails g model Post

~/environment/rails/sample_app
❯ rails db:migrate
```

###アソシエーション

```rb:app/model/user.rb
class User < ApplicationRecord
  has_many :posts
end
```

```rb:app/model/post.rb
class User < ApplicationRecord
  has_many :user
end
```

###初期データを入れる
以前、自分が書いた記事をみつつ
UserとPostの初期データを入れる
[以前書いた初期データを入れる記事](https://qiita.com/pank24ever/items/d7402c5be108ceb8db84)

```rb:db/seeds.rb
test_user = User.create!(
  name: 'test',
  email: 'test@mail.com',
  password: 'password'
)

Post.create!(
  title: 'test title',
  contents: '投稿のテストです',
  user_id: test_user.id
)

Post.create!(
  title: 'test title2',
  contents: '投稿のテスト、2回めです',
  user_id: test_user.id
)
```

下記のコマンドで実行

```
rails db:seed
```

###一覧画面を作成

コントローラーの記載
何度もユーザーの取得を宣言するのは、面倒なので
privateメソッドで記述しておく

```rb:app/controller/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_user

  def index
    @posts = Post.where(user_id: @user.id)
  end

  private

  def set_user
    @user = User.find(1)
  end
end
```

viewの作成

```rb:app/views/posts/index.html.erb
<h1>投稿一覧画面</h1>

<% @posts.each do |post| %>
  <p><%= post.title %></p>
  <p><%= post.contents %></p>
<% end %>
```

ルーティング定義

```rb:config/routes.rb
Rails.application.routes.draw do
  get 'posts/index'
end
```

###投稿作成画面を作成

こちらもコントローラーの記載から

```rb:app/controller/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_user

  def index
    @posts = Post.where(user_id: @user.id)
  end

  def new
    @posts = Post.new
  end

  def create
    @posts = Post.create(
      title: params[:post][:title],
      contents: params[:post][:contents],
      user_id: @user.id
    )

    if @posts.save
      redirect_to posts_index_path
    else
      render :new
    end
  end

  private

  def set_user
    @user = User.find(1)
  end
end
```

「`params[:title]`」として取得しようとしてつまずいた
パラメータの確認
**params[:モデル名][:渡したname属性]**で取得する

viewの作成

```rb:app/views/posts/new.html.erb
<h1>投稿新規作成画面</h1>

<%= form_with(model: @posts, url: posts_create_path, local: true) do |f| %>
  <div id="container">
    <%= f.text_field :title %>
    <%= f.text_area :contents %>

    <%= f.submit %>
  </div>
<% end %>
```

ルーティング定義

```rb:config/routes.rb
Rails.application.routes.draw do
  get 'posts/index'
  # 追加
  get 'posts/new'
  post 'posts/create'
end
```

これでまずは普通のユーザーが投稿して
一覧画面までできた

##いよいよ確認画面を作成する
Postコントローラーに`確認画面`用の「**confirmアクション**」を追加する

```rb:app/controller/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_user
  # 追加
  before_action :posts_params

  def index
    @posts = Post.where(user_id: @user.id)
  end

  def new
    @posts = Post.new
  end

  # 確認画面用のアクション 追加
  def confirm
    @posts = Post.new(
      title: params[:post][:title],
      contents: params[:post][:contents]
    )
   
    # エラーが発生した場合true、エラーが無い場合falseを返す
    if @posts.valid?
      render :confirm
    else
      render :new
    end
  end
  
  # 削除
  def create
    @posts = Post.create(
      title: params[:post][:title],
      contents: params[:post][:contents],
      user_id: @user.id
    )

    if @posts.save
      redirect_to posts_index_path
    else
      render :new
    end
  end

  private

  def set_user
    @user = User.find(1)
  end

  # ストロングパラメーター追加
  def posts_params
    params.require(:post).permit(:title, :contents)
  end
end
```

####「valid?」「invalid?」メソッド
こちらにも違いがあるみたい

「**valid?**」
バリデーションが実行された結果
エラーが**無い場合true**を返し，エラーが**発生した場合false**を返す

「**invalid?**」はその逆

ルーティングの定義から

```rb:config/routes.rb
Rails.application.routes.draw do
  get 'posts/index'
  get 'posts/new'
  # 削除
  post 'posts/create'
  # 追加
  post 'posts/confirm'
end
```

view作成

```
~/environment/rails/sample_app
❯ touch app/views/posts/confirm.html.erb
```

viewを修正していく
でも、まずは確認画面へ行くかテストしたいので

```rb:app/views/posts/confirm.html.erb
  <h1>成功</h1>
```

だけ記入
フォームを送信した際に、URLが
`localhost:3000/posts/confirm`になっていて
画面に「成功」の画面がでていればOK

###さらに細かく実装していく
アクションを追加する

```rb:app/controller/posts_controller.rb
class PostsController < ApplicationController
  # ~割愛~

  # 確認画面用のアクション
  def confirm
    @posts = Post.new(
      title: params[:post][:title],
      contents: params[:post][:contents]
    )
   
    # エラーが発生した場合true、エラーが無い場合falseを返す
    # 後置ifで記述
    render :confirm if @posts.valid?
  end

  
  # ~割愛~
end
```

viewを作成していく

```rb:app/views/posts/confirm.html.erb
<h1>確認画面</h1>

<%= form_with(model: @posts, url: posts_complete_path, local: true) do |f| %>
  <div id="container">
    # ここで登場、「hidden_field」
    <%= f.hidden_field :title %>
    <%= @posts.title%>

    <%= f.hidden_field :contents %>
    <%= @posts.contents%>

    # パラメーターに対して名前をつけることができる、name属性
    <%= f.submit '戻る', name: 'back' %>
    <%= f.submit '送信する' %>
  </div>
<% end %>
```

フォームの送信先に少し手こずる
・form_withは、デフォルトで「POST」で送信する
・コントローラーでcreateを使用してオブジェクトを作成するとnewとsaveを
行ってくれる
→そのため、フォームが「PATCH」で送信される
 →「PATCH」はすでにオブジェクトがある場合に送信される

**完了画面**用のアクションを追加する

```rb:app/controller/posts_controller.rb
class PostsController < ApplicationController
  # ~割愛~

  # 確認画面用のアクション
  def confirm
    @posts = Post.new(
      title: params[:post][:title],
      contents: params[:post][:contents]
    )
   
    # エラーが発生した場合true、エラーが無い場合falseを返す
    # 後置ifで記述
    render :confirm if @posts.valid?
  end

  # 完了画面用のアクション
  def complete
    @posts = Post.new(
      title: params[:post][:title],
      contents: params[:post][:contents],
      user_id: @user.id
    )

    # パラメーターに[:back]がついている(戻るボタン)をクリック
    # もしくは、オブジェクトが保存されなかったら「new」の画面を戻す
    render :new if params[:back] || !@posts.save
  end

  # ~割愛~
end
```

これで、**投稿→確認→完了**の画面遷移までができる
投稿の簡易アプリが完成

##戻るボタンを押した際に使用される「hidden_field」

作成したview

```rb:app/views/posts/confirm.html.erb
<h1>確認画面</h1>

<%= form_with(model: @posts, url: posts_complete_path, local: true) do |f| %>
  <div id="container">
    # ここで登場、「hidden_field」
    <%= f.hidden_field :title %>
    <%= @posts.title%>
```

作成されているHTML

```
<input type="text" name="post[title]" id="post_title">

内容を入力して、戻るボタンを押す
<input type="text" value="テスト タイトル" name="post[title]" id="post_title">
``` 

「**value**」の中に`入力した値`がはいっている
ユーザが直接フォームから入力させないまま、値を受け渡したい時に使用する

##参考記事
https://railsguides.jp/action_view_overview.html
https://railsdoc.com/page/hidden_field
https://qiita.com/___xxx_/items/7c9b194230c7ce2b02ca
https://remonote.jp/rails-confirm-form
https://web-begginer-log.com/rails-confirm/