はじめに
コピペ禁止です。
コマンド、コードはコピぺしないで作成してください。
気になった用語があれば各自で調べる。
投稿したデータを取得してみる。
前回、フォームからリクエストを送りサーバーサイドからデータベースにデータを挿入することができたので
挿入したデータを取得してみようと思います。
routes/index.js
を書き換えます。
// 中略/* GET home page. */router.get('/',function(req,res,next){varquery='SELECT *, to_char(created_at, \'YYYY年MM月DD日 HH24時MI分SS秒\') AS created_at FROM board';pool.connect(function(err,client){client.query(query,function(err,result){console.log(result.rows);res.render('index',{title:'Express'});});});});// 中略
ソースコードを変更した後、/
にアクセスするとデータが取得できているはずです。
取得できたので、このデータをViewに渡しましょう。
routes/index.js
を変更します。
// 中略/* GET home page. */router.get('/',function(req,res,next){varquery='SELECT *, to_char(created_at, \'YYYY年MM月DD日 HH24時MI分SS秒\') AS created_at FROM board';pool.connect(function(err,client){client.query(query,function(err,result){res.render('index',{title:'はじめてのNode.js',boardList:result.rows});});});});// 中略
titleと同じような感じで、boardList
というkeyでresult.rows
を渡しています。
取得したデータを表示させる。
データも取得できViewに渡せたので、View側で取得した値を展開しましょう。views/index.js
を以下のように書き換えます。
<!-- 中略 --></form><ulclass="main-list"><%boardList.forEach(function(boardItem){%><liclass="main-list__item"><divclass="board"><pclass="board__title"><%=boardItem.title%></p><pclass="board__date"><%=boardItem.created_at%></p></div></li><%});%></ul></div><!-- 中略 -->
View側に入れるが渡された時バイアに配列をforEach(function() {});
というメソッドを使って展開してきます。
forEach内のcallback関数の第一引数には配列内のオブジェクトが一つずつ入ってます。
今回は、boardList
という配列を一つずつboardItem
というオブジェクトに展開しています。
forEach内部で<%= boardItem.title %>
や<%= boardItem.created_at %>
のようにしてやればいいだけです。
ちゃんと表示できています。
詳細ページをつくる。
新規ページを作るので最初にapp.js
にルーティングの設定をします。app.js
を書き換えます。
// 中略varusersRouter=require('./routes/users');varboard=require('./routes/board');// ←追加varapp=express();// 中略app.use('/users',users);app.use('/board',board);// ←追加// catch 404 and forward to error handlerapp.use(function(req,res,next){// 中略
この後に作るroutes/borad.js
を/board
というURLに紐づける処理をしています。
routes/borad.js
を作成します。
varexpress=require('express');varrouter=express.Router();varpool=require('../dbConnection');router.get('/:board_id',function(req,res,next){varboardId=req.params.board_id;varquery="SELECT * FROM board WHERE board_id = "+boardId;pool.connect(function(err,client){client.query(query,function(err,board){console.log(err)res.render('board',{title:board.rows[0].title,board:board.rows[0],});});});});module.exports=router;
routes/board.js
内の処理でrouter.get('/:board_id',...
があるですが、どんなURLに紐づくかを説明していきます。
はい、ざっくりこんな感じです。app.js
と個別のjsの両方を使ってルーティングしているのがわかると思います。
views/board.ejs
を作成します。
<!DOCTYPE html><htmllang="ja"><head><title><%=title%></title><metahttp-equiv="X-UA-Compatible"content="IE=edge,chrome=1"><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><metaname="format-detection"content="telephone=no"><linkrel='stylesheet'href='/stylesheets/style.css'></head><body><divclass="wrapper"><pclass="main-title"><%=title%></p><ahref="/"class="btn">トップへもどる</a></div></body></html>
http://localhost:3000/boards/1
にアクセスしてみてください。
エラーなく表示されたらOKです。これが詳細ページになります。
メッセージを投稿する
詳細ページにメッセージを投稿するフォームを設置しましょう。views/board.ejs
を書き換えます。
<!-- 中略 --><pclass="main-title"><%=title%></p><formaction="/board/<%= board.board_id %>"method="post"class="board-form"><inputtype="text"name="message"class="input"required><buttontype="submit"class="submit">投稿</button></form><!-- 中略 -->
routes/board.js
を書き換えます。
varexpress=require('express');varrouter=express.Router();varmoment=require('moment');//追加varpool=require('../dbConnection');router.get('/:board_id',function(req,res,next){varboardId=req.params.board_id;varquery="SELECT * FROM board WHERE board_id = "+boardId;pool.connect(function(err,client){client.query(query,function(err,board){res.render('board',{title:board.rows[0].title,board:board.rows[0],});});});});router.post('/:board_id',function(req,res,next){varmessage=req.body.message;varboardId=req.params.board_id;varcreatedAt=moment().format('YYYY-MM-DD HH:mm:ss');varquery="INSERT INTO messages (message, board_id, created_at) VALUES ('"+message+"', "+"'"+boardId+"', "+"'"+createdAt+"')";pool.connect(function(err,client){client.query(query,function(err,rows){res.redirect('/board/'+boardId);});});});module.exports=router;
ここのロジックは前回やったものと同じなので説明は割愛します。
formからmessageをpostすると、しっかりデーターベースに格納されていますね。
この格納されているmessageを画面に表示させてみましょう。
routes/board.js
`を書き換えます。
// 中略router.get('/:board_id',function(req,res,next){varboardId=req.params.board_id;vargetBoardQuery="SELECT * FROM board WHERE board_id = "+boardId;// 変更vargetMessagesQuery="SELECT *, to_char(created_at, \'YYYY年MM月DD日 HH24時MI分SS秒\') AS created_at FROM messages WHERE board_id = "+boardId;// 追加pool.connect(function(err,client){client.query(getBoardQuery,function(err,board){client.query(getMessagesQuery,function(err,messages){res.render('board',{title:board.rows[0].title,board:board.rows[0],messageList:messages.rows});});});});});// 中略
はい、気持ち悪いコードですね、これには理由があり非同期処理が混ざっているからです。
本来なら
varboard,messages;pool.connect(function(err,client){client.query(getBoardQuery,function(err,board){board=board.rows;});client.query(getMessagesQuery,function(err,board){messages=messages.rows;});res.render('board',{title:board.rows[0].title,board:board.rows[0],messageList:messages.rows});});
といった形にするのが綺麗ですが、client.query();
は非同期処理なので、このように書くとエラーになります。
非同期処理はとは?
webブラウザは基本的に、JavaScriptコードを実行する時、上から一行ずつ実行します。
関数を呼び出す時も、呼び出した関数の処理が終わるまで(return文で呼び出し下の関数に戻ってくるまで)
次の行には進みません。
他にも、JavaScriptコードを実行中は他の処理になる。
例えば、マウスクリックイベントをイベントハンドラで処理している間にユーザーがキー入力を行っても、画面に入力した
文字は反映されず、キー入力に対するイベントハンドラも実行されない。(マウスクリックのハンドラの実行が終わった後に実行されます。)
要するに、JavaScriptは上から順番に処理が走ります。
varboard,messages;// 1pool.connect(function(err,client){// 2client.query(getBoardQuery,function(err,board){// 3board=board.rows;// 4});client.query(getMessagesQuery,function(err,board){// 5messages=messages.rows;// 6});res.render('board',{// 7title:board.rows[0].title,board:board.rows[0],messageList:messages.rows});});
順番に処理するとなるとこうなりそうだとイメージがつきますが、非同期処理なのでこうはならず、以下のようになります。
varboard,messages;// 1pool.connect(function(err,client){// 2client.query(getBoardQuery,function(err,board){// 3 (実行するが、いつ処理終わるかわからん)board=board.rows;// 6か7 (いつ処理おわるかわかんないから6、7のどっちになるかわかんないよ)});client.query(getMessagesQuery,function(err,board){// 4 (実行するが、いつ処理終わるかわからん)messages=messages.rows;// 6か7 (いつ処理おわるかわかんないから6、7のどっちになるかわかんないよ)});res.render('board',{// 5title:board.rows[0].title,// 5の時点では処理終わってないのでnudefinedboard:board.rows[0],// 5の時点では処理終わってないのでnudefinedmessageList:messages.rows// 5の時点では処理終わってないのでnudefined});});
3と4が非同期処理です。この処理がいつ終わるかわからないので、5の時点ではboard.rows[0]
やmessages.rows
には値が格納されないのでエラーになります。
routes/board.js
に書いたコードをみてみます。
pool.connect(function(err,client){// 1client.query(getBoardQuery,function(err,board){// 2client.query(getMessagesQuery,function(err,messages){// 3(2の処理いつ終わるかわからないけど、終わったらこれ実行して)res.render('board',{// 4(3の処理いつ終わるかわからないけど、終わったらこれ実行して)title:board.rows[0].title,board:board.rows[0],// 2の処理終わってるからboard.rows[0]は存在するよmessageList:messages.rows// 3の処理終わってるからmessages.rowsは存在するよ});});});});
こんな感じで実行しています。非同期処理はインデントが深くなりやすいのですが、解決する方法はたくさんあります。JavaScriptををやる限り非同期処理とは仲良くしないと行けないです。
Viewにデータは渡せているので、views/board.ejs
を書き換えます。
<!-- 中略 --><pclass="main-title"><%=title%></p><%if(messageList.length){%><divclass="white-bg"><ulclass="main-list"><%messageList.forEach(function(messageItem){%><liclass="main-list__item"><divclass="message"><pclass="message__title"><%=messageItem.message%></p><pclass="message__date"><%=messageItem.created_at%></p></div></li><%});%></ul></div><%}%><formaction="/boards/<%= board.board_id %>"method="post"class="board-form"><!-- 中略 -->
<% if (messageList.length) { %>
を使いました。
これはメッセージが一件でもある場合のみリストを表示させています。
これを使ってブロックしたユーザーを表示にしたり、ログインしているユーザーだけを表示するなど、場合分けができます。
表示されていたら完成です。
URLで直接アクセスするのは面倒なので、メインぺージのボードをクリックで飛べるようにしたいので
views/index.ejs
を書き換えます。
<liclass="main-list__item"><ahref="/boards/<%= boardItem.board_id %>"class="board"><pclass="board__title"><%=boardItem.title%></p><pclass="board__date"><%=boardItem.created_at%></p></a></li>
これで設定ができました。これで遷移すると思います。
今回は以上です、次はユーザー登録とログイン機能を作ります。