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

[ExpressJS] Bài 8 - Viết Code Điều Hành Blog Cá Nhân (Tiếp Theo)

0 0 7

Người đăng: Semi Art

Theo Viblo Asia

Trong bài này, chúng ta sẽ bắt đầu viết code điều hành cho nhóm route/article. Tuy nhiên trước khi bắt tay vào viết code xử lý cho các route mới, chúng ta hãy cùng nhìn lại code xử lý yêu cầu xem trang chủ đã được xử lý trong bài trước.

router.get("/", async (request, response) => { var data = new Data(); /* --- Data for Meta */ var logoText = config.get("logo-text"); data.set("title", `${logoText} | Trang Chủ`); /* --- Data for Topnav */ data.set("logo-text", logoText); var categoryList = []; await databaseManager.execute( Category.name, "select", categoryList, Infinity, "default", ["@id", "name"] ); for (var category of categoryList) { var id = category.get("@id"); category.set("url", `/category/view/${id}`); } data.set("category-list", categoryList.slice(0, -1)); /* --- Data for Header */ data.set("heading", config.get("site-heading")) .set("description", config.get("site-description")); /* --- Data for Entry */ var articleList = []; await databaseManager.execute( Article.name, "select", articleList, 10, "reversed", ["@id", "title", "content"] ); var enryList = []; for (var article of articleList.slice(1)) { var contentMarkdown = article.get("content"); var excerptMarkdown = contentMarkdown.slice(0, 300); var entry = new Entry(); entry.set("title", article.get("title")) .set("excerpt", `${excerptMarkdown}...`) .set("url", `/article/view/${article.get("@id")}`); enryList.push(entry); } data.set("entry-list", enryList); /* --- Render the Page */ response.render("index", { layout: "home", action: null, data });
}); // router.get

Tất cả nội dung công việc mà chúng ta đã xử lý trong đoạn code này chỉ là truy vấn dữ liệu từ database, và map (chuyển đổi) sang kiểu dữ liệu phù hợp để cung cấp cho các thành phần trong giao diện trang chủ.

Code điều hành của các route khác về cơ bản sẽ không có gì khác biệt nhiều đối với dạng yêu cầu xem nội dung của một trang đơn nào đó, bất kể đó là trang danh mục hay trang bài viết... Như vậy các tác vụ truy vấn dữ liệu cho mỗi thành phần trong giao diện cần trình bày sẽ có khả năng lặp lại rất cao; Mặc dù quá trình chuyển đổi từ dữ liệu truy vấn được sang kiểu dữ liệu phù hợp cho view có thể sẽ khác ở mỗi route.

Sẽ thật thuận tiện nếu như chúng ta phân tách các tác vụ nhỏ này thành các sub-procedure để sử dụng chung cho các route; Và đồng thời cung cấp các tham số tùy chọn để điều chỉnh phương thức hoạt động của mỗi sub-procedure này để phù hợp với từng route cụ thể. Như vậy chúng ta sẽ có thể tập trung code xử lý cho các route ở cùng một nơi, và có thể so sánh phương thức hoạt động ở các route dễ dàng hơn khi cần thực hiện bất kỳ thao tác điều chỉnh nào.

Cấu trúc lại code với các sub-procedure

[express-blog]
. |
. +-----[route]
. |
. +-----home.js
. +-----[sub-procedure]
. |
. +-----get-data-for-meta.js
. +-----get-data-for-topnav.js
. +-----get-data-for-header.js
. +-----get-data-for-entry.js

Và đây là kết quả mà chúng ta hướng đến sau khi cấu trúc lại code.

const express = require("express");
const Data = require("../view/type/Data");
const getDataForMeta = require("./sub-procedure/get-data-for-meta");
const getDataForTopnav = require("./sub-procedure/get-data-for-topnav");
const getDataForHeader = require("./sub-procedure/get-data-for-header");
const getDataForEntry = require("./sub-procedure/get-data-for-entry"); const router = express.Router(); router.get("/", async (request, response) => { var data = new Data(); await getDataForMeta(data, "home"); await getDataForTopnav(data); await getDataForHeader(data, "home"); await getDataForEntry(data, "home"); response.render("index", { layout: "home", action: null, data });
}); // router.get module.exports = router;

Ở đây các thao tác getDataForComponent sẽ lần lượt thực hiện truy vấn và chuyển đổi kiểu dữ liệu để cung cấp cho các thành phần giao diện và thêm vào map Data.

Ngoại trừ thanh điều hướng topnav, tất cả các thành phần khác khi xuất hiện trong các layout khác nhau và với tham số action khác nhau sẽ cần thực hiện thao tác truy vấn và chuyển đổi dữ liệu với logic khác nhau. Do đó chúng ta sẽ cần cung cấp các tham số tùy chọn là layoutaction trong phần định nghĩa của các sub-procedure để code sử dụng ở các route có thể điều chỉnh phương thức hoạt động của các sub-procedure.

get-data-for-meta

const Data = require("../../view/type/Data");
const config = require("../../config"); module.exports = async ( out_data = new Data(), in_layout = "home" /* home | category | article | admin | oops */
) => { if (in_layout == "home") getMetaForHome(out_data); else throw new Error("Unsupported layout");
}; // module.exports const getMetaForHome = ( out_data = new Data()
) => { var logoText = config.get("logo-text"); out_data.set("title", `${logoText} | Trang Chủ`);
};

Đối với phần nội dung trong thẻ <head> thì hiện tại chúng ta chỉ có <title> thay đổi theo các trang đơn và bạn có thể bổ sung tham số action nếu cảm thấy cần thiết. Tạm thời thì chúng ta có thể xuất phát với khối module.exports đang hỗ trợ duy nhất layout: "home" như ở trên. Những layout khác sẽ được hỗ trợ bổ sung thêm khi chúng ta thực hiện tới.

get-data-for-topnav

const databaseManager = require("../../database/manager");
const config = require("../../config");
const Category = require("../../database/type/Category");
const Data = require("../../view/type/Data"); module.exports = async ( out_data = new Data()
) => { var logoText = config.get("logo-text"); out_data.set("logo-text", logoText); var categoryList = []; await databaseManager.execute( Category.name, "select", categoryList, Infinity, "default", ["@id", "name"] ); for (var category of categoryList) { var id = category.get("@id"); category.set("url", `/category/view/${id}`); } out_data.set("category-list", categoryList.slice(0, -1));
}; // module.exports

Riêng đối với thành phần topnav thì mình sử dụng thiết kế đơn giản và vì vậy không cần bổ sung thêm tham số tùy chọn nào cả. Trong trường hợp muốn xác định trang đơn đang hiển thị thuộc danh mục nào để tạo hiệu ứng hiển thị cho liên kết tương ứng thì chúng ta sẽ cần thêm cả hai tham số hỗ trợ layoutaction.

get-data-for-header

const Data = require("../../view/type/Data");
const config = require("../../config"); module.exports = async ( out_data = new Data(), in_layout = "home", /* home | category | oops */ in_categoryId = "Infinity"
) => { if (in_layout == "home") await getHeaderForHome(out_data); else throw new Error("Unsupported layout");
}; // module.exports const getHeaderForHome = async ( out_data = new Data()
) => { out_data .set("heading", config.get("site-heading")) .set("description", config.get("site-description"));
}; // getHeaderForHome

Khối header được sử dụng lại cho các layout là trang chủ home và các trang đơn danh mục category. Trong trường hợp là một trang đơn hiển thị danh mục, chúng ta cần xác định thông tin về danh mục đó để hiển thị, và vì vậy sẽ cần cung cấp thêm tham số categoryId.

get-data-for-entry

const databaseManager = require("../../database/manager");
const Article = require("../../database/type/Article");
const Data = require("../../view/type/Data");
const Entry = require("../../view/type/Entry"); module.exports = async ( out_data = new Data(), in_layout = "home" /* home | category | admin */
) => { if (in_layout == "home") await getEntryListForHome(out_data); else throw new Error("Unsupported Layout");
}; // module.exports const getEntryListForHome = async ( out_data = new Data()
) => { var entryList = []; var articleList = []; await databaseManager.execute( Article.name, "select", articleList, 10, "reversed", ["@id", "title", "content"] ); for (var article of articleList.slice(1)) { var entry = new Entry() .set("title", article.get("title")) .set("excerpt", article.get("content")) .set("url", `/article/view/${article.get("@id")}`); entryList.push(entry); } // for .. of out_data.set("entry-list", entryList);
}; // getEntryListForHome

Khối entry được hiển thị các trích đoạn ngắn và liên kết tới các bài viết tương ứng được sử dụng cho trang chủ home, các trang đơn danh mục category, và trong trường hợp hiển thị trong một trang quản trị admin thì các liên kết sẽ được sử dụng để trỏ tới /article/edit/:id để mở giao diện chỉnh sửa bài viết.

Kết thúc bài viết

Như vậy là chúng ta đã thực hiện xong việc tái cấu trúc lại code xử lý cho route cơ bản đầu tiên và chuẩn bị cho khối route/article. Trong bài viết tiếp theo, chúng ta sẽ viết code cho route đầu tiên của khối này để xử lý yêu cầu xem nội dung của một bài viết.

(Sắp đăng tải) [ExpressJS] Bài 9 - Viết Code Điều Hành Blog Cá Nhân (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 499

- 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 414

- 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 136

- 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 117

- 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 93

- 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 229