- vừa được xem lúc

[EJS] Bài 2 - Viết Code Xây Dựng Giao Diện Trang Blog Đơn Giản

0 0 22

Người đăng: Semi Art

Theo Viblo Asia

Trong bài viết này, chúng ta sẽ bắt đầu xây dựng giao diện blog đơn giản bằng EJS để sử dụng làm chất liệu cho code server trên nền ExpressJS mà chúng ta đang xây dựng.

Logic khởi điểm

Ở phần này mình xin trích đoạn lại toàn bộ phần thảo luận về cấu trúc thư mục view trong bài viết trước của Sub-Series ExpressJS. Chúng ta sẽ không tạo ra những tệp template thụ động với các biến gắn dữ liệu đơn giản mà sẽ tạo ra một phần mềm xây dựng giao diện người dùng bằng EJS.

[ExpressJS] Bài 6 - Viết Code Điều Hành Một Blog Cá Nhân Đơn Giản

Đối với việc cấu trúc thư mục view thì chắc chắn là mỗi người chúng ta sẽ có một cách sắp xếp riêng. Tuy nhiên thì về cơ bản sẽ chỉ có 2 lối tư duy khởi điểm:

Cách đầu tiên, chúng ta có thể xem như các tệp trong view là dạng template thụ động không có chứa kiến trúc logic mà chỉ có các biến chờ gắn dữ liệu để hiển thị. Đối với cách này thì khi code xử lý ở một route nào đó cần render sẽ cần tìm tới chính xác tệp template phù hợp với mục đích hiển thị kết quả của route đó. Ví dụ:

[express-blog]
. |
. +-----[view]
. |
. +-----home.ejs
. +-----oops.ejs
. |
. +-----[article]
. |
. +-----view.ejs
. +-----edit.ejs
router.get("/", async (request, response) => { var data = {}; /* truy vấn dữ liệu từ database -> data */ /* cung cấp đường dẫn tới tệp home.ejs */ response.render("home.ejs", { data });
});
router.get("/:id", async (request, response) => { var { id } = request.params; var data = {}; /* truy vấn dữ liệu từ database -> data */ /* cun cấp đường dẫn tới tệp article/view.ejs */ response.render("article/view.ejs", { data });
});

Cách thứ hai, là chúng ta có thể nhìn nhận khối view ở dạng một phần mềm vẽ giao diện người dùng có chứa logic xử lý riêng và có một tệp đại diện ví dụ như index.ejs. Code xử lý ở các route sẽ chỉ sử dụng duy nhất tệp này để render và truyền các tham số dữ liệu vào để mô tả giao diện muốn hiển thị. Lúc này code logic trong index.ejs sẽ phân tích dữ liệu được truyền vào để kiến trúc nên giao diện hiển thị phù hợp.

[express-blog]
. |
. +-----[view]
. |
. +-----index.ejs
. |
. +-----[layout]
. |
. +-----home.ejs
. +-----oops.ejs
. +-----article.ejs
router.get("/", async (request, response) => { var data = {}; /* truy vấn dữ liệu từ database -> data */ response.render("index.ejs", { layout: "home", data });
});
router.get("/:id", async (request, response) => { var { id } = request.params; var data = {}; /* truy vấn dữ liệu từ database -> data */ response.render("index.ejs", { layout: "article", action: "view", data });
});

Cách xử lý đầu tiên sẽ đơn giản hơn nhưng khi chúng ta cập nhật giao diện người dùng và nếu có sự thay đổi về cấu trúc thư mục bên trong view thì sẽ cần phải sửa lại cả ở code xử lý của các route. Trong khi đó thì cách xử lý thứ hai rất linh động để chỉnh sửa hoặc tái cấu trúc lại thư mục view khi cần thiết nhưng lại yêu cầu thiết lập ban đầu hơi rườm rà hơn một chút.

Thiết lập ban đầu

Như đã nói thì chúng ta sẽ nhìn nhận thư mục view là một phần mềm xây dựng giao diện người dùng. Câu lệnh khởi điểm để kích hoạt phần mềm này là:

response.render("index.ejs", { layout, action, data });

Ở đây các tham số đầu vào layoutaction giúp code logic trong index.ejs phân tích và lựa chọn các thành phần cần thiết để cấu trúc nên văn bản HTML phù hợp kèm theo dữ liệu được nạp từ data.

Xuyên suốt Sub-Series này, chúng ta sẽ không quan tâm tới các khối databaseroute; mà thay vào đó sẽ chỉ quan tâm tới thư mục view, public, và một vài tệp JavaScript trong express-blog:

  • express-blog/public/ - Thư mục chứa các tệp CSS và client-side JavaScript.
  • express-blog/view/ - Thư mục chứa các tệp template.
  • express-blog/test.js - Khởi tạo một server chạy thử code template.
  • express-blog/data.js - Giả lập dữ liệu truy vấn được từ database.

Và chúng ta sẽ xuất phát với cấu trúc thư mục đơn giản như thế này:

[express-blog]
. |
. +-----[public]
. | |
. | +-----[css]
. | | |
. | | +-----style.css
. | |
. | +-----[js]
. | |
. | +-----main.js
. |
. +-----[view]
. | |
. | +-----index.ejs
. |
. +-----test.js
. +-----data.js

Code thiết lập ban đầu cho các tệp trong thư mục public.

* { box-sizing: border-box; margin: 0; padding: 0; font-family: "Times New Roman", "Times", serif; font-size: 45px; line-height: 1.618;
} body { padding-top: 90px; text-align: center;
}
console.log("Client-side JavaScript");

Code thiết lập ban đầu cho các tệp trong thư mục view.

<!doctype html>
<html>
<head> <title> <%= data["title"] %> </title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="stylesheet" href="/css/style.css" />
</head>
<body> <h1> <%= data["title"] %> </h1> <script src="/js/main.js"></script>
</body>
</html>

Code thiết lập ban đầu cho các tệp trong thư mục gốc express-blog.

const data = { "title": "Hello EJS !"
}; module.exports = data;
const path = require("path");
const express = require("express");
const lessMiddleware = require("less-middleware"); var app = express(); var pathToView = path.join(__dirname, "view");
var pathToPublic = path.join(__dirname, "public"); app.set("views", pathToView);
app.set("view engine", "ejs"); app.use(lessMiddleware(pathToPublic));
app.use(express.static(pathToPublic)); app.get("*", async (request, response) => { response.render("index.ejs", { layout: null, action: null, data: require("./data.js") });
}); app.listen(8080, (_) => console.log("Server started"));

Chạy thử server test.

npm test Server started

http://localhost:8080/

Tham số layout

Tham số layout - dịch nôm na là bố cục - được sử dụng để định vị bố cục chính được sử dụng cho trang đơn HTML kết quả.

Giao diện blog đơn giản mà chúng ta đang xây dựng sẽ có giao diện trang chủ, giao diện các trang danh mục, giao diện các trang bài viết, giao diện các trang quản trị, và giao diện trang thông báo lỗi.

[view]
. |
. +-----[layout]
. | |
. | +-----home.ejs
. | +-----category.ejs
. | +-----article.ejs
. | +-----admin.ejs
. | +-----oops.ejs
. |
. +-----index.ejs

Lệnh khởi đầu của phần mềm view có thể sử dụng tham số này để chọn ra tệp layout phù hợp. Và chuyển tiếp các tham số actiondata cho tệp xử lý giao diện tiếp theo.

<%# --- Các tham số --- layout: home | category | article | admin | oops action: view | edit data : { title }
%> <% const makeHTML = function(layoutName) { if (layoutName == "home") return include("./layout/home", { action, data }); if (layoutName == "category") return include("./layout/category", { action, data }); if (layoutName == "article") return include("./layout/article", { action, data }); if (layoutName == "admin") return include("./layout/admin", { action, data }); if (layoutName == "oops") return include("./layout/oops", { action, data }); if ("any-other-case") return `<h1>Unsupported layout</h1>`; }; // makeHTML var HTMLcode = makeHTML(layout);
%> <%- HTMLcode %>

Lúc này code HTML tạo bố cục sẽ được di chuyển vào các tệp layout.

<!doctype html>
<html>
<head> <title> <%= data["title"] %> </title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="stylesheet" href="/css/style.css" />
</head>
<body> <h1> <%= data["title"] %> </h1> <script src="/js/main.js"></script>
</body>
</html>

Bây giờ chúng ta có thể chỉnh sửa lại route duy nhất trong tệp test.js để thử các giá trị khác nhau của tham số layout. Trong code ví dụ ở đây mình sẽ thử với layouthome và chỉnh lại titledata.js thành Trang Chủ.

const data = { "title": "Trang Chủ"
}; module.exports = data;
app.get("*", async (request, response) => { response.render("index.ejs", { layout: "home", action: null, data : require("./data.js") });
});
npm start Server started

http://localhost:8080/

Tham số action

Nói riêng đối với các giao diện hiển thị các bài viết và nội dung giới thiệu các trang danh mục. Chúng ta sẽ cần cung cấp giao diện xem thông tin cho người đọc blog và giao diện chỉnh sửa nội dung cho người quản trị blog. Việc cung cấp thêm tham số action sẽ giúp cho câu lệnh render trở nên linh hoạt hơn so với việc sử dụng tên layout có dạng như view-article hay edit-article.

Lúc này chúng ta có thể tạo thêm logic phân nhánh cho các layout của các trang bài viết và các trang danh mục như sau.

[view]
. |
. +-----[layout]
. | |
. | +-----home.ejs
. | +-----admin.ejs
. | +-----oops.ejs
. | |
. | +-----[category]
. | | |
. | | +-----[action]
. | | | |
. | | | +-----view.ejs
. | | | +-----edit.ejs
. | | |
. | | +-----index.ejs
. | |
. | +-----[article]
. | |
. | +-----[action]
. | | |
. | | +-----view.ejs
. | | +-----edit.ejs
. | |
. | +-----index.ejs
. |
. +-----index.ejs

Như vậy đối với layoutarticlecategory thì chúng ta cần sửa lại là include tệp index.ejs ở thư mục tương ứng và đồng thời chuyển tiếp tham số actiondata.

 const makeHTML = function(layoutName) { ... if (layoutName == "category") return include("./layout/category/index", { action, data }); if (layoutName == "article") return include("./layout/article/index", { action, data }); ... }; // makeHTML var HTMLcode = makeHTML(layout);
%> <%- HTMLcode %>

Ở các tệp index.ejs trong thư mục layout/articlelayout/category, chúng ta tiếp tục tạo logic rẽ nhánh với giá trị của tham số action.

<%# --- Các tham số --- action: view | edit data : { title }
%> <% const makeHTML = function(actionType) { if (actionType == "view") return include("./action/view", { data }); if (actionType == "edit") return include("./action/edit", { data }); if ("any-other-case") return `<h1>Unsupported layout</h1>`; }; // makeHTML var HTMLcode = makeHTML(action);
%> <%- HTMLcode %>

Giao diện view.ejs để đọc nội dung sẽ hiển thị tiêu đề bằng thẻ <h1>.

<!doctype html>
<html>
<head> <title> <%= data["title"] %> </title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="stylesheet" href="/css/style.css" />
</head>
<body> <h1> <%= data["title"] %> </h1> <script src="/js/main.js"></script>
</body>
</html>

Giao diện edit.ejs để chỉnh sửa nội dung sẽ hiển thị tiêu đề trong ô nhập liệu <input>.

<!doctype html>
<html>
<head> <title> <%= data["title"] %> </title> <meta charset="utf-8" /> <meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <link rel="stylesheet" href="/css/style.css" />
</head>
<body> <input type="text" value=" <%= data["title"] %> " /> <script src="/js/main.js"></script>
</body>
</html>

Bây giờ chúng ta sẽ thử yêu cầu render với layout: "article"action: "edit" để chỉnh sửa nội dung của một bài viết.

const data = { "title": "Giới Thiệu EJS"
}; module.exports = data;
app.get("*", async (request, response) => { response.render("index.ejs", { layout: "article", action: "edit", data : require("./data.js") });
});
npm start Server started

http://localhost:8080/

Tham số data

Dữ liệu chính để cung cấp cho các giao diện được tập trung toàn bộ trong tham số data. Một object đơn giản chứa các object dữ liệu khác tùy vào yêu cầu dữ liệu từ các layout cụ thể sau khi chúng ta xây dựng giao diện chi tiết. Lưu ý duy nhất ở đây là chúng ta cần tổng kết cấu trúc của object này tại phần chú thích ở đầu các tệp template. Và chúng ta nên duy trì cách đặt tên thuộc tính có quy luật để sử dụng chung cho các layout.

Ví dụ các trang đơn đều sẽ có chung thanh điều hướng, cần dữ liệu là danh sách các danh mục - cần dữ liệu là một mảng các object danh mục, và thuộc tính bổ sung cho data sẽ là categoryList.

Trang chủ và các trang danh mục cần hiển thị một danh sách các đoạn trích ngắn - cần dữ liệu là một mảng các object bài viết, và thuộc tính bổ sung cho data sẽ là articleList.

Chúng ta sẽ bổ sung dần thuộc tính cho data trong quá trình xây dựng các template chi tiết. Hẹn gặp lại bạn trong bài viết tiếp theo.

(Sắp đăng tải) [EJS] Bài 3 - Viết Code Xây Dựng Giao Diện Blog (Tiếp Theo)

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 153

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 145

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 110

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 245