0
0

More than 1 year has passed since last update.

rails内(簡易チャットアプリ)でjavascriptを使った際にエラーが出た時の対処フロウ【自分用】

Posted at

簡易チャットアプリ(rails)で起こったJSのエラー

Image from Gyazo
①非同期通信でメッセージを送信すると、current_userの名前が「undefined」になる。
②imageがnullの時、ビューに表示されないための条件式(三項演算子)を以下のように書いたにも関わらず、imageが表示されてしまう。

message.js
function buildHTML(message) {
    var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";

    var html = `<div class='message'>
                  <div class='message-info'>
                    <div class='message-info__name'>
                      ${message.name}
                    </div>
                    <div class='message-info__date'>
                      ${message.created_at}
                    </div>
                  </div>
                  <div class='message-text'>
                    <p class='message-text__content'>
                      ${message.content}
                    </p>
                      ${image}
                  </div>
                </div>`
    return html;
  }

しかし、リロードするとちゃんと表示される。

③メッセージを更新するためのアクションをapp/controllers/api/messages_controller.rb内に実装し、それをリクエストされるように、message.js内でreloadMessagesという名前の関数にして、呼び出すようにした。

しかし、failメソッドが作動しconsoleにはerrorが表示される。

message.js
var reloadMessages = function () {
    //カスタムデータ属性を利用し、ブラウザに表示されている最新メッセージのidを取得
    var last_message_id = $('.message').last().data("id");
    console.log(last_message_id)
    var group_id = $(".current-group").data("group-id");
    console.log(group_id)
    $.ajax({
        //ルーティングで設定した通りのURLを指定
        url: '/groups/' + group_id + '/api/messages',
        //ルーティングで設定した通りhttpメソッドをgetに指定
        type: 'get',
        dataType: 'json',
        //dataオプションでリクエストに値を含める
        data: {
          id: last_message_id
        }
      })
      .done(function (messages) {
        console.log('success');
      })
      .fail(function () {
        console.log('error');
      });
  };
  reloadMessages();

Image from Gyazo

実際にデバッグ作業をしてみる

①リロード前の非同期通信で起こるメッセージ送信後のエラーなので、message.jsを確認する。

message.js
$(function () {
  function buildHTML(message) {
    var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";

    var html = `<div class='message'>
                  <div class='message-info'>
                    <div class='message-info__name'>
                      ${message.name}
                    </div>
                    <div class='message-info__date'>
                      ${message.created_at}
                    </div>
                  </div>
                  <div class='message-text'>
                    <p class='message-text__content'>
                      ${message.content}
                    </p>
                      ${image}
                  </div>
                </div>`
    return html;
  }
  $('#new_message').on('submit', function (e) {
    e.preventDefault();
    var formData = new FormData(this);
    var url = $(this).attr('action')
    $.ajax({
        url: url,
        type: "POST",
        data: formData,
        dataType: 'json',
        processData: false,
        contentType: false
      })
      .done(function (data) {
        console.log(data)
        var html = buildHTML(data);
        $('.messages').append(html);
        $('.messages').animate({
          scrollTop: $('.messages')[0].scrollHeight
        }, 'fast');
        $(".form__submit-btn").prop('disabled', false);
        $("#new_message")[0].reset();
      })
      .fail(function (data) {
        alert('error');
      });
  });

  var reloadMessages = function () {
    //カスタムデータ属性を利用し、ブラウザに表示されている最新メッセージのidを取得
    var last_message_id = $('.message').last().data("id");
    console.log(last_message_id)
    var group_id = $(".current-group").data("group-id");
    console.log(group_id)
    $.ajax({
        //ルーティングで設定した通りのURLを指定
        url: '/groups/' + group_id + '/api/messages',
        //ルーティングで設定した通りhttpメソッドをgetに指定
        type: 'get',
        dataType: 'json',
        //dataオプションでリクエストに値を含める
        data: {
          id: last_message_id
        }
      })
      .done(function (messages) {
        console.log('success');
      })
      .fail(function () {
        console.log('error');
      });
  };
  reloadMessages();
});

ビューを表示しているのは、message.jsの「var html = buildHTML(data);」および、呼び出し元の「function buildHTML(message)の箇所。

バッククオート(``)を使ったテンプレートリテラル記法で書かれたHTML内の、式展開(${})に送られているデータが怪しそうと判断。データ元は「$('#new_message').on('submit', function (e)」の中にあるdoneメソッドの引数のdata。

message.js
.done(function (data) {
  console.log(data)
  var html = buildHTML(data);
  $('.messages').append(html);
message.js
function buildHTML(message) {
    var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";

    var html = `<div class='message'>
                  <div class='message-info'>
                    <div class='message-info__name'>
                      ${message.name}
                    </div>
                    <div class='message-info__date'>
                      ${message.created_at}
                    </div>
                  </div>
                  <div class='message-text'>
                    <p class='message-text__content'>
                      ${message.content}
                    </p>
                      ${image}
                  </div>
                </div>`
    return html;
  }

②console.logでdataの中身を確認

「.done(function (data) {」にすぐ下にconsole.log(data)を記述。引数にdataを入れることで、dataの中身を確認。

message.js
.done(function (data) {
  console.log(data)
  var html = buildHTML(data);
  $('.messages').append(html);

chromeのデベロッパーツールで、consoleを確認すると...

Image from Gyazo

Image from Gyazo

dataは取得されていた。それぞれのキーバリューのキーの名前と、message.js内のビューを表示している部分を見比べると、「user_name」が${message.name}とドットの後の名前が違っていた。

message.js
function buildHTML(message) {
    var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";

    var html = `<div class='message'>
                  <div class='message-info'>
                    <div class='message-info__name'>
                      ${message.name}  
                    </div>

create.json.jbuilderの名前を確認してみる。すると、user_nameを変数名にしていた!

create.json.jbuilder
json.(@message, :content, :image)
json.created_at @message.created_at
json.user_name @message.user.name
json.id @message.id

create.json.jbuilderの「user_name」を「name」にしても直るが、「user_name」の方が他の人が見ても分かり易く親切。

なので、message.jsの式展開の方を「${message.name}」から「${message.user_name}」に変更して、①の問題は完了。

Image from Gyazo

③imageが表示されないように条件式を書いている箇所を確認

続いて、②のimageがnullの時、ビューに表示されないための条件式(三項演算子)を以下のように書いたにも関わらず、imageが表示されてしまう問題。

message.js
var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";

consoleで先ほど同様、doneメソッド内に送られているdataを見てみる。

Image from Gyazo

imageの中にはurlがある...。つまり、三項演算子で書かれたif文では、「message.image」となっているので、trueが返ってきてしまう。正しくは「message.image.url」のように、messageの中のimageの中にurl中に値がある場合の条件にする。

message.js
 var image = message.image.url ? `<img class="message-text__image" src=${message.image.url}>` : "";

これで②の問題は解決。

④.failメソッドが動いているので非同期通信はうまくいっている。dataを送っているコントローラーが怪しい

一応、変数の「var last_message_id」と「var group_id」の下にそれぞれconsole.logを置いたが、値は送られていた。

doneメソッドにdataをjson形式で送っているapp/controllers/api/messages_controller.rbのindex下にbinding.pryを記述し、paramsの中身をターミナルで確認する。

app/controllers/api/messages_controller.rb
class Api::MessagesController < ApplicationController
  def index
    binding.pry
    @group = Group.find(params[:group_id])
    @messages = @group.messages.where('id > ?, params[:last_message_id]')
    respond_to do |format|
      format.json
    end
  end
end
[9] pry(#<Api::MessagesController>)> params
=> <ActionController::Parameters {"id"=>"284", "format"=>"json", "controller"=>"api/messages", "action"=>"index", "group_id"=>"37"} permitted: false>

app/controllers/api/messages_controller.rbのwhereメソッドで記入している「:last_message_id(最新のメッセージ)」が無いことが確認。ターミナル内の「"id"=>"284"」が本来whereメソッド内に書くべきキーだった。

なので、「params[:last_message_id]」を「params[:id]」に変更した。

binding.pryの記述を消して、ブラウザを確認してみるもまだerrorが出る。

Image from Gyazo

⑤consoleのエラーURLを開いてエラーを確認

Image from Gyazo

エラー文を確認すると、mysqlでsyntaxエラー(構文エラー)が起こっていることがわかった。

ActiveRecord::StatementInvalid in Api::Messages#index

Showing /Users/riku27/projects/chat-space/app/views/api/messages/index.json.jbuilder where line #1 raised:
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?, params[:id])' at line 1: SELECT `messages`.* FROM `messages` WHERE `messages`.`group_id` = 37 AND (id > ?, params[:id])

その中でも以下のエラー文に注目すると、(id > ?, params[:id])が値に置き換えされていないのがわかる。

at line 1: SELECT `messages`.* FROM `messages` WHERE `messages`.`group_id` = 37 AND (id > ?, params[:id])

それを踏まえて、app/controllers/api/messages_controller.rbのwhereメソッドをよくみると...

app/controllers/api/messages_controller.rb
@messages = @group.messages.where('id > ?, params[:id]')

本来、where('id > ?', params[:id])のように、id > ?のみをシングルクォーテーションで囲わないといけないところ、params[:id]まで囲ってしまったので、文字列として認識されてしまっていた。

これを修正して、全ての問題解決。

デバックをする上でこういう風に考えろと言われたこと

①databaseにデータを渡す前に起こったエラー(DBには保存されていない)なのか
②databaseにあるデータをもらった時に起こったエラーなのか(主にコントローラーに原因ある)

でDBを見て線引きする。

 最後に

僕が教えてもらったことを自分用メモとして、覚えている範囲で殴り書きしました。かなりまとまりが悪くなってしまっているので、読んでいただいた方には申し訳ないと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0