Read more »

Để làm một ứng dụng web app chạy mượt mà ngay trên Blogger (Blogspot) và sử dụng IndexedDB làm cơ sở dữ liệu lưu trữ offline, bạn hoàn toàn có thể gom toàn bộ code thành một file HTML duy nhất (bao gồm cả HTML, CSS và JavaScript).

Blogger cho phép bạn tạo một trang tĩnh (Page) và nhúng code trực tiếp vào đó. Dưới đây là hướng dẫn từng bước và bộ code mẫu chuẩn chỉnh để bạn làm kem nền cho ứng dụng của mình.

1. Hiểu cách hoạt động của hệ thống

  • Giao diện & Logic: Chạy hoàn toàn dưới client (trình duyệt của người dùng) bằng HTML5, CSS3 và JavaScript.

  • Database (IndexedDB): Là cơ sở dữ liệu NoSQL lưu trữ ngay trong trình duyệt của người dùng. Dung lượng lưu trữ lớn (lên đến hàng trăm MB), không bị mất dữ liệu khi tắt trình duyệt (trừ khi xóa cache trang web).

  • Nền tảng (Blogspot): Đóng vai trò như một hosting miễn phí để chứa và hiển thị file HTML này.

2. Mã nguồn mẫu (All-in-One HTML)

Bạn hãy copy toàn bộ đoạn code dưới đây. Code này đã được cấu trúc sẵn để tạo một ứng dụng quản lý dữ liệu cơ bản (Thêm, Đọc, Xóa) sử dụng IndexedDB, giao diện tối giản và trực quan.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web App Local Database</title>
    <style>
        /* CSS Giao diện tối giản, responsive */
        :root {
            --primary-color: #007bff;
            --danger-color: #dc3545;
            --bg-color: #f4f6f9;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: var(--bg-color);
            margin: 0;
            padding: 20px;
            color: #333;
        }
        .container {
            max-width: 600px;
            margin: 0 auto;
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        h2 { text-align: center; color: #222; }
        .form-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            width: 100%;
            padding: 10px;
            background-color: var(--primary-color);
            border: none;
            color: white;
            font-size: 16px;
            font-weight: bold;
            border-radius: 4px;
            cursor: pointer;
            transition: background 0.2s;
        }
        button:hover { background-color: #0056b3; }
        .data-list {
            margin-top: 25px;
            padding: 0;
            list-style: none;
        }
        .data-item {
            background: #f9f9f9;
            padding: 12px;
            border: 1px solid #eee;
            margin-bottom: 8px;
            border-radius: 4px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .btn-delete {
            background-color: var(--danger-color);
            width: auto;
            padding: 5px 10px;
            font-size: 13px;
        }
        .btn-delete:hover { background-color: #bd2130; }
    </style>
</head>
<body>

<div class="container">
    <h2>Web App Manager</h2>
    
    <div class="form-group">
        <label for="inputKey">Item Code / Name:</label>
        <input type="text" id="inputKey" placeholder="e.g., PROD001">
    </div>
    <div class="form-group">
        <label for="inputValue">Details / Value:</label>
        <input type="text" id="inputValue" placeholder="e.g., Premium Pack">
    </div>
    <button id="btnSave">Save Data</button>

    <ul id="dataList" class="data-list"></ul>
</div>

<script>
    // --- CẤU HÌNH INDEXEDDB ---
    const dbName = "WebAppDB";
    const dbVersion = 1;
    const storeName = "MainStore";
    let db;

    // Khởi tạo kết nối Database
    const request = indexedDB.open(dbName, dbVersion);

    request.onupgradeneeded = function(e) {
        db = e.target.result;
        // Tạo Object Store nếu chưa tồn tại (Dùng thuộc tính 'id' làm key chính)
        if (!db.objectStoreNames.contains(storeName)) {
            db.createObjectStore(storeName, { keyPath: "id" });
        }
    };

    request.onsuccess = function(e) {
        db = e.target.result;
        renderData(); // Tải dữ liệu lên giao diện khi kết nối thành công
    };

    request.onerror = function(e) {
        console.error("Database error: " + e.target.errorCode);
    };

    // --- CÁC HÀM XỬ LÝ CRUD DỮ LIỆU ---

    // 1. Thêm / Cập nhật dữ liệu
    document.getElementById('btnSave').addEventListener('click', () => {
        const id = document.getElementById('inputKey').value.trim();
        const value = document.getElementById('inputValue').value.trim();

        if (!id || !value) {
            alert("Please fill in both fields!");
            return;
        }

        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);
        
        const record = { id: id, dataValue: value, timestamp: new Date().getTime() };
        const putRequest = store.put(record); // put() sẽ thêm mới hoặc ghi đè nếu trùng id

        putRequest.onsuccess = () => {
            // Reset form
            document.getElementById('inputKey').value = '';
            document.getElementById('inputValue').value = '';
            renderData(); // Cập nhật lại danh sách hiển thị
        };
    });

    // 2. Đọc và Hiển thị dữ liệu ra màn hình
    function renderData() {
        const dataList = document.getElementById('dataList');
        dataList.innerHTML = ''; // Xóa danh sách cũ

        const transaction = db.transaction([storeName], "readonly");
        const store = transaction.objectStore(storeName);
        const cursorRequest = store.openCursor(); // Duyệt qua toàn bộ record

        cursorRequest.onsuccess = function(e) {
            const cursor = e.target.result;
            if (cursor) {
                const li = document.createElement('li');
                li.className = 'data-item';
                li.innerHTML = `
                    <div>
                        <strong>${cursor.value.id}</strong>: ${cursor.value.dataValue}
                    </div>
                    <button class="btn-delete" onclick="deleteData('${cursor.value.id}')">Delete</button>
                `;
                dataList.appendChild(li);
                cursor.continue(); // Chuyển sang record tiếp theo
            }
        };
    }

    // 3. Xóa dữ liệu theo ID
    function deleteData(id) {
        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);
        const deleteRequest = store.delete(id);

        deleteRequest.onsuccess = () => {
            renderData(); // Cập nhật lại giao diện sau khi xóa
        };
    }
</script>

</body>
</html>

3. Cách đưa code vào Blogspot chi tiết

Để ứng dụng hoạt động như một trang độc lập, sạch sẽ và không bị dính các thành phần giao diện của theme (như thanh bên, thanh tìm kiếm, bình luận...), bạn nên tạo một Trang tĩnh (Page) và tối ưu như sau:

Bước 1: Tạo Trang mới

  1. Đăng nhập vào quản trị Blogger.com.

  2. Chọn blog bạn muốn sử dụng.

  3. Ở menu bên trái, chọn mục Trang (Pages) > Chọn Trang mới (New Page).

Bước 2: Dán mã HTML

  1. Tại trình soạn thảo trang, nhìn lên góc trên bên trái, bấm vào biểu tượng chiếc bút và chuyển sang chế độ Chế độ xem HTML (HTML view).

  2. Xóa toàn bộ các ký tự mặc định đang có sẵn trong khung soạn thảo.

  3. Copy toàn bộ đoạn code mẫu ở mục 2 và dán vào.

Bước 3: Cấu hình cài đặt trang (Quan trọng)

Ở menu cài đặt bên phải trang soạn thảo, bạn cần tinh chỉnh để giao diện ứng dụng không bị lỗi:

  • Bình luận của độc giả: Chọn Không cho phép, ẩn các bình luận hiện có.

  • Chế độ soạn thảo: Chọn Sử dụng thẻ <br> thay vì <p> (Điều này giúp Blogger không tự động chèn các thẻ <p> làm phá vỡ cấu trúc code JavaScript của bạn).

Bước 4: Xuất bản và Kiểm tra

  1. Nhập tiêu đề cho trang (Ví dụ: App Quan Ly Data).

  2. Bấm Xuất bản (Publish).

  3. Nhấn vào biểu tượng hình con mắt (Xem/View) để mở trang web app của bạn lên. Thử nhập một vài dữ liệu, nhấn Save, sau đó F5 làm mới trang để kiểm tra xem dữ liệu có lưu dưới IndexedDB và giữ nguyên hay không.

4. Lưu ý khi phát triển nâng cao trên Blogspot

  • Tên miền và Bảo mật: IndexedDB được quản lý theo cơ chế Same-Origin Policy (Cùng nguồn). Tức là dữ liệu lưu ở trang abc.blogspot.com sẽ không thể truy cập được từ xyz.blogspot.com. Nếu sau này bạn đổi sang tên miền riêng (ví dụ: my-app.com), cơ sở dữ liệu cũ trên subdomain blogspot.com sẽ không tự chuyển sang tên miền mới được.

  • Đồng bộ hóa dữ liệu: Vì IndexedDB nằm hoàn toàn ở máy người dùng (Offline), nếu bạn muốn làm tính năng sao lưu (Backup), hãy viết thêm hàm xuất dữ liệu ra file JSON (JSON.stringify()) để người dùng tải về máy, hoặc đẩy dữ liệu lên một Cloud API bên ngoài khi có kết nối mạng.


Web App Manager