今天,來到鐵人賽第二十九天。
你以為,明天就結束了嗎?
安可,安可。
我們來看看今天的進度吧!
前二天,我們已經把 TODO List 的 Model 及 View的部份搞定了。
今天,要利用 Controller 將這二方做一個互通有無。
其實,在node.js 的 Controller ,就是其 web 應用程式 app.js 的三二事,及其router要做的事情而已。我們將分別來看看。
還記得,我們昨天在做View的時候,為了方便起見,做了一個簡單的 app.js 嗎?
找到這一段:
app.js
現在,我們要把它抽出來,變成一個router檔,讓app.js 引入router檔案,而不寫在一起!
所以,上面的三行程式,會變成這樣:
app.js
接著,開新檔案,將上面第10行~ 第14行,複製,貼上新檔案,取名 todo.js ,放入 ./routes/ 資料夾底下,這就是我們的router檔案!!
./routes/todo.js
var express=require('express');
var router = express.Router();
var dataset=require('./recordset.js');
router.get('/todo',function(req,res){
res.render('restfulTP',{itemlist:dataset});
});
module.exports=router;
而原本的 app.js 將變成這樣:
app.js
var express = require('express');
var todoRouter= require('./routes/todo');
var app = express();
//set view engine
app.set("view engine","jade")
//set view directory
app.set("views",__dirname+"/views")
app.use('/restful', todoRouter);
app.use('/restful',express.static(__dirname+'/public'));
app.listen(3000,function(){
console.log('Ready...for 3000');
});
我們可以試著執行 node app.js,並且打開瀏覽器:
依舊,可以正常執行!!!
瞧!當我們把router,提取出來,這樣程式是不是更清楚明瞭?!
接下來,我們來增加一些東西,在app.js !
為了要正確的接收表單傳過來的 data ,別忘了,我們之前有學過,要引入 body-parser!
所以,開頭加上
var bodyParser = require( 'body-parser' );
並且加入
// configure app to use bodyParser()
// this will let us get the data from Request
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.text());
最後,完成的 app.js 會像這樣:
app.js
var express = require('express');
var bodyParser = require( 'body-parser' );
var todoRouter= require('./routes/todo');
var app = express();
//var dataset=require('./recordset.js'); //資料集...方便測試View流程使用
//set view engine
app.set("view engine","jade")
//set view directory
app.set("views",__dirname+"/views")
// configure app to use bodyParser()
// this will let us get the data from Request
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.text());
// Apply this router on (/restful)
app.use('/restful', todoRouter);
app.use('/restful',express.static(__dirname+'/public'));
app.listen(3000,function(){
console.log('Ready...for 3000');
});
再次,試著執行 node app.js,並且打開瀏覽器,看是否正常執行?
(我們只是簡單做了引入body-parser模組,並且使用它的middleware而已!當然可以囉!)
好了, 我們已經完成了 app.js 的相關引入及設定,接下來,沒你(app.js)的事了!
之後,都是 router 的事了!!(哇!!是不是很簡單呢?)
當初,我們的 router,引入了 ./recordset.js 這個資料集檔。
當時,只是為了測試view,而做的!
現在,我們不用它了!而是,直接引入我們之前在 model 做好的 CRUD 檔案,如下:
./routes/todo.js
// load mongodb-CURL
var modelCreate=require('../model/todocreatedb.js');
var modelUpdate=require('../model/todoupdatedb.js');
var modelRemove=require('../model/todoremovedb.js');
var modelQuery=require('../model/todoquerydb.js');
接著,為了測試每個路由的進入,小編加了載入中介軟體函式:(這段可有可無..)
./routes/todo.js
// middleware that is specific to this router
router.use(function(req, res, next) {
console.log('Something is happening.');
next(); // make sure we go to the next routes and don't stop here
});
接著,還記得,我們按鈕要做的事嗎?開始吧.......................
./routes/todo.js
// READ ALL & FORM (/restful/todo)
router.get('/todo',function(req,res){
//mongodb find all.....
modelQuery.QueryGet({},function(record){
if(req.xhr)
res.render('recordTP',{layout:false, itemlist:record});
else
res.render('restfulTP',{itemlist:record});
});
});
因為是載入全部,所以,我們的搜尋的條件資料集是 {}。
這邊,有二個render ,其中一個有用 layout,代表第一次載入。
其他時候,則只單純 改變 partial的部份,所以,不用 layout!
要特別注意的是,我們按下搜尋,如果,沒有填資料,代表路由是走 /todo,而非 /todo/:id ,在同樣GET的情況下,會跑到這裡執行。
./routes/todo.js
// CREATE (/restful/todo)
router.post('/todo', function(req, res) {
// ...
var dataset=[{message:req.body.momsg}];
modelCreate.InsertNew(dataset,function(msg){
return res.redirect('/restful/todo');
});
//res.send('you push a request to create');
});
我們接收,從表單送出的 req.body.momsg,也就是 message,轉成資料集,送給 InserNew方法,做新增!
做完新增,回到 /restful/todo 路由!(載入全部資料)
./routes/todo.js
// READ (/restful/todo/:id)
// 這邊要注意的:這裡的id是網址列後的搜尋字串
// 在用 req.params是根據路由給的參數名稱, 與req.body的不同處!
// 如果你怕會搞混, 請修改!
router.get('/todo/:id', function(req, res) {
// mongodb find one or all...
var dataset={message:req.params.id}
modelQuery.QueryGet(dataset,function(record){
if(req.xhr)
res.render('recordTP',{layout:false, itemlist:record});
else
res.render('restfulTP',{itemlist:record});
});
//res.send('you push a request to read one');
});
我們接收從URL接收過來的 req.params.id 做為關鍵字, 依此做查詢的條件,轉成資料集,送給 QueryGet 方法,做查詢!
再將結果 render 給 recordTP 或是 restfulTP。
這邊,會與載入全部資料的部份有些雷同之處!
要注意的是,如果,搜尋的關鍵字部份留下空白,則不會到此路由運行。
因為,我們這裡的路由是 /todo/:id,如果參數部份留下空白,則直接去 /todo。
./routes/todo.js
// UPDATE ((/restful/todo/:id))
router.put('/todo/:id', function(req, res) {
// ...
var dataset={id:parseInt(req.params.id),message:req.body.momsg};
modelUpdate.UpdateSave(dataset,function(record){
res.render('oneTP',{layout:false,
oneid:record.id,onemsg:record.message});
});
//res.send('you push a request to put! ' + req.body.moid+req.body.momsg);
});
這邊,我們將從表單送回來,要更新的訊息 req.body.momsg 也就是 message,以及 URL 所帶的 req.params.id 也就是 id。
轉成資料集,送回給UpdateSave,做該筆id資料的更新!
再將結果,render 給 oneTP view。
./routes/todo.js
// DELETE (/restful/todo/:id)
router.delete('/todo/:id', function(req, res) {
// ...
var dataset={id:parseInt(req.params.id)}
//console.log(dataset);
modelRemove.RemoveSave(dataset,function(msg){
res.send(msg);
});
});
這邊,我們將從URL接收過來的id,即要被刪除的id,轉成資料集,送回給 RemoveSave方法,做該筆資料的刪除。再回傳 ‘刪除成功’ 之類的相關訊息給前端。
別忘了,最後要加 module.exports=router
; 以提供給 app.js使用!
以上,是我們的路由器撰寫,完成!
最後,完成的 router檔,會像這樣:
./routes/todo.js
var express=require('express');
var router = express.Router();
// load mongodb-CURL
var modelCreate=require('../model/todocreatedb.js');
var modelUpdate=require('../model/todoupdatedb.js');
var modelRemove=require('../model/todoremovedb.js');
var modelQuery=require('../model/todoquerydb.js');
// middleware that is specific to this router
router.use(function(req, res, next) {
console.log('Something is happening.');
next(); // make sure we go to the next routes and don't stop here
});
// READ ALL & FORM (/restful/todo)
router.get("/todo", function(req,res){
//mongodb find all.....
modelQuery.QueryGet({},function(record){
if(req.xhr)
res.render("recordTP",{layout:false, itemlist:record});
else
res.render("restfulTP",{itemlist:record});
});
});
// CREATE (/restful/todo)
router.post("/todo", function(req, res) {
// ...
var dataset=[{message:req.body.momsg}];
modelCreate.InsertNew(dataset,function(msg){
return res.redirect("/restful/todo");
});
//res.send("you push a request to create");
});
// READ (/restful/todo/:id)
// 這邊要注意的:這裡的id是網址列後的搜尋字串
// 在用 req.params是根據路由給的參數名稱, 與req.body的不同處!
// 如果你怕會搞混, 請修改!
router.get("/todo/:id", function(req, res) {
// mongodb find one or all...
var dataset={message:req.params.id}
modelQuery.QueryGet(dataset,function(record){
if(req.xhr)
res.render("recordTP",{layout:false, itemlist:record});
else
res.render("restfulTP",{itemlist:record});
});
//res.send("you push a request to read one");
});
// UPDATE ((/restful/todo/:id))
router.put("/todo/:id", function(req, res) {
// ...
var dataset={id:parseInt(req.params.id),message:req.body.momsg};
modelUpdate.UpdateSave(dataset,function(record){
res.render("oneTP",{layout:false,
oneid:record.id,onemsg:record.message});
});
//res.send("you push a request to put! " + req.body.moid+req.body.momsg);
});
// DELETE (/restful/todo/:id)
router.delete("/todo/:id", function(req, res) {
// ...
var dataset={id:parseInt(req.params.id)}
//console.log(dataset);
modelRemove.RemoveSave(dataset,function(msg){
res.send(msg);
});
});
module.exports=router;
你們看,在router裡,我們也只是做負責**“控制資料"** 傳進至Model處理 與 送出 給View呈現。
我們並沒有在裡面做任何有關商業邏輯的事情,也沒有在裡面draw任何的html。
完全符合MVC 在做Controller,也必須注意到的 關注點分離
。
在Controller,有二個檔案,一個是主要的 app.js,另外,則是router檔 todo.js。
瞧,是不是用 RESTful架構 及 MVC,把專案清楚容易的完成了?
最後,可以執行 node app.js,開始把玩這個 TODO List了!
Demo畫面:
https://github.com/circleuniv/todo