Handlebars自定義的寫法

handlebars相對來講算一個輕量級、高性能的模板引擎,因其簡單、直觀、不污染HTML的特性,我個人特別喜歡。另一方面,handlebars作爲一個logicless的模板,不支持特別複雜的表達式、語句,只內置了一些基本的語法,像if、each這些。可惜的是就連if都十分弱,只能判斷值是否爲true/false,或轉化後是否爲true/false,不能對值進行比較。不過,handlebars提供了自定義helper的能力,通過自定義helper,可以實現非常豐富的功能。本篇來總結一下handlebars註冊helper都有哪些方式,以及一些相關的知識。

helper大概可以分爲兩類,一類是用於格式化輸出數據,使用起來像這樣:{{formatDate date}},官方沒有給起名字,我姑且叫做簡單helper好了。另一類叫塊級helper,塊級helper有自己的作用域,可以拿到上下文數據,並可以定義渲染的內容,可以發揮的作用就比較大了。通過這兩類helper,handlebars由一個弱邏輯的模板可以擴展出很強大的功能。通過registerHelper方法,我們便可以註冊一個helper。下面先看一下簡單helper。

簡單helper

簡單helper主要用來對數據進行格式化,例如我們經常會格式化日期、數字、金額等等。看一個例子就明白了,此處我寫一個把數字進行千分位分割的helper,所謂千分位分割就是把123456789這樣的值格式化爲123,456,789.代碼如下:

Handlebars.registerHelper('formatnumber', function(num, options){
          num = num + '';
          return num.replace(/(?=(?!^)(?:\d{3})+(?:\.|$))(\d{3}(\.\d+$)?)/g,',$1');
     });

然後就可以在模板中使用:

{{formatnumber num}}

registerHelper的第一個參數是helper的名稱,我這裏把它叫做formatnumber,第二個參數是一個函數,該函數傳入的第一個參數就是我們在使用helper時候的值,如上面的num,最後,函數return的內容就是我們模板中輸出的內容。此外還會傳入第二個參數options,options是一個對象,包含上下文相關的一些信息,不過在簡單helper中用不到,我們會下面在塊級helper中詳細說說。

一個簡單helper的定義就是如此簡單,真如其名~

塊級helper

塊級helper的能力就強大很多,可以實現一些自己想要的迭代器,或者增強判斷語句等。主要依賴的就是這個options參數。下面通過一個例子來說明一下。

handlebars的if語句只能進行true/false判斷,如果我們想判斷一個數字是否是偶數,我這麼寫是不可以的:{{#if num%2 == 0}},if不支持表達式,也不支持==這樣的操作符。所以要想在模板中判斷偶數,我們需要定義一個helper。代碼如下:

//判斷是否是偶數
Handlebars.registerHelper('if_even', function(value, options) {
	console.log('value:', value); // value: 2
	console.log('this:', this); // this: Object {num: 2}
	console.log('fn(this):', options.fn(this)); // fn(this): 2是偶數
	if((value % 2) == 0) {
		return options.fn(this);
	} else {
		return options.inverse(this);
	}
});

然後我們造一個數據,寫在模板中來看看:

var data3 = {
    num : 2
}

模板中:

{{#if_even num}}
      {{this.num}}是偶數
{{else}}
      {{this.num}}是奇數
{{/if_even}}

得到的結果是輸出“2是偶數”。通過在代碼中log出的數據,可以看到用this可以取到當前的上下文主體,此處就是我們的定義好的數據對象了。另外一個比較重要的就是options.fn方法,此方法可以將你傳入的上下文主體編譯到模板,返回編譯後的結果,在helper中,我們把this傳了進去,於是在模板中也可以引用到它。最終options.fn返回編譯後的結果:2是偶數。其實你也可以爲options.fn傳入其他的上下文對象,比如你要寫一個迭代器,可以把數組的元素依次傳入。

此處我們還看到了另一個方法,options.inverse,它是取相反的意思,對應了我們模板中的{{else}}標籤,它會編譯{{else}}中的的內容並返回結果,如果我們的helper中需要帶else邏輯,用它就可以了。

塊級helper在用的時候開頭要加"#",並且要有結束符,就是上面的{{/if_even}}

接收多個參數的helper

自定義helper可以傳入多個參數,只要依次寫在registerHelper的函數中就可以了,看下面一個例子。

由於handlebars內置的if語句太弱,有時候我們需要判斷像==、!=、>、<這樣的邏輯,就必須自己寫定義helper了。這樣的helper需要傳入左右操作數還有操作符,參數不只一個。下面這個compare是從別的地方抄來的,也是我在項目中用的最多的:

Handlebars.registerHelper('compare', function(left, operator, right, options) {
         if (arguments.length < 3) {
           throw new Error('Handlerbars Helper "compare" needs 2 parameters');
         }
         var operators = {
           '==':     function(l, r) {return l == r; },
           '===':    function(l, r) {return l === r; },
           '!=':     function(l, r) {return l != r; },
           '!==':    function(l, r) {return l !== r; },
           '<':      function(l, r) {return l < r; },
           '>':      function(l, r) {return l > r; },
           '<=':     function(l, r) {return l <= r; },
           '>=':     function(l, r) {return l >= r; },
           'typeof': function(l, r) {return typeof l == r; }
         };

         if (!operators[operator]) {
           throw new Error('Handlerbars Helper "compare" doesn\'t know the operator ' + operator);
         }

         var result = operators[operator](left, right);

         if (result) {
           return options.fn(this);
         } else {
           return options.inverse(this);
         }
     });
View Code

使用的時候是這樣:

{{#compare people.name '==' 'peter'}}
     他的名字是peter
     {{else}}
     他的名字不是peter
 {{/compare}}

可以看到在模板中傳入的參數依次對應helper定義中的left、operator、options。同時定義中也是用了options.inverse,用來處理else的邏輯。

爲helper傳入hash參數

在模板中使用helper的時候,我們還可以爲helper傳入一些變量參數,叫做hash參數,在helper中可以通過options.hash拿到這些參數進行處理。這樣helper的靈活性和可複用性就大大增強了。我們還是舉例來說明。

定義一個名爲list的helper,它的作用是循環輸出數據,並把數據包裹在ul>li標籤中。同時爲了給元素增加不同的class,我把class名稱作爲hash來傳入。helper代碼如下:

Handlebars.registerHelper('list', function(items, options) {
	var out = '<ul>';
	for(var i=0, l=items.length; i<l; i++) {
		var item = options.fn(items[i]);
		out = out + '<li class="'+options.hash.class+'">' + item + '</li>';
	}
	return out + '</ul>';
});

在模板中,我使用了兩次list,並傳入不同的hash值:

{{#list people class="green"}}{{firstName}}-----{{lastName}}{{/list}}
{{#list people class="red"}}{{firstName}}-----{{lastName}}{{/list}}

定義以下數據做測試:

var data = {
    people: [
        {firstName: "Yehuda", lastName: "Katz"},
        {firstName: "Carl", lastName: "Lerche"},
        {firstName: "Alan", lastName: "Johnson"}
    ]
}

最終頁面上生成的節點如下所示:

這樣我們就重用了同一個helper,完成了更加靈活的任務。看到這裏,是不是覺得helper很強大了呢,利用上面這些特性,我們可以寫出非常豐富的功能了,足以滿足開發需求。

其他

另外還有兩點的小知識,補充在此處:

1. helper的銷燬

調用Handlebars.unregisterHelper('list')即可銷燬一個helper

2. 一次註冊多個helper

andlebars.registerHelper({
    foo: function() {},
    bar: function() {}
});

handlebars作爲一個弱邏輯的靜態模板引擎,本身簡單好用,沒有太多冗餘的東西,同時還提供了強大的擴展性,這也是我喜歡它的原因。希望通過本篇文章能讓你更多的瞭解handlebars的helper,開始喜歡上它。

原創傳送門:http://www.cnblogs.com/lvdabao/p/handlebars_helper.html?utm_source=tuicool&utm_medium=referral

Handlebars.js傳送門:http://www.ghostchina.com/introducing-the-handlebars-js-templating-engine/

Handlebars官網傳送門:http://handlebarsjs.com/

發佈了22 篇原創文章 · 獲贊 23 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章