簡易チャットアプリ(rails)で起こったJSのエラー
①非同期通信でメッセージを送信すると、current_userの名前が「undefined」になる。
②imageがnullの時、ビューに表示されないための条件式(三項演算子)を以下のように書いたにも関わらず、imageが表示されてしまう。
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が表示される。
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を確認する。
$(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。
.done(function (data) {
console.log(data)
var html = buildHTML(data);
$('.messages').append(html);
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の中身を確認。
.done(function (data) {
console.log(data)
var html = buildHTML(data);
$('.messages').append(html);
chromeのデベロッパーツールで、consoleを確認すると...
dataは取得されていた。それぞれのキーバリューのキーの名前と、message.js内のビューを表示している部分を見比べると、「user_name」が${message.name}とドットの後の名前が違っていた。
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を変数名にしていた!
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が表示されないように条件式を書いている箇所を確認
続いて、②のimageがnullの時、ビューに表示されないための条件式(三項演算子)を以下のように書いたにも関わらず、imageが表示されてしまう問題。
var image = message.image ? `<img class="message-text__image" src=${message.image}>` : "";
consoleで先ほど同様、doneメソッド内に送られているdataを見てみる。
imageの中にはurlがある...。つまり、三項演算子で書かれたif文では、「message.image」となっているので、trueが返ってきてしまう。正しくは「message.image.url」のように、messageの中のimageの中にurl中に値がある場合の条件にする。
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の中身をターミナルで確認する。
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が出る。
⑤consoleのエラーURLを開いてエラーを確認
エラー文を確認すると、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メソッドをよくみると...
@messages = @group.messages.where('id > ?, params[:id]')
本来、where('id > ?', params[:id])のように、id > ?のみをシングルクォーテーションで囲わないといけないところ、params[:id]まで囲ってしまったので、文字列として認識されてしまっていた。
これを修正して、全ての問題解決。
デバックをする上でこういう風に考えろと言われたこと
①databaseにデータを渡す前に起こったエラー(DBには保存されていない)なのか
②databaseにあるデータをもらった時に起こったエラーなのか(主にコントローラーに原因ある)
でDBを見て線引きする。
## 最後に
僕が教えてもらったことを自分用メモとして、覚えている範囲で殴り書きしました。かなりまとまりが悪くなってしまっているので、読んでいただいた方には申し訳ないと思います!