Knowledge/javascript

데이터 바인딩을 도와주는 handlebars.js 사용법

TakeKnowledge 2019. 8. 26. 23:50
반응형

ajax로 가져온 데이터를 동적으로 추가해야 하는 상황은 상당히 자주 발생한다.

Handlebars.js는 이런 상황을 좀 더 쉽게 처리할 수 있도록 도와주는 라이브러리.. 라는데

사실 익숙하지 않으면 되려 더 어렵다.

 

필자도 이번에 처음 써보면서 많이 헤맸는데

헤매면서 알게된, 진작 알았으면 좋았을 만한 것들을 정리해두려 한다.

 

- 기본 사용법

 

기본사용법은 다음과 같다.

 

1. 템플레이팅할 html을 세팅한다. 이때 데이터 바인딩 시킬 부분은 {{바인딩시킬 데이터의 프로퍼티명}} 의 형태로 적는다. 

 

2. htmlTemplate을 가져온후 Handlebars로 compile 한다.

 

3. 컴파일한 템플릿에 데이터를 집어넣는다. 이 때 리턴값은 html로 나온다.

 

4. 리턴받은 html을 target에 innerHTML로 세팅한다. 

 

코드로 보자면 아래와 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Handelbar</title>
</head>
<body>
        <ul id="commentUl">
 
        </ul>
     <!-- 1. 템플레이팅할 html을 세팅한다. 이때 데이터 바인딩 시킬 부분은 --> 
<!--{{바인딩시킬 데이터의 프로퍼티명}} 의 형태로 적는다.  -->
    <script type="template" id="commentTemplate">
                <li class="list_item">
                    <div>
                        <div class="review_area">
                            <p class="review">{{comment}}</p>
                        </div>
                        <div class="info_area">
                            <div class="review_info"> 
                                <span class="name">{{name}}</span> 
                            </div>
                        </div>
                    </div>
                </li>
    </script>
    <script type="text/javascript" charset="utf-8">
 
            // 데이터
            var data = {
                comment : '굿' ,name : "초보" 
            }
          //2. htmlTemplate을 가져온후 Handlebars로 compile 한다.
           var commentTemplate = document.querySelector("#commentTemplate").innerHTML;
           var commentBindTemplate = Handlebars.compile(commentTemplate);
 
            //3. 컴파일한 템플릿에 데이터를 집어넣는다. 이 때 리턴값은 html로 나온다.
            var resultHtml = '';
            resultHtml += commentBindTemplate(data);
 
            // 4. 리턴받은 html을 target에 innerHTML로 세팅한다. 
            var commentUl = document.querySelector("#commentUl");
            commentUl.innerHTML = resultHtml; 
         
    </script>
</body>
</html>
 
 

 

이 코드를 실행시키면 아래와 같이 잘 작동한다

 

css는 생략한다

 

- 데이터가 배열일 경우

 

그런데 만약 데이터가 아래와 같이 배열로 들어있다면 어떻게 해야할까?

 

1
2
3
4
5
6
7
8
9
            var data = {
                comments : [
                            {comment : '굿' ,name : "김춘삼" },
                            {comment : '별로' ,name : "허준" },
                            {comment : '좋았음' ,name : "대장금" },
                            {comment : '음..' ,name : "김두한"},
                            {comment : '가성비가 그닥..',name : "시라소니" }
                        ]
            }

 

아주 쉽다. 템플릿 HTML만 아래와 같이 바꿔주면 된다 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    <script type="template" id="commentTemplate">
                {{#each comments}}
                <li class="list_item">
                    <div>
                        <div class="review_area">
                            <p class="review">{{comment}}</p>
                        </div>
                        <div class="info_area">
                            <div class="review_info"> 
                                <span class="name">{{name}}</span> 
                            </div>
                        </div>
                    </div>
                </li>
                {{/each}}
    </script>

 

이렇게 each를 사용해 배열명을 적어주면 forEach처럼 잘 작동한다. 

 

역시 CSS는 생략

 

여기까지는 별로 어려운 게 없다 그러나..

 

- 배열갯수보다 적게 붙이고 싶다면? 예를 들어 배열 갯수가 다섯개인데 그 중 세개만 붙이고 싶다면?

 

여기서부터가 필자가 헤맨 부분이다.

배열 내용을 붙이려면 each를 써야하고 그럼 배열 갯수만큼 도는데 어떻게 배열 갯수보다 적게 붙일 수 있지?

 

이것 저것 찾아본 결과 이건 기본 제공 기능만으로는 구현할 수 없고 Handlebars의 registerHelper와 index를 사용해야 한다. 이를 위해선 먼저 template을 아래와 같이 바꿔야한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    <script type="template" id="commentTemplate">
                {{#each comments}}
                {{#ifThirdOver @index}}
                <li class="list_item">
                    <div>
                        <div class="review_area">
                            <p class="review">{{comment}}</p>
                        </div>
                        <div class="info_area">
                            <div class="review_info"> 
                                <span class="name">{{name}}</span> 
                            </div>
                        </div>
                    </div>
                </li>
                {{/ifThirdOver}}
                {{/each}}
    </script>

 

{{#ifThirdOver @index}}

 

{{/ifThirdOver}}

 

가 추가된 걸 볼 수 있다. ifThirdOver는 기본 제공 예약어는 아니다. 이 후에 만들 Helper함수 이름으로 임의로 정한 것이이고 @index는 배열의 index를 반환해주는 예약어다. 지금은 데이터가 5개니 0,1,2,3,4 순으로 찍히는 값이란 말.

 

그리고 @index는 ifThirdOver 함수의 파라미터다 즉  {{#ifThirdOver @index}} 이런식으로 쓰면 띄어쓰기 뒤의 값이 앞의 함수에 파라미터로 들어간다. 그럼 그 파라미터를 받은 ifThirdOver 함수를 알아보자 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
           var commentTemplate = document.querySelector("#commentTemplate").innerHTML;
 
            var commentBindTemplate = Handlebars.compile(commentTemplate);
            
            // 코멘트가 3개 이상일 경우 3개까지만 세팅되도록 하는 Handlebars Helper method 
            Handlebars.registerHelper('ifThirdOver',function(index, options){
                   if(index < 3){
                          return options.fn(this);
                   } else {
                          return options.inverse(this);
                   }
            });
             
            var resultHtml = '';
            resultHtml += commentBindTemplate(data);
 
            // commentUl 에 li 세팅
            var commentUl = document.querySelector("#commentUl");
            commentUl.innerHTML = resultHtml; 

 

위 아래 부분은 '그래서 위치를 어디에 넣으라는거야?' 하는 분이 계실까봐 (실제로 필자가 포스팅 보며 자주 그런다) 집어 넣은 부분이고 Handlebars.registerHelper('ifThirdOver',function(index, options){ } 부분만 보면 된다.

 

index를 파라미터로 받아서 index가 3보다 적을 경우 options.fn(this); 를 리턴하고 그렇지 않은 경우엔 options.inverse(this); 를 리턴한다. 나도 이 부분이 정확히 어떤 의미인지는 파악하지 못했으나 

return options.inverse(this);는 return false 같은 거고 return options.fn(this); 는 정상 작동 같은 거라 이해했다.

 

실제로 그렇게 작동한다

 

 

3개만 붙은 걸 확인할 수 있다

 

굳. 이제 마지막 미션만 남았다.

 

- each가 도는 중에 다른 배열에 있는 데이터도 바인딩하고 싶다면?

 

이게 제일 어려웠던 부분인데 해결은 매우 쉬워 허무했다. 일단 좀 더 자세한 상황 설명을 위해 데이터부터 보자

 

1
2
3
4
5
6
7
8
9
10
11
12
            var data = {
                comments : [
                            {comment : '굿' ,name : "김춘삼" },
                            {comment : '별로' ,name : "허준" },
                            {comment : '좋았음' ,name : "대장금" },
                            {comment : '음..' ,name : "김두한"},
                            {comment : '가성비가 그닥..',name : "시라소니" }
                        ],
            displayInfo : [
                            { title : '전시' }
                        ]
            }

 

이런 식으로 data안에 배열이 2개가 된 상황이다 그리고 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <script type="template" id="commentTemplate">
                {{#each comments}}
                {{#ifThirdOver @index}}
                <li class="list_item">
                    <div>
                        <div class="review_area">
                            <h4 class="resoc_name">{{displayInfo.0.title}}</h4>
                            <p class="review">{{comment}}</p>
                        </div>
                        <div class="info_area">
                            <div class="review_info"> 
                                <span class="name">{{name}}</span> 
                            </div>
                        </div>
                    </div>
                </li>
                {{/ifThirdOver}}
                {{/each}}
    </script>

 

displayInfo.0.title  부분에 저 '전시' 라는 타이틀을 넣고 싶은건데

Handlebar 안에서 배열은 . 으로 접근할 수 있지만 comments가 each로 돌고 있는 중이기 때문에 

comments의 하위 배열이 아닌 displayInfo로는 접근이 되지 않는다. 

 

그래서 이걸 어떻게 해야하나 싶어 여기 저기 뒤지고 다니다가 마침내 방법을 찾았다.

그건 바로 ../

 

Handlebar에서 ../을 쓰면 상위로 올라간다.

즉 여기서는 comments가 each로 돌고 있으니 data로 돌아간다는 말이고 

거기서는 displayInfo가 하위 배열이니 접근이 가능하다 즉 아래와 같이

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <script type="template" id="commentTemplate">
                {{#each comments}}
                {{#ifThirdOver @index}}
                <li class="list_item">
                    <div>
                        <div class="review_area">
                            <h4 class="resoc_name">{{../displayInfo.0.title}}</h4>
                            <p class="review">{{comment}}</p>
                        </div>
                        <div class="info_area">
                            <div class="review_info"> 
                                <span class="name">{{name}}</span> 
                            </div>
                        </div>
                    </div>
                </li>
                {{/ifThirdOver}}
                {{/each}}
    </script>

 

../을 추가해 템플릿을 변경해주면

 

원했던 결과를 얻을 수 있다

 

참고

 

https://handlebarsjs.com/

 

https://programmingsummaries.tistory.com/381

 

https://www.edwith.org/boostcourse-web/lecture/16784/

 

반응형