前言
在上一篇筆記中我們了解到要如何利用if & each 兩個helper去幫助我們做出動態的樣板,雖然很方便卻無法滿足我們所有的需求,畢竟if helper不能做更複雜的條件判斷,這時候就輪到客製helper(custom helper)出手了!
在介紹用法之前先試想一下一個情境,今天你在做一個記帳的程式,每一筆紀錄包含著以下的資訊
- 消費項目名稱
- 消費金額
- 消費日期
- 消費類別(假設是食、衣、住、行)
為了在頁面上呈現消費紀錄,你從資料庫抓資料出來並利用each將每一筆資料印出,一切都很順利,你做出類似這樣的效果。

現在問題出現了,光看項目名稱你看不出消費的類別,而你又不想傻傻的在頁面上直接寫個"類別:食",你希望在每筆消費旁邊增加對應的圖案,很明顯你學到的兩個helper幫不上忙,只好自己寫一個囉!
客製helper(custom helper)
官方文件中也有提到如何客製想要的helper,我個人認為那個做法有些不直觀,所以我後來找了另一種我比較喜歡的做法,可以達成一模一樣的效果。
首先回到app.js,我們修改一下設定樣板引擎(view engine)的部份。
app.js
//加入粗體字的部分 app.engine('handlebars', exphbs({ defaultLayout: 'main', helpers: { add_up: function (a, b) { return a + b } } })) app.set('view engine', 'handlebars')
還記得我們一開始設置樣板時,我們利用了handlebars其中一個option:defaultLayout 去告訴它說我們預設的主頁面是main.handlebars嗎? helpers其實也是眾多option之一,你可以在這邊用你習慣的方式設立helper function,我這邊簡單寫了一個addUp 函數來做示範。
接著回到樣板的位置,我們測試一下剛建立的addUp是否正常運作。
views/index.handlebars
/* ......... some codes here ......... */ <h1>{{add_up 5 9}}</h1>
再次打開頁面你應該就會看到下方出現了數字14。

這邊有幾個重點需要注意一下。
- 客製的helper不需要#開頭以及/結尾,可直接使用
當然你仍可以用一般的helper方式,並在中間夾入你要的HTML結構
以addUp為例:
{{#addUp}}
{{this}}
{{/addUp}} - 傳入的arguments用空格隔開而非一般的逗號
了解用法後我們就可以回頭解決我們的問題了,由於解決的邏輯不是本篇的重點,這邊就只提供程式碼跟粗略的說明,圖示部分使用了fontawesome做出預期的效果。
app.js
// 增加取得圖示的helper app.engine('handlebars', exphbs({ defaultLayout: 'main', helpers: { getImage: function (category, category_image) { return category_image[category] }, add_up: function (a, b) { return a + b } } })) app.get('/', (req, res) => { //建立圖示物件 const category_image = { '食': '<i class="fas fa-utensils"></i>', '衣': '<i class="fas fa-tshirt"></i>', '住': '<i class="fas fa-home"></i>', '行': '<i class="fas fa-bus"></i>', '其他': '<i class="fas fa-shopping-bag"></i>' } //建立測試用的消費紀錄 const records = [ { name: "生鮮食品", amount: 1200, date: '2019-04-14', category: '食' }, { name: "婚禮西裝", amount: 1800, date: '2019-04-15', category: '衣' }, { name: "線上學費", amount: 3000, date: '2019-04-13', category: '其他' } ] //將消費紀錄與圖示物件傳入樣板中 res.render('index', { records, category_image }) })
views/index.handlebars
<ul>
{{#each records}}
//注意這邊傳入的兩個arugment,由於現在是在each的迴圈中 直接使用 category_image會報錯(畢竟在records中並沒有category_image)
{{{getImage this.category ../category_image}}}
<li>
{{this.name}}
{{this.date}}
金額:{{this.amount}}元
Delete
Edit
</li>
{{/each}}
</ul>
一切順利的話你會看到以下的成品~一切都如我們預期的美好:D。

結語
這篇文章中我們介紹了設立以及使用客製helper,舉的例子只是使用的其中一種情況!因應你開發的項目不同可以有更多不同的應用。當然你也可以使用官方的做法,同樣的東西你會寫成類似下方的東西。
const Handlebars = require('handlebars') Handlebars.registerHelper('addUp', function (options) { return options.fn(this) + 5 });
我想你可以理解為什麼我偏好文章中的方法了吧:P~!
本文章的程式碼可以在這裡取得,歡迎自由使用。