iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 18
0

接著今天按照教材步驟,藉由昨天學習的方法,建立基礎 template,並完成 Controller 回應 get request 的內容,完成呈現 read-only 的頁面。


LocalLibrary base template

  • 首先,昨天學到可以把建立好的 template 帶入其他 template 使用。所以第一步先來建立一個基礎 template 於 /views/layout.pug,以供所有頁面使用。(extendsblock
  • 這邊將基礎的頁面設計為: 側邊欄(當作連覽列,提供連結到各頁面)+ 主區域(用以呈現各個頁面)。
  • 目前這部分先簡單的使用 Bootstrap 框架,並藉以達成初步 RWD。(因此需在 head 中連結 bootstrap 的 css、js)
  • 最後需新增 側邊欄 的基本css設定在 /public/stylesheets/style.css

Home page

(/controllers/bookController.js 中的 index)

  • 首頁基本的頁面設計為:除了標題跟一些內文介紹外,最主要的部分是要列出一個目錄,並提供各項目的 現況(如:有多少藏書、有多少作者的書、多少書在館..等等)
  • 目標:在 controller 中新增「計數」功能,並在 template 中使用 controller 回傳的結果,來呈現在頁面。
  • countDocuments({}, callback):Mongoose 的功能,可用來計數,計算 參數1 設定的條件,並由 callback 回傳數量(或error)。
    (若參數1為空object,則不設條件計算所有文件。)
  • 而這個時機點,即可使用 async.parallel() 來讓所有資料同時動作。
  • 最後再一次性由 res.render(),帶入資料到 template 輸出頁面。
  • template 上可以 ifelse 新增「錯誤提示訊息」。

List page

  • List page 基本的頁面設計為:列出該 collection 現有的所有資料,各 collection 再外加其他以下資訊。
    books:作者 / authors:生卒年 / book-instances:狀態、歸還日期)
  • 實踐的函式基本結構:1.以find()找出符合條件的資料 2.以exec()將資料帶進callback執行 3.callback 中以render()將資料以變數帶進 template 呈現在頁面中。
  • 以自我挑戰的 Genre List 為例:
    /controllers/genreController.js
    • 因為我們要所有的'Genre'資料,所以'find()'不需填參數
    • 成功後,使用'render()'將結果以'listGenre'帶入,並以'genre_list'模板呈現
exports.genre_list = function (req, res, next) {
  Genre.find()
    .exec(function (err, listGenre) {
      if (err) { return next(err) }
      res.render('genre_list', { title: 'Genre List', genre_list: listGenre })})}
  • genre_list.pug
    • 從'content'開始銜接'layout'模板
    • 以'name'的字母順序排列(其中轉大寫,以避免大小寫造成順序不同)
    • 以each-in-else直接帶入所有資料建立列表,並在沒有資料時傳送訊息提示
extends layout
block content
  h1= title
  ul
    - genre_list.sort(function(a, b) {let textA = a.name.toUpperCase(); let textB = b.name.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;});
    each genre in genre_list
      li 
        a(href=genre.url) #{genre.name}
    else
      li There are no genres.
  • 完成後就可以在 All genres 看到以下頁面:
    https://ithelp.ithome.com.tw/upload/images/20191005/20120981T13qmnlgiR.png
  • 其中需要呈現日期的部分,可使用 moment 模組,將日期轉換成更好讀的呈現方式:
    ./models/author.js 為例:
    • 首先需輸入moment模組
    • 新增一個 virtual 屬性,以 moment 設定日期樣式。
AuthorSchema
  .virtual('birthDay')
  .get(function () {
    if (this.date_of_birth) {
      return moment(this.date_of_birth).format('MMMM Do, YYYY')
    } else {
      return ''
    }
  })
  • 完成後,All authors 頁面上的日期就變得較易讀:
    https://ithelp.ithome.com.tw/upload/images/20191006/20120981iFlbJXCTcn.png

Detail page

  • Detail page 基本需求:該項目的標題內容。(好像廢話 也就是說,要提供給 標題&內容 的資料來源,是放在不同 collection 中,因此這邊跟 Home page 一樣,需要從不同來源撈資料,就會需要用到非同步。)
  • 實踐的函式基本結構:1.以async.parallel()同時撈各個來源的資料 2.以findById()給予配對的資訊 3.以req.params.id來取得 request 所配對的 id 4.以render()將資料以變數帶進 template 呈現在頁面中。
  • 以 Author Detail 為例:
    /controllers/authorController.js
    • 因為兩個資料沒有先後順序、也不互相依賴,因此選用 async.parallel() 同時進行。
    • 利用 req.params.id 取得ID。(req.params 可取得路徑參數,路徑上以冒號表示的部分)
    • 用 ID 來配對取的互相關聯的資料。
    • 最後 render() 帶入 template 做出畫面。
    • 另外可能碰到資料不存在(如剛好碰到剛刪除該資料),因此這邊添加一個錯誤訊息,並以next(err),以 err 為參數帶到 app.js 中的下一個 middleware 執行,使其對 error 進行處理。(原本都只執行到 app.use('/catalog', catalogRouter) 這個 middleware,現在使用 next 使其跳入下一個。)
exports.author_detail = function (req, res, next) {
  async.parallel({
    author: function (callback) {
      Author.findById(req.params.id)
        .exec(callback)
    },
    authors_books: function (callback) {
      Book.find({ author: req.params.id }, 'title summary')
        .exec(callback)
    }
  }, function (err, results) {
    if (err) { return next(err) } 
    if (results.author === null) { 
      const err = new Error('Author not found')
      err.status = 404
      return next(err)
    }
    res.render('author_detail', { title: 'Author Detail', author: results.author, author_books: results.authors_books })
  })
}

上一篇
Day 17- 191003學習筆記 async、Pug、moment
下一篇
Day 19- 191005學習筆記 Express validator / form
系列文
轉職道上的萌芽人生 − 自學程式開發ing30

尚未有邦友留言

立即登入留言