rails 非同期通信

今回はシンプルな投稿機能をrailsでjbuilderを使用して非同期通信を実装する

自分はjbuilderを使用した実装方法しかわからない、、

コードも綺麗とは言えませんが、自分が遭遇したエラーも何個か書いていこうと思うので

誰かの助けになればと思います


いずれjbuilderを使わないパターンやjs.erbを使ったパターンとの比較をしていきたい



##完成品
[![Image from Gyazo](https://i.gyazo.com/18041ab0e4dfa7ee277c4746c6c7db42.gif)](https://gyazo.com/18041ab0e4dfa7ee277c4746c6c7db42)


##環境
ruby：2.5.1
rails：2.5.3
DB：mysql(Sequel Pro)
ブラウザ：Google
OS：Mac（10.14.6）


##Github
https://github.com/tana1818/ajax


##作成手順

####開発ファイルの作成
```ruby:ターミナルで置きたいディレクトリに移動してから
rails new ajax -d mysql  #ここではajaxって名前のアプリにするけどなんでもいい
```
＊補足

```ruby:ターミナルで置きたいディレクトリに移動してから
rails new ajax
```
でもいいけど、このコマンドでアプリを作成するとDBがSQLiteで設定されたものが作られ、DBをSQLiteじゃないもの使っていた場合、この後入力するコマンドを打つとエラーが起こる。

そもそもrails newをする際にも色んなオプションをつけてアプリを作成することができる

今回は自分がmysqlを使用する為、最初の時点でDBの設定をmysqlに設定してコマンドを実行する

因みにもしオプションをつけずにコマンドを実行したらもう一度作成し直すか、config/database.ymlとGemfileをいじれば設定できる

↓先ほど作成したajaxのディレクトリで下記のコマンドを順に入力

```ruby:ajax
bundle install #gemをインストール
rake db:create #DB作成
rails g scaffold Fruit name:string  #scaffoldを使用してコントローラ、モデル、ビュー、を全部作成
rake db:migrate  #先ほどscaffoldで作成したマイグレーションファイルを実行する
```
scaffoldでほとんどを作成
楽してすいません、、、

一応どんなファイルが作られるかだけ知っておきましょう
[【Rails】 Scaffoldの使い方](https://remonote.jp/how-to-use-scaffold)

```ruby:ajax
rails s  #ローカルで開発用サーバーを起動する（これまでのコマンドがうまくいってるか確認　 Googleでhttp://localhost:3000にアクセス）
```
http://localhost:3000

うまくいっていれば下図のような画面が出てくる

![スクリーンショット 2019-10-14 14.32.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349277/d99c78ee-439a-e882-31ad-e808aecf0f4c.png)

http://localhost:3000/fruits
を検索すると

![スクリーンショット 2019-10-14 15.28.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349277/d62b3c82-cd68-ecb3-12ea-b4330809433f.png)
この画面が表示されるはず
　
####ルーティングの設定
とりあえずルート画面を上図の画面に設定

```ruby:config/routes.rb
Rails.application.routes.draw do
  root to: 'fruits#index'
  resources :fruits
end
```
　
####hamlとbootstrapをインストール
次にgemをインストールしてhamlとbootstrapを使えるようにする

```ruby:Gemfile
gem 'haml-rails'
gem 'bootstrap-sass'
```
　
hamlを使ってるうちにhtmlが書けなくなっていった為hamlを使用します、、
↓しっかりgemをインストール

```ruby:ajax
bundle install
```
　
次にhtmlをhamlに変換するコマンドを入力

```ruby:ajax
bundle exec rails haml:erb2haml
```
　
erbファイルは消していいよね？って出るのでyを入力

```ruby:ajax
Would you like to delete the original .erb files? (This is not recommended unless you are under version control.) (y/n)
```
　
こんなが出たらOK

```ruby:ajax
Task complete!
No .erb files found. Task will now exit.
```
　
次にbootstorapを使う準備をする

①ファイル名変更
application.css　→　application.scss
　

②application.jsに追記

```ruby:assets/stylesheets/application.js
#上記省略
//
//= require rails-ujs
//= require activestorage
//= require bootstrap #追記
//= require turbolinks
//= require_tree .
```
　
③application.scss

```ruby:assets/javascripts/application.scss
@import "bootstrap"; 
```
　
④scaffolds.scssのコメント化
scaffolds.scssの記述を全てコメント化、ファイルごと削除してもいい！
　
####コントローラの設定

indexとcreateとdestroyアクションの記述変更

```ruby:controllers/fruites.controller.rb
  def index
    @fruits = Fruit.all
    @fruit = Fruit.new  #追記
  end

  def create
    @fruit = Fruit.create(fruit_params)
      if @fruit.save
        respond_to do |format|
          format.html { redirect_to :root ,notice: 'fruits was successfully created!!!'}
          format.json
        end
      end
  end

  def destroy
    respond_to do |format|
      if @fruit.destroy
        format.html { redirect_to root_path, notice: 'フルーツ は destroyed.' }
        format.json { head :no_content }
      else
        format.html{redirect_to root_path, notice: '削除できてねえええええ'}
      end
    end
  end
```
　
####ビューのページの記述変更

①index.html.haml編集

```ruby:views/fruits/index.html.haml
.container 
  .row
    %h1 Listing fruit
    - if flash[:notice]
      %p= flash[:notice]

  .row
    %table.table.table-bordered.table-sortable
      %thead
        %tr
          %th Name
          %th
          %th
          %th
      %tbody.fruit_item
        - @fruits.each do |fruit|
          %tr.item
            %td
              = fruit.name
            %td
              = link_to 'Show', fruit
            %td
              = link_to 'Edit', edit_fruit_path(fruit)
            %td
              = link_to 'Destroy', fruit, data: {confirm: 'Are you sure?'}, method: :delete
    
  .row
    %h1 New fruit
    = render 'form'
```

②_form.html.haml編集

```ruby:views/fruits/_form.html.haml
= form_for @fruit, class: 'a' do |f|
  .field
    = f.label :name
    = f.text_field :name, class: 'b'
    = f.submit 'Save', class: 'c'
```

今の所うまくいっていれば下記のようになる
[![Image from Gyazo](https://i.gyazo.com/66a7560c568220c827c30d4be5c789be.gif)](https://gyazo.com/66a7560c568220c827c30d4be5c789be)
　
####非同期通信の実装
①jqueryをインストール

```ruby:Gemfile
#上記省略
gem 'jquery-rails'
```
　
②application.jsに追記

```ruby:assets/stylesheets/application.js
#上記省略
//
//= require rails-ujs
//= require activestorage
//= require bootstrap
//= require jquery #追記
//= require turbolinks
//= require_tree .
```
　
③create.json.jbuilderのファイル作成

```jbuilder:views/fruits/create.json.jbuilder
json.id @fruit.id
json.name @fruit.name
```
必要な値は「id」「フルーツの名前」なのでそれぞれをキーとしたjsonデータを生成するため上記のように追記
　

④fruits.jsのファイル作成

```javascript:assets/javascripts/fruits.js
$(function(){
  function buildHTML(fruit){ //通信が成功するとdoneメソッドの引数にデータが入るようになっているため、これを利用してHTMLを組み立てる
    var html = `<tr class = "item ui-sortable-handle">
                  <td>
                    ${fruit.name}
                  </td>
                  <td>
                    <a href = /fruits/${fruit.id}>Show</a>
                  </td>
                  <td>
                    <a href = /fruits/${fruit.id}/edit>Edit</a>
                  </td>
                  <td>
                  <a data-confirm = "Are you sure?", rel = "nofollow", href = /fruits/${fruit.id}, data-method = "DELETE">Destroy</a>
                  </td>
                </tr>`
    return html;
  }
  $('#new_fruit').on('submit', function(e){ //'#new_fruit'の'submit'が押された時に発火
    e.preventDefault(); //これを書いてるせいで'submit'を押した際に要素にdisabledが付与される→最後の.alwasが書いてある行を足すことでそのdisabled要素を削除してる
    var formData = new FormData(this); //FormDataオブジェクトの引数はthisとなってる。イベントで設定したfunction内でthisを利用した場合はイベントが発生したDOM要素を指す。今回であればnew_commentというIDがついたフォームの情報を取得している
    $.ajax({
      url: "/fruits/", //ここはアクションのURLなのでrails routesで確認
      type: "POST", //ここはアクション名
      data: formData,
      dataType: 'json',
      processData: false,
      contentType: false
    })
    .done(function(data){ //非同期通信の結果として返ってくるデータは、done(function(data) { 処理 })の関数の引数で受け取る
      var html = buildHTML(data);
      $('.fruit_item').append(html) //htmlに追加
      $('.b').val('') //なんかエラー出るなーと思ったらvarと書き間違えていた（ここでは'submit'を押した後テキストボックスに空の要素を付与してる）
    })
    .fail(function(){ //エラーが置きた際
      alert('投稿できませんでした')
    })
    .always(function(){ //この記述を書いてないと連続で投稿できない
      $(".c").removeAttr("disabled")
    })
  })
});
```

以上で実装は終了
再度サーバーを立ち上げ直して

```ruby:ajax
rails s
```
　
うまくいっていればくるっとした通信のマークが何も動かないはずです
![スクリーンショット 2019-10-15 23.54.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/349277/72a937f0-bf91-fb5e-1d39-c47b0ac648fe.png)

　
うまく実装できました？
_form.html.hamlでクラス名'a'とか'b'とか使ってましたけど、めんどくさくてつけなかっただけでちゃんと名前つけてください笑
決して真似しちゃいかん気がする

まだコード自体、綺麗にできる余地はあるものの一旦これであげたいと思います
指摘事項あれば教えて頂けると嬉しいです

今後、完成品のコードもgithubにあげていこうと思うので少々お待ちを
それでは

はてなブログもやっているので是非！
https://tanagram18.hatenablog.com/