Làm việc với khối thả xuống

Google Docs API cho phép bạn tạo, chèn, cập nhật, đọc và quản lý các khối thả xuống trong tài liệu Google Tài liệu theo phương thức lập trình.

Khối thả xuống là gì?

Khối thả xuống trong Google Tài liệu cung cấp cho người dùng một trình đơn lựa chọn có tính tương tác và có thể tuỳ chỉnh ngay trong văn bản của tài liệu. Người dùng có thể nhấp vào một chip thả xuống để chọn trong danh sách các lựa chọn được xác định trước, mỗi lựa chọn có văn bản hiển thị và kiểu màu riêng. Khối thả xuống thường được dùng để theo dõi dự án, cập nhật trạng thái, quy trình xem xét và các giai đoạn phê duyệt.

Thông qua API cho Tài liệu, bạn có thể:

  • Xác định các mẫu trình đơn thả xuống có thể sử dụng lại với tiêu đề, tên tuỳ chọn và màu sắc tuỳ chỉnh.
  • Chèn các khối thả xuống tại một vị trí ký tự hợp lệ.
  • Cập nhật lựa chọn đã chọn của từng khối thả xuống.
  • Sửa đổi định nghĩa của trình đơn thả xuống dùng chung tại chỗ, đồng thời cập nhật tất cả các khối tham chiếu.
  • Thay thế hoặc loại bỏ các lựa chọn một cách an toàn trong khi vẫn duy trì tính toàn vẹn tham chiếu trên các thành phần hiện có.
  • Xoá các mẫu trình đơn thả xuống không dùng đến.

Kiến trúc: định nghĩa và các trường hợp

Trong Docs API, các khối thả xuống có định nghĩa và thực thể. Định nghĩa này đặt các lựa chọn cho một thực thể trình đơn thả xuống. Một phiên bản trình đơn thả xuống là một trình đơn thả xuống mà mọi người có thể tương tác và lưu thông tin về các lựa chọn.

Docs API tách cấu hình mẫu trình đơn thả xuống khỏi các thực thể chip thả xuống nội tuyến:

  1. DropdownDefinition: Một mẫu cấp thẻ xác định tiêu đề trình đơn thả xuống và tập hợp các lựa chọn có thể chọn (DropdownOption). Mẫu này không nằm ở một vị trí cụ thể trong tài liệu mà được lưu trữ trong bản đồ định nghĩa của thẻ: document.tabs[].documentTab.dropdownDefinitions.
  2. Trình đơn thả xuống: Một thực thể chip riêng lẻ được nhúng nội tuyến trong một phần tử đoạn văn (ParagraphElement.dropdown). Mỗi thực thể trình đơn thả xuống tham chiếu đến một dropdownDefinitionId và lưu trữ selectedOptionId đang hoạt động của riêng thực thể đó.

Việc sửa đổi một DropdownDefinition (ví dụ: thêm một lựa chọn hoặc đổi tên tiêu đề) sẽ cập nhật tất cả các thành phần tham chiếu trên thẻ mà không yêu cầu bạn cập nhật từng phần tử riêng lẻ.

Mỗi thực thể Dropdown sẽ theo dõi selectedOptionId riêng. Việc thay đổi giá trị đã chọn của một chip riêng lẻ chỉ ảnh hưởng đến phiên bản cụ thể đó.

Định dạng mã nhận dạng và quy tắc xác thực

Mã nhận dạng do người dùng cung cấp phải tuân thủ các quy cách nghiêm ngặt về định dạng, tiền tố và độ dài:

Số nhận dạng Tiền tố bắt buộc Biểu thức chính quy xác thực Giới hạn độ dài Ví dụ
Mã nhận dạng định nghĩa trình đơn thả xuống (dropdownDefinitionId) kix. ^kix\.[a-zA-Z0-9_-]{2,14}$ 6 – 18 ký tự kix.review_status
Mã nhận dạng lựa chọn trong trình đơn thả xuống (optionId) dropdownItem. ^dropdownItem\.[a-zA-Z0-9_-]{2,14}$ 15 – 27 ký tự dropdownItem.pending

Tạo mã nhận dạng do người dùng cung cấp

Để tạo mã nhận dạng tuân thủ các yêu cầu bắt buộc về tiền tố và biểu thức chính quy, hãy sử dụng các hàm trợ giúp sau. Các đối tượng hỗ trợ này tạo ra hậu tố chữ và số cơ sở 36 viết thường, khớp với định dạng do giao diện người dùng Google Tài liệu tạo ra:

Python

import random
import string

def generate_dropdown_definition_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with 'kix.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"kix.{suffix}"

def generate_dropdown_option_id(suffix_len: int = 8) -> str:
    """Generates a valid user-provided DropdownOption ID (15-27 chars, starting with 'dropdownItem.')."""
    chars = string.ascii_lowercase + string.digits
    suffix = "".join(random.choices(chars, k=max(2, min(suffix_len, 14))))
    return f"dropdownItem.{suffix}"

Java

import java.security.SecureRandom;

public final class DropdownIdGenerator {
  private static final String BASE36_CHARS =
      "abcdefghijklmnopqrstuvwxyz0123456789";
  private static final SecureRandom RANDOM = new SecureRandom();

  private DropdownIdGenerator() {}

  /** Generates a valid user-provided DropdownDefinition ID (6-18 chars, starting with "kix."). */
  public static String generateDefinitionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("kix.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }

  /** Generates a valid user-provided DropdownOption ID (15-27 chars, starting with "dropdownItem."). */
  public static String generateOptionId(int suffixLength) {
    int length = Math.max(2, Math.min(suffixLength, 14));
    StringBuilder sb = new StringBuilder("dropdownItem.");
    for (int i = 0; i < length; i++) {
      sb.append(BASE36_CHARS.charAt(RANDOM.nextInt(BASE36_CHARS.length())));
    }
    return sb.toString();
  }
}

Tạo và chèn khối thả xuống

Tạo một lô (nên dùng)

Mẫu hiệu quả nhất kết hợp CreateDropdownDefinitionRequest và InsertDropdownRequest trong một lệnh gọi documents.batchUpdate.

Mã mẫu sau đây tạo một định nghĩa trình đơn thả xuống "Trạng thái đánh giá" với 3 lựa chọn được mã hoá bằng màu sắc bằng cách sử dụng mã nhận dạng do người dùng cung cấp và chèn một thực thể vào cuối tài liệu:

Python

requests = [
    {
        "createDropdownDefinition": {
            "dropdownDefinition": {
                "dropdownDefinitionId": "kix.review_status",
                "dropdownDefinitionProperties": {
                    "title": "Review Status",
                    "options": [
                        {
                            "optionId": "dropdownItem.pending",
                            "displayValue": "Pending Review",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.99, "green": 0.90, "blue": 0.65}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.45, "green": 0.30, "blue": 0.0}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.approved",
                            "displayValue": "Approved",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                                },
                            },
                        },
                        {
                            "optionId": "dropdownItem.rejected",
                            "displayValue": "Needs Changes",
                            "textStyle": {
                                "backgroundColor": {
                                    "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                                },
                                "foregroundColor": {
                                    "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                                },
                            },
                        },
                    ],
                },
            }
        }
    },
    {
        "insertDropdown": {
            "endOfSegmentLocation": {},
            "dropdownDefinitionId": "kix.review_status",
            "selectedOptionId": "dropdownItem.pending",
        }
    },
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();

List<DropdownOption> options = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.pending")
        .setDisplayValue("Pending Review")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.99f).setGreen(0.90f).setBlue(0.65f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.45f).setGreen(0.30f).setBlue(0.0f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Needs Changes")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

DropdownDefinition definition = new DropdownDefinition()
    .setDropdownDefinitionId("kix.review_status")
    .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
        .setTitle("Review Status")
        .setOptions(options));

requests.add(new Request().setCreateDropdownDefinition(
    new CreateDropdownDefinitionRequest().setDropdownDefinition(definition)));

requests.add(new Request().setInsertDropdown(
    new InsertDropdownRequest()
        .setEndOfSegmentLocation(new EndOfSegmentLocation())
        .setDropdownDefinitionId("kix.review_status")
        .setSelectedOptionId("dropdownItem.pending")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Quy tắc và giới hạn xác thực cho trình đơn thả xuống

Khi tạo hoặc sửa đổi định nghĩa trình đơn thả xuống, bạn phải tuân thủ các quy tắc ràng buộc sau:

  • Số lượng lựa chọn: Định nghĩa trình đơn thả xuống phải có từ 2 đến 50 lựa chọn.
  • Độ dài tiêu đề: Định nghĩa title không được để trống và không được vượt quá 200 ký tự.
  • Độ dài giá trị hiển thị: displayValue của mỗi lựa chọn không được để trống và không được vượt quá 200 ký tự.
  • Tạo kiểu cho lựa chọn: Chỉ foregroundColor và backgroundColor được hỗ trợ trong DropdownOption.textStyle. Việc đặt các thuộc tính kiểu khác sẽ trả về lỗi 400 Bad Request.
  • Lựa chọn mặc định: Trong InsertDropdownRequest, nếu selectedOptionId bị bỏ qua, khối sẽ mặc định là lựa chọn đầu tiên được xác định trong DropdownDefinition.

Cập nhật lựa chọn cho từng khối thả xuống

Để thay đổi lựa chọn đã chọn của một phiên bản khối thả xuống hiện có mà không làm thay đổi mẫu hoặc các khối khác, hãy sử dụng UpdateDropdownPropertiesRequest:

  • dropdownId: (Bắt buộc) Mã nhận dạng của thực thể khối thả xuống cụ thể cần cập nhật.
  • tabId: Mã nhận dạng của thẻ chứa trình đơn thả xuống (mặc định là thẻ đầu tiên nếu bị bỏ qua).
  • fields: Đặt thành "selectedOptionId".

Mã mẫu sau đây cập nhật lựa chọn đang hoạt động của một chip thả xuống thành "dropdownItem.approved":

Python

requests = [
    {
        "updateDropdownProperties": {
            "dropdownId": "kix.chip_abc1",
            "tabId": "t.0",
            "dropdownProperties": {
                "selectedOptionId": "dropdownItem.approved",
            },
            "fields": "selectedOptionId",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownProperties(
    new UpdateDropdownPropertiesRequest()
        .setDropdownId("kix.chip_abc1")
        .setTabId("t.0")
        .setDropdownProperties(new DropdownProperties()
            .setSelectedOptionId("dropdownItem.approved"))
        .setFields("selectedOptionId")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Cập nhật các định nghĩa và lựa chọn trong trình đơn thả xuống

Để sửa đổi mẫu (tiêu đề, danh sách lựa chọn, nhãn hiển thị hoặc màu sắc), hãy sử dụng biểu tượng UpdateDropdownDefinitionPropertiesRequest. Khi bạn cập nhật một định nghĩa, tất cả các khối thả xuống tham chiếu đến định nghĩa đó trên thẻ sẽ tự động được cập nhật.

Thay thế toàn bộ danh sách cho các lựa chọn

Khi dropdownDefinitionProperties.options được đưa vào mặt nạ fields, yêu cầu sẽ thực hiện thao tác thay thế toàn bộ danh sách. Bạn phải cung cấp danh sách đầy đủ các lựa chọn theo thứ tự đã chọn. Máy chủ sẽ so sánh danh sách đến với định nghĩa hiện tại:

  • Lựa chọn mới: Việc thêm một lựa chọn không có optionId (hoặc có optionId mới do người dùng cung cấp) sẽ thêm lựa chọn đó vào định nghĩa.
  • Tuỳ chọn được cập nhật: Việc cung cấp một optionId hiện có với displayValue hoặc textStyle đã sửa đổi sẽ cập nhật tuỳ chọn đó tại chỗ.
  • Các lựa chọn được sắp xếp lại: Danh sách các lựa chọn được lưu theo đúng trình tự được cung cấp trong yêu cầu.
  • Tuỳ chọn đã xoá: Việc bỏ qua một optionId hiện có sẽ xoá tuỳ chọn đó khỏi định nghĩa.

Tính toàn vẹn tham chiếu và lựa chọn thay thế

Nếu một lựa chọn bị xoá được chọn trong bất kỳ khối thả xuống nào trong tài liệu, bạn phải cung cấp một mối liên kết trong selectedOptionIdReplacements (map<string, string>). Khoá là mã nhận dạng của các lựa chọn bị xoá và giá trị là mã nhận dạng của các lựa chọn thay thế.

Mã mẫu sau đây minh hoạ cách cập nhật định nghĩa trình đơn thả xuống bằng các lựa chọn thay thế:

Python

requests = [
    {
        "updateDropdownDefinitionProperties": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
            "dropdownDefinitionProperties": {
                "title": "Editorial Review Status",
                "options": [
                    {
                        "optionId": "dropdownItem.approved",
                        "displayValue": "Approved",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.85, "green": 0.95, "blue": 0.85}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.08, "green": 0.40, "blue": 0.15}}
                            },
                        },
                    },
                    {
                        "optionId": "dropdownItem.rejected",
                        "displayValue": "Changes Requested",
                        "textStyle": {
                            "backgroundColor": {
                                "color": {"rgbColor": {"red": 0.98, "green": 0.84, "blue": 0.84}}
                            },
                            "foregroundColor": {
                                "color": {"rgbColor": {"red": 0.65, "green": 0.10, "blue": 0.10}}
                            },
                        },
                    },
                ],
            },
            "selectedOptionIdReplacements": {
                "dropdownItem.pending": "dropdownItem.rejected"
            },
            "fields": "title,options",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<DropdownOption> updatedOptions = Arrays.asList(
    new DropdownOption()
        .setOptionId("dropdownItem.approved")
        .setDisplayValue("Approved")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.85f).setGreen(0.95f).setBlue(0.85f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.08f).setGreen(0.40f).setBlue(0.15f))))),
    new DropdownOption()
        .setOptionId("dropdownItem.rejected")
        .setDisplayValue("Changes Requested")
        .setTextStyle(new TextStyle()
            .setBackgroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.98f).setGreen(0.84f).setBlue(0.84f))))
            .setForegroundColor(new OptionalColor().setColor(
                new Color().setRgbColor(new RgbColor().setRed(0.65f).setGreen(0.10f).setBlue(0.10f))))));

Map<String, String> replacements = new HashMap<>();
replacements.put("dropdownItem.pending", "dropdownItem.rejected");

List<Request> requests = new ArrayList<>();
requests.add(new Request().setUpdateDropdownDefinitionProperties(
    new UpdateDropdownDefinitionPropertiesRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")
        .setDropdownDefinitionProperties(new DropdownDefinitionProperties()
            .setTitle("Editorial Review Status")
            .setOptions(updatedOptions))
        .setSelectedOptionIdReplacements(replacements)
        .setFields("title,options")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Xoá định nghĩa trình đơn thả xuống

Để xoá một mẫu trình đơn thả xuống không dùng đến, hãy sử dụng DeleteDropdownDefinitionRequest.

Mã mẫu sau đây minh hoạ cách xoá một định nghĩa trình đơn thả xuống:

Python

requests = [
    {
        "deleteDropdownDefinition": {
            "dropdownDefinitionId": "kix.review_status",
            "tabId": "t.0",
        }
    }
]

result = service.documents().batchUpdate(
    documentId=DOCUMENT_ID,
    body={"requests": requests}
).execute()

Java

List<Request> requests = new ArrayList<>();
requests.add(new Request().setDeleteDropdownDefinition(
    new DeleteDropdownDefinitionRequest()
        .setDropdownDefinitionId("kix.review_status")
        .setTabId("t.0")));

BatchUpdateDocumentRequest body = new BatchUpdateDocumentRequest().setRequests(requests);
BatchUpdateDocumentResponse response = docsService.documents()
    .batchUpdate(DOCUMENT_ID, body)
    .execute();

Xử lý lỗi và khắc phục sự cố

Các điều kiện lỗi thường gặp và cách khắc phục:

Mã trạng thái Nguyên nhân Độ phân giải
400 INVALID_ARGUMENT Tiền tố mã nhận dạng hoặc định dạng biểu thức chính quy không hợp lệ. Đảm bảo dropdownDefinitionId bắt đầu bằng kix. (6 – 18 ký tự) và optionId bắt đầu bằng dropdownItem. (15 – 27 ký tự).
400 INVALID_ARGUMENT Số lượng lựa chọn nằm ngoài giới hạn. Định nghĩa trình đơn thả xuống phải có từ 2 đến 50 lựa chọn.
400 INVALID_ARGUMENT Giá trị tiêu đề hoặc giá trị hiển thị bị bỏ trống hoặc vượt quá 200 ký tự. Cung cấp một chuỗi không trống có độ dài từ 1 đến 200 ký tự.
400 INVALID_ARGUMENT Thuộc tính kiểu văn bản không được hỗ trợ trong lựa chọn. Chỉ foregroundColor và backgroundColor được hỗ trợ trong các lựa chọn thả xuống. Xoá phông chữ, kích thước hoặc các thuộc tính khác.
400 INVALID_ARGUMENT Thiếu chế độ liên kết thay thế tuỳ chọn khi xoá. Khi xoá một lựa chọn mà bất kỳ khối nào đã chọn, hãy cung cấp một lựa chọn thay thế hợp lệ trong selectedOptionIdReplacements.
400 INVALID_ARGUMENT Đã cố gắng xoá một định nghĩa đang được sử dụng. Xoá hoặc nhắm lại mục tiêu tất cả các thực thể chip thả xuống tham chiếu đến định nghĩa trước khi xoá định nghĩa đó.
400 INVALID_ARGUMENT Mã do người dùng cung cấp bị trùng lặp. Đảm bảo rằng các mã nhận dạng do người dùng cung cấp là riêng biệt trong thẻ tài liệu.