Friday, May 29, 2015

Sử dụng jade templates với nodejs

Chào các bạn,

Hôm nay mình và các bạn sẽ cùng làm quen với một Template Engine sử dụng cùng với NodeJS rất hiệu quả và phổ biến nhé.

Trong mô hình MVC, các bạn biết rằng, phần View để có thể hiển thị dữ liệu động cho cùng một form nào đó, thì chúng ta cần phải có các template dựng sẵn.
Đối với lập trình web application trên NodeJS, chúng ta có thể sử dụng một số Template Engine sau:
  • EJS : Là một JavaScript Template library vô cùng dễ sử dụng, cú pháp đơn giản, rõ ràng.
  • Mustache:  Là một Logic-less templates cũng khá nổi tiếng với những ai hay code java script để bind dữ liệu từ chuỗi JSON. Template này sử dụng trên rất nhiều ngôn ngữ, phiên bản sử dụng cho NodeJS các bạn có thể tìm ở đây: https://github.com/raycmorgan/Mu
  •  jQuery Templates plugin: Một template là plugin của jquery cũng vô cùng dễ dùng và khá quen thuộc với các bạn từng làm nhiều về js client để bind dữ liệu từ các đối tượng dạng json ra html.
Tuy nhiên, hôm nay, mình sẽ cùng giới thiệu và nghiên cứu với các bạn một Template Engine vô cùng mạnh mẽ, kết hợp cả css, js và template vô cùng dễ chỉnh sửa, thao tác, và cú pháp mạch lạc, rõ ràng và đặc biệt là nó được sử dụng trong Module Express - Một module phổ biến cho lập trình web sử dụng NodeJS. Điều này, có nghĩa là để tiếp tục với các phần Tutorial tiếp theo về lập trình một ứng dụng web trên nền tảng Nodejs, chúng ta sẽ làm quen với Express, trong đó phần render ra html của Express sử dụng chính là Jade.
Để khỏi bỡ ngỡ với quá nhiều cái mới trong Express, chúng ta sẽ cùng nhau đi từ những phần nhỏ của nó. Hôm nay chúng ta sẽ bắt đầu từ Jade.

Tản mạn như vậy là hơi nhiều và dài dòng. Chúng ta bắt tay vào làm thôi nào.
Như bài trước, chúng ta làm quen với một ứng dụng web đầu tiên là trang web với nội dung là lời chào Hello World.
var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/html"});
    response.write("Hello World");
    response.end();
}).listen(8080);
Các bạn chú ý, ở phần này mình đã thay đổi:
"Content-Type": "text/plain"
Thành
"Content-Type": "text/html"
Vì để có thể out ra được html với các thẻ định dạng như p, div, span...cùng với css trong đó.
Để nhận biết được điều này, các bạn đơn giản thử hai đoạn code sau sẽ rõ:
var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/plan"});
    response.write("<h1>Hello World</h1><p>Chào các bạn, mình đến từ YourNodeJS.com</p>");
    response.end();
}).listen(8080);

var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/html"});
    response.write("<h1>Hello World</h1><p>Chào các bạn, mình đến từ YourNodeJS.com</p>");
    response.end();
}).listen(8080);
Ok, bây giờ bài toán đặt ra là: Bạn cần hiển thị một cái gì đó chẳng hạn như trang chủ của blog như hình đơn giản sau đây, và dữ liệu tùy biến được. Có nav panel, có header của blog, có Cột content ở giữa là danh sách các post ngắn gọn có thumb, có một đoạn lead ngắn gọn, có các tags của bài đó, có cột bên trái là các box Đọc nhiều nhất, danh sách tags của toàn blog, rồi box liên kết website chẳng hạn. Bạn có thể tùy biến cái thổ tả gì bạn muốn, tùy theo yêu cầu, nhu cầu, sở thích hoặc sở đếch thích gì của bạn cũng được. Phía dưới có hẳn phần paging luôn. Thích bạn có thể làm cái banner chèn vào phần Top để nhìn cho nó hoành tráng.


Cơ bản nhìn màn hình đơn giản thế thôi, nếu bạn code từng dùng html, rồi dùng lệnh res.end('html'); ra thì bạn cũng ốm đòn. Chưa kể các cột nội dung là động. Danh sách post, danh sách tags, box Đọc nhiều nhất.....Hơn nữa, bất cứ khi nào bạn muốn sửa đổi là phải tắt server đi, chỉnh sửa vô cùng vất vả, việc thay đổi style cũng khó khăn.

Yên tâm, hôm nay, với việc sử dụng jade, cùng với kiến thức bạn đã có từ bài Đọc ghi dữ liệu từ XML thì việc xuất ra một trang blog như thế này chỉ là trong tầm tay, và chỉ mất chưa đầy 1 tiếng. He He.

Để làm bài toán này, chúng ta sẽ chia làm các bước sau:
  • Chuẩn bị dự án: bao gồm tạo cây thư mục, file dữ liệu, js, css, image cần thiết.
  • Thiết kế html của blog, tạo template.
  • Lấy dữ liệu, bind vào html và hiển thị ra client.
Nội dung của nó ta sẽ nói sau nhé, giờ đầu tiên chúng ta phải làm quen với Jade đã. Nói về nó từ đầu đến giờ, chưa biết Jade là gì, hoạt động như thế nào.

Jade như đã nói là một template engine giống bạn viết view trong MVC, để từ đó bạn bạn bind dữ liệu vào đó và xuất thành html.
Như tại trang chủ của Jade có nói rõ:
Jade is a clean, whitespace sensitive syntax for writing html.
Nghĩa là đối với Jade, mọi cú pháp viết html đã tinh giản hết, không còn phải đóng mở các thẻ (tag), không phải thêm các từ khóa cơ bản như: class, div, id..., các tag bọc lồng nhau đơn giản là dùng các khoảng trắng để thể hiện (thường người ta dùng tab cho dễ nhìn), đó là bạn thụt lề các tag này vào, ra là được. Để dễ hình dung, chúng ta nhìn vào ví dụ sau:
doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) bar(1 + 5)
  body
    h1 Jade - node template engine
    #container.col
      if youAreUsingJade
        p You are amazing
      else
        p Get on it!
      p.
        Jade is a terse and simple templating language with a
        strong focus on performance and powerful features. 
Chuyển thành HTML sẽ như sau:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
    <script type="text/javascript">
      if (foo) bar(1 + 5)
    </script>
  </head>
  <body>
    <h1>Jade - node template engine</h1>
    <div id="container" class="col">
      <p>You are amazing</p>
      <p>Jade is a terse and simple templating language with a strong focus on performance and powerful features.</p>
    </div>
  </body>
</html> 
Jade rất dễ sử dụng từ viết tag, chèn text, javascript, link, add thuộc tính, rồi đặt id, class đặc biệt thú vị đó là cho sử dụng vòng lặp, hằng biến, điều kiện cũng như dữ liệu từ bên ngoài.
Chúng ta sẽ đi từ những ví dụ đơn giản, đến nâng cao nhé.
Tạo một thư mục JadeEx, sau đó cài đặt module của  jade bằng lệnh:
$node install jade
Tạo hai file sau:
  • template.jade:  Là file jade mà chúng ta sẽ code jade vào đó
  • index.js: Là file mà chúng ta sẽ build ra html từ template là xuất ra trình duyệt.
Ở file index.js, nhiệm vụ là sử dụng jade module, đọc từ template ra và xuất ra html, chúng ta đơn giản chỉ dùng đoạn code sau:
var jade = require('jade');
var html = jade.renderFile('template.jade');
var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/html;charset=utf-8"});
    response.write(html);
    response.end();
}).listen(8080);
Hiện tại file template chúng ta đang rỗng, nên chả in ra cái gì cả.
Giờ bạn thử đoạn code sau vào file template nhé.
doctype html
html(lang='en')
  head
    title Jade
  body
    h1 Jade - node template engine 
Kết quả xuất ra html sẽ là:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Jade</title>
  </head>
  <body>
    <h1>Jade - node template engine</h1>
  </body>
</html>  
Chúng ta sẽ có các cách viết cho phần tag, class, text, id, javascript...lần lượt như sau:

Tạo tag đơn giản

div
  address
  i
  strong

output sẽ là:
<div>
  <address></address><i></i><strong></strong>
</div>

Đưa text vào nội dung tag:

h1 Welcome to Jade
p
  | Text can be included in a number of
  | different ways.
p.
  This way is shortest if you need big
  blocks of text spanning multiple
  lines.
Out put là:
<h1>Welcome to Jade</h1>
<p>
  Text can be included in a number of
  different ways.
</p>
<p>
  This way is shortest if you need big
  blocks of text spanning multiple
  lines.
</p> 

Chèn id, class và thuộc tính vào tag

 h1#title Welcome to Jade
button.btn(data-action="bea") Awesome
Out put sẽ là:
<h1 id="title">Welcome to Jade</h1>
<button data-action="bea" class="btn">Awesome</button>
Thật là đơn giản phải không các bạn.
Bây giờ là đến phần chèn dữ liệu động, và vòng lặp.
Đến đây dữ liệu chúng ta put từ bên ngoài vào nên không render file thông thường nữa, mà chúng ta cần compile nó. Vậy nên cần thay đổi lại file index.js một chút.
var jade = require('jade');
var fs = require('fs');

var popularPosts = {posts :  [{id: 1,
                    title:"NodeJS là gì?"
                    },{
                        id: 2,
                        title:"Viết ứng dụng Hello World bằng NodeJS"
                    },{
                        id: 3,
                        title:"Sử dụng Jade trong NodeJS như thế nào?"
                    }]};

var jadetemplate = jade.compile(fs.readFileSync('template.jade', 'utf8'));
console.log(popularPosts.length);
var html = jadetemplate(popularPosts);
console.log(html); 
Chúng ta có list các Bài viết Đọc nhiều, gồm 3 bài với id, tiêu đề. Giờ chúng ta cần xuất ra html list như sau:
  • NodeJS là gì?
  • Viết ứng dụng Hello World bằng NodeJS
  • Sử dụng Jade trong NodeJS như thế nào?
Để xuất ra dạng list trong html, ta dùng cặp thể <ul>, <li> thôi.
Ở file template.jade ta sử lại như sau:
ul
 - each post in posts
  li.item #{post.title} 

Mình giải thích tý xíu cả hai đoạn code trên.
Ở đoạn code của index.js, để bind dữ liệu ngoài vào, ta cần dử dụng đến chức năng compile của jade. Là compile chuỗi jade string vào một function, sau đó chúng ta sẽ render ra html với data truyền vào. Cú pháp:
// Compile a function
var fn = jade.compile('string of jade', options);
// Render the function
var html = fn(locals);
 Ở ví dụ trên, string of jade chúng ta đọc từ file template ra thông qua hàm readFileSync của file system, options thì bao gồm name, doc type, cache... phần này ta sẽ tìm hiểu sau. Ở ví dụ trên ta không dùng đến.
Kế đến là phần render với data. locals là một đối tượng chính là phần data ta truyền vào, trong trường hợp này là list post. Thường ta phải truyền vào là: locals.posts = popularPosts.Post. Nhưng ở đây ta chỉ truyền vào một dữ liệu, nên không cần thiết. Đối với nhiều dữ liệu, chẳng hạn truyền cả posts, cả categories, cả comments chẳng hạn. Thì ta sẽ gán vào locals. Ở phần này ta sẽ có ví dụ ở bài tiếp theo.
Ở template, ta chú ý:
Dùng dấu - để bắt đầu một đoạn code giống javascript. Ở đây duyệt hết các post trong list post, với mỗi post, thì gán post.title vào thẻ li.
Ok, hiểu sơ lược vậy đã. Giờ ta chạy thử xem sao nào:
$npm index.js
Nếu output ra là:
<ul>
    <li class="item">NodeJS là gì?</li>
    <li class="item">Viết ứng dụng Hello World bằng NodeJS</li>
    <li class="item">Sử dụng Jade trong NodeJS như thế nào?</li>
</ul>
Là bạn đã thành công rồi đó.
Bây giờ chúng ta sẽ phát triển lên một tý.
Trường hợp bạn cần lồng các list con vào nữa thì sao? Ví dụ như list cây danh mục chẳng hạn.
  • Ngôn ngữ lập trình
    • .NET
    • PHP
    • Java
  • Hệ cơ sở dữ liệu
    • MS SQL
    • MySQL
    • Oracle
  • Hệ điều hành
    • Windows
    • MAC OSX
    • Linux
Thì ta sẽ làm sao.
Hoàn toàn đơn giản, cú pháp template cũng như trên mà thôi.
ul.cate

 - each category in categories

  li.item #{category.name}

   ul.subcate

    - each subcate in category.listsubcates

     li.subitem #{subcate.name}
index.js ta truyền dữ liệu vào, và thay đổi một chút để xuất ra hml xem trên trình duyệt xem thế nào nhé.
var jade = require('jade');
var fs = require('fs');
var listCategory = {
    categories : [
                    {
                        id: 1,
                        name : "Ngôn ngữ lập trình",
                        listsubcates : [
                            {
                                id : 11,
                                name : ".NET"    
                            },
                            {
                                id : 12,
                                name : "PHP"    
                            },
                            {
                                id : 13,
                                name : "Java"    
                            }
                        ]    
                    },
                    {
                        id: 2,
                        name : "Hệ cơ sở dữ liệu",
                        listsubcates : [
                            {
                                id : 21,
                                name : "MS SQL Server"    
                            },
                            {
                                id : 22,
                                name : "MySQL"    
                            },
                            {
                                id : 23,
                                name : "Oracle"    
                            }
                        ]    
                    },
                    {
                        id: 3,
                        name : "Hệ điều hành",
                        listsubcates : [
                            {
                                id : 21,
                                name : "Windows"    
                            },
                            {
                                id : 32,
                                name : "MAC OSX"    
                            },
                            {
                                id : 33,
                                name : "Linux"    
                            }
                        ]    
                    }
                ]
};
var jadetemplate = jade.compile(fs.readFileSync('template.jade', 'utf8'));
var html = jadetemplate(listCategory);

var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/html;charset=utf-8"});
    response.write(html);
    response.end();
}).listen(8080); 
Ok, chạy lệnh:
$node index.js
Mở trình duyệt lên và kiểm tra:
http://localhost:8080/
Kết quả như hình sau là bạn thành công rồi nha:
Giờ chúng ta thêm chút style cho nó nhé.
Thay đổi nội dung file template thành:
doctype html
html(lang='vn')
  head
    meta(charset='utf-8')
    title Danh sách chuyên mục
    link(href='http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css', rel='stylesheet')
    style(type='text/css').
      ul.cate{border: 1px solid #ACACAC;width: 200px;margin: auto;}
      li.item{font-weight: 700;font-size: 15px;}
      ul.subcate{font-style: italic;color: blue;}
  body
   ul.cate
    - each category in categories
      li.item #{category.name}
        ul.subcate
          - each subcate in category.listsubcates
            li.subitem #{subcate.name}
    script(src='http://code.jquery.com/jquery.js')
Chú ý có 3 thứ mới nhé:
1. Chèn link file css bên ngoài. Mình sử dụng:
link(href='http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css', rel='stylesheet')
2. Chèn link js bên ngoài:
script(src='http://code.jquery.com/jquery.js')
3. Chèn style inner html:
style(type='text/css').
      ul.cate{border: 1px solid #ACACAC;width: 200px;margin: auto;}
      li.item{font-weight: 700;font-size: 15px;}
      ul.subcate{font-style: italic;color: blue;}
Ở 1,2 mình đưa vào để các bạn nắm được cách chèn thôi, chứ trong ví dụ ta cũng không dùng. Ở ví dụ là chèn đoạn style cho các ul, li của ta thêm chút xíu cho nó thay đổi thôi.
 Giờ chạy lại xem kết quả thế nào nào.
 Oh, thay đổi style rồi nhé.
html Output:
<!DOCTYPE html>
<html lang="vn">
<head>
    <meta charset="utf-8">
    <title>Danh sách chuyên mục</title>
    <link href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
    <style type="text/css">
        ul.cate {
            border: 1px solid #ACACAC;
            width: 200px;
            margin: auto;
        }
        
        li.item {
            font-weight: 700;
            font-size: 15px;
        }
        
        ul.subcate {
            font-style: italic;
            color: blue;
        }
    </style>
</head>

<body>
    <ul class="cate">
        <li class="item">Ngôn ngữ lập trình
            <ul class="subcate">
                <li class="subitem">.NET</li>
                <li class="subitem">PHP</li>
                <li class="subitem">Java</li>
            </ul>
        </li>
        <li class="item">Hệ cơ sở dữ liệu
            <ul class="subcate">
                <li class="subitem">MS SQL Server</li>
                <li class="subitem">MySQL</li>
                <li class="subitem">Oracle</li>
            </ul>
        </li>
        <li class="item">Hệ điều hành
            <ul class="subcate">
                <li class="subitem">Windows</li>
                <li class="subitem">MAC OSX</li>
                <li class="subitem">Linux</li>
            </ul>
        </li>
        <script src="http://code.jquery.com/jquery.js"></script>
    </ul>
</body>
</html>
Một số điểm lưu ý:
  • Để thụt đầu dòng trong template các bạn có thể dùng space hoặc tab, tuy nhiên cần phải dùng đồng nhất và đều. Việc thụt không đúng sẽ dễ gây ra lỗi, cũng như các thẻ không lồng đúng vào nhau.
  •  Ở dữ liệu JSON, các bạn chú ý phải đúng tên thuộc tính, đúng mảng hoặc list khi truyền sang file template.
Vậy để gen ra html động, với jade template engine trên nodejs cũng đơn giản phải không các bạn.
Ở bài tiếp theo, chúng ta sẽ nâng cao lên với việc tạo một trang home của một blog đơn giản, như muc tiêu đề ra ban đầu nhé. Và dữ liệu là ta load từ file xml vào.
Có bất cứ lỗi hay vấn đề gì cần trao đổi. Các bạn để lại comment nhé.
Share this post
  • Share to Facebook
  • Share to Twitter
  • Share to Google+
  • Share to Stumble Upon
  • Share to Evernote
  • Share to Blogger
  • Share to Email
  • Share to Yahoo Messenger
  • More...
 
Posts RSSComments RSSBack to top
© 2011 Tự học NodeJS ∙ Designed by BlogThietKe
Released under Creative Commons 3.0 CC BY-NC 3.0