iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0
自我挑戰組

轉職道上的萌芽人生 − 自學程式開發ing系列 第 20

Day 20- 191006學習筆記 Express - 建立 Form - 新增

  • 分享至 

  • xImage
  •  

提醒:由於看到這系列鐵人訂閱人數漸漸變多,標記一下這些內容是在「非常萌新時期」所寫,更多技術內容請參考我的 Github,歡迎跟我一起討論 ^ ^


今天藉由昨天學習的方法,建立各種 form,用以更新 database,達到可以更改網站的呈現內容。(此範例將完成三個功能:新增、刪除、更新)


新增

以新增Genre的頁面為例

  • 首先需編寫想讓頁面呈現的 template。
    • POST 提交 form。
    • placeholder:可在輸入框顯示你想提示的文字。
    • 設定 value,讓第一次傳送 空白form,而當被 validator 擋住時,回傳的頁面能保留已輸入的內容。
    • 最後在下面顯示 validator 檢查到的錯誤訊息。
    extends layout
    block content
      h1 #{title}
      form(method='POST' action='')
        div.form-group
          label(for='name') Genre:
          input#name.form-control(type='text', placeholder='Fantasy, Poetry etc.' name='name' value=(undefined===genre ? '' : genre.name))
        button.btn.btn-primary(type='submit') Submit
      if errors 
       ul
        for error in errors
         li!= error.msg
    
  • GET:收到要新增 form 的 request時,導向其去回傳空白 form 的頁面。
    exports.genre_create_get = function (req, res, next) {
      res.render('genre_form', { title: 'Create Genre' })
    }
    
  • POST:當收到填好的 form POST 時,按照昨天提到的:
    • 先進行驗證清理
      • 需先輸入express-validator:
        const validator = require('express-validator')
    validator.body('name', 'Genre name required').isLength({ min: 1 }).trim(),
    validator.sanitizeBody('name').escape(),
    
    • 若有不合規定,則回傳錯誤訊息&保留已填欄位:
      • 以 validationResult 取得錯誤資訊。
      • 以已填資料來創建新的 Genre。
      • 帶入變數回傳保留已填資料的 form。
    (req, res, next) => {
      const errors = validator.validationResult(req)
      const genre = new Genre({ name: req.body.name })
      if (!errors.isEmpty()) {
        res.render('genre_form', { title: 'Create Genre', genre: genre, errors: errors.array()})
      return
      } else {... <save the result/> ...}}
    
    • 若符合規定,先檢查是否已存在該 Genre,若有則導向該頁面;若無,則存擋後導向新建頁面。
    Genre.findOne({ 'name': req.body.name })
    .exec( function(err, foundGenre) {
      if (err) { return next(err) }  
        if (foundGenre) { 
          res.redirect(foundGenre.url)
          }
        else {
          genre.save(function (err) {
            if (err) { return next(err) }
              res.redirect(genre.url)
            })}})
    
    • 因為 validator 是 middleware function,所以需注意其是以 array 方式給數個 callback function,其將會照順序執行。
    exports.genre_create_post = [
      validator.body( ... ),
      validator.sanitizeBody( ... ),
      (req, res, next) => { ... }
    ]
    
    • 完成後就可以新增 Genre 了:
      (空資料送出,則會在下方顯示錯誤訊息)
      https://ithelp.ithome.com.tw/upload/images/20191006/20120981cwmOsmVQnz.png
    • validator 也可直接輸入所需功能,直接使用:
    const { body,validationResult } = require('express-validator/check')
    const { sanitizeBody } = require('express-validator/filter')
     (...)
    body('name', 'Genre name required').isLength({ min: 1 }).trim(),
    sanitizeBody('name').escape(),
    
    • sanitizeBody('*').escape():也可使用*代替所有資料,一次清理。
    • 為驗證 genre,先確保他為 array,再以 genre.* 來清理所有 genre array 裡面的資料。(例如只選擇 0 或 1 個 genre 時,就不會是 array)
    (req, res, next) => {
      if (!(req.body.genre instanceof Array)) {
        if (typeof req.body.genre === 'undefined') { req.body.genre = [] } else { req.body.genre = new Array(req.body.genre) }
      }
      next()
    },
     ( ... )
    sanitizeBody('genre.*').escape(), 
    
    • Book 的作者欄,製作成下拉式選項:
      • input 改選用 select 替代,並將 type 設定為 select
      • selected:回傳錯誤時,可以將已選擇選項保留。
        (這邊要注意是現有的 author._id 不是 string,所以要先轉換過去)
        (但剛好這範例 validator 檢查的項目跟 form 的 required 重疊,頁面不會刷新,所以研究這段功能時,可先去掉 required,或是隨便加個別的檢查功能如:.isISO8601())
      for author in authors
        if book
          option(value=author._id selected=(author._id.toString()===book.author ? 'selected' : false) ) #{author.name}
        else
          option(value=author._id) #{author.name}
    
    https://ithelp.ithome.com.tw/upload/images/20191007/20120981AdtEFtrzqp.png
    • Book 的風格欄,製作成勾選式選項:
      • 將 type 設定為 checkbox
    div
      for genre in genres
        div(style='display: inline;')
          input(type='checkbox', name='genre', id=genre._id, value=genre._id, checked=genre.checked )
          label(for=genre._id) #{genre.name}
    
    https://ithelp.ithome.com.tw/upload/images/20191007/201209814TI5n3i3wf.png
    • 同樣為了回傳錯誤時,可以將已勾選選項保留,因此使用以下代碼來將已勾選的checked 設定成 true。
      • 這裡發現使用 sanitizeBody('*').escape() ,將會把 array 清到只剩一個項目,因此我改成一個一個分別 sanitizeBody。
        (p.s. 這部分因為我 debug 流程還是不夠熟悉,搞了好久@@ 覺得這份教材也頗用心,常在一些小地方安排了抓蟲練習XD )
      • 不過同樣的是,這範例 validator 檢查的項目跟 form 的 required 重疊,因此研究這段功能時,須先關掉 required。
    for (let i = 0; i < results.genres.length; i++) {
      if (book.genre.indexOf(results.genres[i]._id) > -1) {
        results.genres[i].checked = 'true'
      }
    }
    

上一篇
Day 19- 191005學習筆記 Express validator / form
下一篇
Day 21- 191007學習筆記 Express - 建立 Form - 更新、刪除
系列文
轉職道上的萌芽人生 − 自學程式開發ing30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言