Sidebar & Dialog (HtmlService)

Thời gian đọc: ~35 phút

HtmlService Là Gì?

HtmlService cho phép Apps Script hiển thị giao diện HTML tùy chỉnh trong Google Sheets dưới dạng:

  • Sidebar: Panel bên phải màn hình (tốt nhất cho form nhập liệu)
  • Modal Dialog: Hộp thoại popup ở giữa màn hình
  • Web App: Trang web độc lập chạy từ script.google.com/exec

Tạo Sidebar HTML

Tạo 2 file trong project: Code.gs (server-side) và Sidebar.html (client-side):

// Code.gs
function showSidebar() {
  const html = HtmlService.createHtmlOutputFromFile('Sidebar')
    .setTitle('Cong Cu Nhap Lieu')
    .setWidth(320);
  SpreadsheetApp.getUi().showSidebar(html);
}

// Ham duoc goi tu HTML qua google.script.run
function addProductRow(data) {
  const sheet = SpreadsheetApp.getActiveSheet();
  sheet.appendRow([
    data.name,
    data.price,
    data.qty,
    data.category,
    new Date()
  ]);
  return { success: true, row: sheet.getLastRow() };
}

function getCategories() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName('Danh muc');
  if (!sheet) return [];
  return sheet.getDataRange().getValues()
    .slice(1)
    .map(function(row) { return row[0]; })
    .filter(Boolean);
}
/* Sidebar.html */
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    body { font-family: Arial, sans-serif; padding: 16px; }
    input, select { width: 100%; padding: 8px; margin: 4px 0 12px; box-sizing: border-box; }
    button { background: #4285F4; color: white; padding: 10px 20px; border: none; cursor: pointer; width: 100%; }
  </style>
</head>
<body>
  <h3>Them San Pham</h3>
  <label>Ten san pham</label>
  <input type="text" id="name" placeholder="Nhap ten...">
  <label>Don gia (VND)</label>
  <input type="number" id="price" placeholder="299000">
  <label>So luong</label>
  <input type="number" id="qty" value="1">
  <label>Danh muc</label>
  <select id="category"><option>Dang tai...</option></select>
  <br>
  <button onclick="submitForm()">Them vao Sheet</button>
  <div id="status"></div>

  <script>
    // Tai danh muc khi mo sidebar
    google.script.run
      .withSuccessHandler(loadCategories)
      .getCategories();

    function loadCategories(cats) {
      const sel = document.getElementById('category');
      sel.innerHTML = cats.map(function(c) {
        return '<option>' + c + '</option>';
      }).join('');
    }

    function submitForm() {
      const data = {
        name: document.getElementById('name').value,
        price: Number(document.getElementById('price').value),
        qty: Number(document.getElementById('qty').value),
        category: document.getElementById('category').value
      };

      if (!data.name || !data.price) {
        document.getElementById('status').innerHTML = '<p style="color:red">Vui long dien du thong tin!</p>';
        return;
      }

      google.script.run
        .withSuccessHandler(function(result) {
          document.getElementById('status').innerHTML = '<p style="color:green">Da them vao hang ' + result.row + '!</p>';
          document.getElementById('name').value = '';
        })
        .withFailureHandler(function(err) {
          document.getElementById('status').innerHTML = '<p style="color:red">Loi: ' + err.message + '</p>';
        })
        .addProductRow(data);
    }
  </script>
</body>
</html>

google.script.run — Giao Tiếp 2 Chiều

google.script.run là API cho phép code JavaScript trong HTML gọi hàm Apps Script (server-side):

// Cu phap co ban
google.script.run
  .withSuccessHandler(function(result) { /* xu ly ket qua */ })
  .withFailureHandler(function(error) { /* xu ly loi */ })
  .tenHamServerSide(thamSo1, thamSo2);

// Truyen du lieu phuc tap (object, array)
google.script.run
  .withSuccessHandler(onSuccess)
  .saveData({ name: 'test', values: [1, 2, 3] });
// Hien thi modal (to hon sidebar)
function showModal() {
  const html = HtmlService.createHtmlOutputFromFile('Modal')
    .setWidth(600)
    .setHeight(400);
  SpreadsheetApp.getUi().showModalDialog(html, 'Tao bao cao');
}

// Dong modal tu HTML
// Trong HTML: google.script.host.close();
Best practice: Sidebar phù hợp cho form nhập liệu liên tục (không cần đóng). Modal phù hợp cho thao tác một lần cần confirm rồi đóng. Web App phù hợp khi cần truy cập bên ngoài Sheets.