본문 바로가기
프로그래밍 놀이터/Web

[Script] Handlebars Tutorial & Reference

by 돼지왕 왕돼지 2012. 9. 21.
반응형



HandleBars 란 무엇인가? 

- Template JavaScript Engine. ( template 은 view라고 생각하면 쉽겠다. ( 정확한 matching은 아님 ) )




Handlebar 를 사용하려면..

<!-- Handlebars 코드 정의 -->
<script id="entry-template" type="text/x-handlebars-template">
   template content
</script> 

<!-- Handlebars 컴파일 -->
<script>
   var source = ${'#entry-template').html();
   var template = Handlebars.compile( source );
</script> 


- 코드에서 바로 컴파일하지 않고, pre-compile 할 수도 있는데 이 경우 컴파일러 없이 runtime lib만 요구되고, 컴파일하는데 걸리는 엄청난 시간도 절약될 수 있다. mobile device 를 사용할 때에는 pre-compile 이 권장된다.

- pre-compile 방법은 아래 링크를 참고한다.

중요한 것은 person.handlebars 를 컴파일 하면 Handlebars.templates.person 이 output 으로 나온다. pre-compile 할 경우에는 간단하게 handlebars.runtime.js 만 연결해주면 된다.


<script src="/libs/handlebars.runtime.js"></script>





HandleBars 기본

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>


- HTML 중간에 아래의 형태로 dynamic하게 변수를 끌어다 쓸 수 있다.

{{ 내용물 }}     // escape char 가 있는 경우 escape 하여 HTML form으로 변환된다.
{{{ 내용물 }}}  // escape char 가 있는 경우 그대로 사용된다.

 - 위의 예시에서 가져다 쓴 내용물은 변수명으로 현재 context 에 정의되어 있는 변수를 끌어다 쓴다.


<h1>{{article.title}}</h1

<h1>{{article/title}}</h1>
- article property의 title 에 접근


{{#each articles.[10].comments}}
  <h1>{{subject}}</h1>
  <div>
    {{body}}
  </div>
{{/each}}

- articles 배열의 10번째 comments 의 모든 property 를 context 로 갖으면서 ( articles.[10].comments 가 기본으로 접두된다고 생각하면 되겠다. ) subject 와 body 에 접근한다.




Helpers 

- Helper 는 기본적으로 보통 language 의 함수라고 볼 수 있다.

- 기본 Helper는 다음의 syntax 로 접근한다.

{{{ 헬퍼이름 파라미터 파라미터 ... }}}


- 아래는 link 라는 헬퍼( 함수 ) 에 story 라는 parameter 를 넣어 실행시킨 결과를 출력하라는 의미이다. 

{{{link story}}}


- link helper 는 Handlebars.registerHelper( 헬퍼이름, 수행할 function ) 으로 등록한다.

Handlebars.registerHelper('link', function(object) {
  return new Handlebars.SafeString(
    "<a href='" + object.url + "'>" + object.text + "</a>"
  );
});

- cf) Handlebars.SafeString( 내용물 ) 을 사용하게 되면, 내용물에 대해 escape char 를 걱정할 필요가 없다. 다시 말해 < 를 HTML식으로 &lt; 로 치환할 필요없이 그냥 < 로 써도 된다는 말.


- Helper 는 최종 파라미터들로 key-value pair hash 를 전달할 수도 있다.

{{{link "See more..." href=story.url class="story"}}}


- 이럴 경우 Helper 함수에서는 hash 부분을 하나의 object 로 받아서, object.hash 로 접근하여 key-value pair 에 대한 context를 얻을 수 있다.

Handlebars.registerHelper('link', function(text, options) {
  var attrs = [];

  for(var prop in options.hash) {
    attrs.push(prop + '="' + options.hash[prop] + '"');
  }

  return new Handlebars.SafeString(
    "<a " + attrs.join(" ") + ">" + text + "</a>"
  );
});

- cf) JSON 의 경우 for( var i in object ) 구문을 실행하면, i 에는 property 의 이름이 assign 되며, value에 대한 접근은 object.i 또는 object[i] 로 접근한다.







Basic Blocks

- Block helper는 일반 Helper 처럼 값을 Handlebars 구문 안에서 parameter 로 전달해주는 형태가 아닌, 다른 handlebars 변수를 nest 하여 전달되는 context 에서 작업을 수행하는 것을 말한다.

- Block helper 의 기본 syntax는 아래와 같다.

{{#helper}} 내용물 {{/helper}} 


<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{#noop}}{{body}}{{/noop}}
  </div>
</div>

Handlebars.registerHelper('noop', function(options) {
  return options.fn(this);
});
- Handlebars 는 항상 현재 context를 helper에 this 로 전달한다. parameter가 있는 경우 전달하는 녀석을 context 로 설정할 수 있다. 
- Block helper 를 사용하면, options 라는 hash 를 항상 전달하는데, 이 option에는 fn 라는 함수가 있다. 이 함수는 context 를 전달받아 block 안의 내용물을 string 의 형태로 return 하는 역할을 한다.



with helper


<div class="entry">
  <h1>{{title}}</h1>
  {{#with story}}
    <div class="intro">{{intro}}</div>
    <div class="body">{{body}}</div>
  {{/with}}
</div>

{
  title: "First Post",
  story: {
    intro: "Before the jump",
    body: "After the jump"
  }
}

Handlebars.registerHelper(function(context, options) {
  return options.fn(context);
});

- JSON 에 있는 story 를 context 로 전달하여 options.fn 으로 다시 전달한다. options.fn 은 해당 context 하에서 내용물을 해석해서 return 해준다. 즉 story.intro 와 story.body 가 대입된 형태의 html 이 완성되어 나온다.
- 사실 with helper 는 built-in helper 이다.



Simple Iterators


<div class="entry">
  <h1>{{title}}</h1>
  {{#with story}}
    <div class="intro">{{{intro}}}</div>
    <div class="body">{{{body}}}</div>
  {{/with}}
</div>
<div class="comments">
  {{#each comments}}
    <div class="comment">
      <h2>{{subject}}</h2>
      {{body}}
    </div>
  {{/each}}
</div>

Handlebars.registerHelper('each', function(context, options) {
  var ret = "";

  for(var i=0, j=context.length; i<j; i++) {
    ret = ret + options.fn(context[i]);
  }

  return ret;
});

- each 도 built-in block helper 이다.
- opionts.fn 대신 바로 options( context ) 를 사용해도 인식한다.
- 간단하게 context.map( function( item ){ } ) 을 사용하면, context 에 있는 property 들이 순서대로 내부 function의 parameter로 assign 되며 iteration 된다.



Conditionals


- if, unless 도 마찬가지로 built-in handlebars block function 이다. 

<if문>

{{#if 조건문}}
   내용물
{{else}}
   내용물
{{/if}} 

- else 문을 handlebars 로 구현하려면 options.fn(this) 대신 options.inverse(this) 를 사용한다.



Hash Arguments


- Block helper 도 일반 helper 와 마찬가지로 key-value pair 를 parameter 의 끝에 명시해서 hash 의 형태로 받을 수 있다.

{{list nav id="nav-bar" class="top"}}
  <a href="{{url}}">{{title}}</a>
{{/list}}

Handlebars.registerHelper('list', function(context, options) {
  var attrs = SC.keys(options.hash).map(function(key) {
    key + '="' + options.hash[key] + '"';
  }).join(" ");

  return "<ul " + attrs + ">" + context.map(function(item) {
    return "<li>" + options.fn(item) + "</li>";
  }).join("\n") + "</ul>";
});



- Helper 는 값을 조회하는 것 뿐만 아니라 새로운 private property를 "assign" 하는 기능도 가지고 있다.

{{#list array}}
  {{@index}}. {{title}}
{{/list}}
Handlebars.registerHelper('list', function(context, options) {
  var out = "<ul>", data;

  for (var i=0; i<context.length; i++) {
    if (options.data) { data = Handlebars.createFrame(options.data); }
    out += "<li>" + options.fn(context[i], { data: data }) + "</li>";
  }

  out += "</ul>";
  return out;
});

- ? 이 구문은 잘 이해가 되지 않지만, 코드 설명상으로는 원래 context에 들어있지 않은 데이터를 넣어주는 작업이고, {{@index}} 를 통해 추가된 data 를 접근하는 것이라 한다.




Built-in Block Helpers

- with : 다른 context 를 전달할 때 사용
- each : for 문과 동일
- if / else
- unless




Handlebars Path

- ../ 를 통해 parent 의 context 로 접근 가능하다.

{{../permalink}}




registerPartial

- 모든 template 에서 사용가능한 부분적인 template 을 만든다. 모든 template 에서 접근 가능하기 때문에 보통 header 나 footer 같이 어느 페이지서든 사용가능한 코드에서 사용한다.

// partial 등록
Handlebars.registerPartial( partialName, partialCodeId );

// partial 사용
{{>partialName}}

 




특이 case

- Block helper 로 내용물을 전달할 때 그 안에 {{else}} 블락이 있다면 options( 혹은 fn )이 아닌 elseFn 이 마지막 인자로 전달된다. else 구문을 선택하려면 return elseFn(); 을 넣어주면 된다.

Handlebars.registerHelper("stripes", function(array, even, odd, fn, elseFn) {

if (array && array.length > 0) {

  var buffer = "";

  for (var i = 0, j = array.length; i < j; i++) {

var item = array[i];

item.stripeClass = (i % 2 == 0 ? even : odd);

buffer += fn(item);

  }

  return buffer;

}

else {

  return elseFn();

}

});

 




Debug Helper

- debug 를 통해 현재 context 와 value 들을 log 로 찍어볼 수 있다.

<정의>

Handlebars.registerHelper("debug", function(optionalValue) {
    console.log("Current Context");
    console.log("====================");
    console.log(this);  
    if (optionalValue) {
        console.log("Value");
        console.log("====================");
        console.log(optionalValue); }
    }
);


<사용>

{{debug}}
{{debug value}} 




jQuery plugin

<html> 

<head> 

<title>What's Up?</title> 

</head> 

<body> 

<div id="content"> This is where the content goes! </div> 

<script id="template" type="text/handlebars"> 

Hello, {{name}}! 

</script> 

<script src="jquery.js"></script> 

<script src="handlebars-0.9.0.pre.5.js"></script> 

<script> 

(function($) { 

var compiled = {}; 

$.fn.handlebars = function(template, data) { 

if (template instanceof jQuery) { 

template = $(template).html(); 

}   

compiled[template] = Handlebars.compile(template); 

this.html(compiled[template](data)); 

}; })(jQuery);   

$('#content').handlebars($('#template'), { name: "Alan" }); 

</script> 

</body> 

</html>




도움이 되셨다면 손가락 꾸욱~ (로그인 필요 x)



 


반응형

댓글