Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách thiết lập và sử dụng một view engine
- công cụ sẽ giúp chúng ta tạo ra các dạng mẫu template
của code HTML chưa có chứa dữ liệu thực tế; Và đồng thời thực hiện công việc tạo ra nội dung văn bản HTML kèm theo dữ liệu được cung cấp để gửi phản hồi lại yêu cầu của trình duyệt web.
Câu lệnh tạo ra một văn bản HTML
Chúng ta sẽ xuất phát từ câu lệnh thể hiện ứng dụng của view engine
mà chúng ta đã nói đến ở cuối bài trước. Ở đây chúng ta sẽ nhìn lại code tạo express app
một chút, vị trí mà chúng ta cần quan tâm tới là các đoạn truy vấn các tệp tĩnh và gửi trả lại bằng phương thức response.sendFile
.
const express = require('express');
const path = require('path'); /* Creating Server */ const app = express(); /* Adding Routes */ const staticFolder = path.join(__dirname, 'static'); app.get('/', function(request, response) { var indexHtml = path.join(staticFolder, 'index.html'); response.sendFile(indexHtml);
}); app.get('*', function(request, response) { var staticFile = path.join(staticFolder, request.originalUrl); response.sendFile(staticFile, errorHandler); const errorHandler = function(error) { if (error instanceof Error) { var oopsHtml = path.join(staticFolder, 'oops.html') response.status(404).sendFile(oopsHtml) } else { /* do nothing */ ; } }; // errorHandler
}); // app.get * /* Start Running Server */ app.listen(3000, function() { console.log('Server is running at...'); console.log('http://127.0.0.1:3000');
});
Chúng ta sẽ bắt đầu chỉnh sửa code ở đoạn thêm route
xử lý cho yêu cầu xem trang chủ app.get('/', ...)
để sử dụng một template
.
Trước hết thì chúng ta sẽ tạm giả định dữ liệu data
truy vấn được từ một tệp nào đó và được chuyển thành một object JSON
. Sau đó dữ liệu này được truyền vào phương thức response.render
mà chúng ta đã nói đến ở cuối bài trước - kèm theo một tệp template
có tên là index
. Phương thức render
sau khi sử dụng tệp template index
và dữ liệu data
để tạo ra nội dung của một tệp HTML hoàn chỉnh sẽ tự động gửi cho trình duyệt web.
/* Adding Routes ... */ app.get('/', function(request, response) { // giả định data truy vấn được từ một tệp tĩnh var data = { title: 'Trang chủ | Blog lập trình?', heading: 'Blog lập trình tự nhiên', content: null }; response.render('index', data);
});
Thực tế thì phương thức response.render
sẽ không trực tiếp việc tạo ra nội dung HTML từ template
, mà sẽ ủy thác tác vụ này cho một phần mềm hỗ trợ ở dạng plug-in
có tên là template engine
hay view engine
như chúng ta đã nói đến ở đầu bài viết. Có rất nhiều engine
khác nhau đang được chia sẻ bởi cộng đồng; Và ExpressJS có cung cấp cho chúng ta một trang tài liệu liệt kê những engine
phổ biến nhất ở đây - ExpressJS.com -> Resouces -> Template Engines.
Mỗi một template engine
sẽ đưa ra một ngôn ngữ riêng để biểu thị dạng mẫu của văn bản HTML - thường được gọi là templating language
. Bạn có thể xem qua từng engine
trong danh sách đó để chọn ra thứ phù hợp nhất và sử dụng. Còn trong ví dụ ở đây thì mình sẽ sử dụng ejs - Embedded JavaScript
- một trong những engine
thân thiện nhất bởi có cú pháp dạng nhúng trong code HTML và các câu lệnh tương đồng với JavaScript.
<!doctype html>
<html lang="en">
<head> <title><%= title %></title> <link rel="stylesheet" href="/asset/style.css">
</head>
<body> <h1><%= heading %></h1> <div> <% if (content) { %> <%= content %> <% } else { %> <p>Trang web đang trong quá trình xây dựng...</p> <% } %> </div> <script src="/asset/main.js"></script>
</body>
</html>
Và kết quả thu được sau khi render
thành HTML thì chúng ta cũng có thể đoán được rồi. Các vị trí <%= title %>
trong khối <head>
, và <%= heading %>
trong khối <body>
, sẽ được gắn dữ liệu thực tế tương ứng với các thuộc tính của data
. Riêng đối với vị trí <%= content %>
thì chúng ta có một khối lệnh điều kiện để hiển thị nội dung nếu có.
Sau khi bạn đã chọn xong một engine
phù hợp thì chúng ta tiến hành cài đặt và thiết lập để sử dụng thôi.
Cài đặt và thiết lập view engine
Nhờ npm
cài đặt thêm template engine
đã chọn vào project
.
CMD | Terminal
npm install ejs --save
Để sử dụng một view engine
thì chúng ta cần khai báo với express app
ở vị trí trước khi bổ sung các route
xử lý yêu cầu, bằng cách sử dụng phương thức app.set
. Đồng thời, ở đây chúng ta cũng sẽ thực hiện thao tác khai báo đường dẫn tới thư mục chứa các tệp template
. Trong code ví dụ dưới đây mình sẽ đặt các tệp template
trong thư mục view
là thư mục con của nodejs-blog
.
/* Creating Server ... */ /* Setup View Engine */ const templateFolder = path.join(__dirname, 'view'); app.set('views', templateFolder);
app.set('view engine', 'ejs'); // tên của engine - pug, ejs, /* Adding Routes ... */
Sau đó là tạo ra tệp template
với tên index
trong thư mục views
để phương thức response.render
có thể hoạt động được. Ở đây nếu như bạn cũng chọn ejs
giống như mình thì có thể lưu ý nhanh cách sử dụng cơ bản - đó là ở những thẻ ejs
hiển thị dữ liệu của các biến thì chúng ta xuất phát bằng ký hiệu mở đầu là <%=
có thêm dấu =
so với các đoạn logic hiển thị khác.
<!doctype html>
<html lang="en">
<head> <title><%= title %></title> <link rel="stylesheet" href="/asset/style.css">
</head>
<body> <h1><%= heading %></h1> <div> <% if (content) { %> <%= content %> <% } else { %> <p>Trang web đang trong quá trình xây dựng...</p> <% } %> </div> <script src="/asset/main.js"></script>
</body>
</html>
Để sử dụng một thành phần cho nhiều trang đơn khác nhau ví dụ như thanh điều hướng navbar
thì chúng ta có thể tạo ra một tệp template
riêng cho thành phần này. Sau đó include
vào code của các tệp template
đại diện cho các trang đơn.
<nav>Thanh điều hướng chính</nav>
<!doctype html>
<html lang="en">
<head> <title><%= title %></title> <link rel="stylesheet" href="/asset/style.css">
</head>
<body> <%- include('component/navbar', {index: 1}) %> <!-- ... -->
</body>
</html>
Đối với ejs
mà mình đang sử dụng trong code ví dụ ở đây thì các thẻ include
sẽ mở đầu bằng ký hiệu <%-
có thêm dấu -
so với các thẻ ejs
thông thường. Và đường dẫn tới tệp ejs
của navbar
là đường dẫn ở dạng tương quan tính từ vị trí của tệp ejs
đang muốn include
thanh navbar
.
Mình thường tạo ra một thư mục component
bên trong thư mục view
để chứa các thành phần của trang web như vậy; Còn các tệp template
đại diện các trang đơn thường được gọi là layout
sẽ được đặt ngay ở cấp cao nhất của thư mục view
. Cái này là tùy cách sắp xếp của mỗi người thôi; Vì vậy nên bạn cứ thoải mái tạo ra logic lưu trữ các tệp template
của bạn. Miễn sao phù hợp với logic lúc viết code sử dụng là được.
Kết thúc bài viết
Như vậy là tính tới thời điểm hiện tại, chúng ta đã biết cách cấu trúc các trang đơn bằng code logic hiển thị. Việc này giúp cho chúng ta không cần phải viết lặp lại code HTML cho từng trang đơn cụ thể; Và khi cần thực hiện chỉnh sửa giao diện của trang web, chúng ta sẽ không cần phải copy/paste
code chỉnh sửa cho 1001 bài viết blog nữa.
Việc cần làm tiếp theo là chúng ta sẽ cần tổ chức lưu trữ dữ liệu của 1001 bài viết blog ở dạng đơn giản hơn và thân thiện hơn đối với thao tác khởi tạo hay chỉnh sửa nội dung - đứng từ góc nhìn của một người sử dụng blog. Việc này cũng có nghĩa là chúng ta sẽ cần xây dựng thêm giao diện đăng nhập
và viết bài
cho người quản trị blog; Và đoạn code điều hướng yêu cầu cần xử lý sẽ trở nên phức tạp hơn một chút. Do đó chúng ta sẽ cần chọn lựa ra một phương thức lưu trữ dữ liệu của các bài viết và chuẩn bị thêm một ít kiến thức về các thao tác điều hướng yêu cầu căn bản.
Về việc tổ chức và lưu trữ dữ liệu thì đây không phải là nội dung thuộc về framework
và thực ra là một chủ đề độc lập. Do đó chúng ta sẽ tạo ra một Sub-Series
riêng dành cho chủ đề này.
(Sắp đăng tải) [Database] Bài 1 - Cơ Sở Dữ Liệu Là Cái Gì?
Và phần kiến thức cần tìm hiểu thêm về các thao tác điều hướng căn bản trong ExpressJS sẽ chính là nội dung thảo luận chính của chúng ta trong bài viết tiếp theo.
(Sắp đăng tải) [ExpressJS] Bài 3 - Điều Hướng Cơ Bản