Praca z elementami menu

Interfejs Google Docs API umożliwia programowe tworzenie, wstawianie, aktualizowanie, odczytywanie i zarządzanie elementami menu w dokumentach Google.

Czym są elementy menu?

Elementy menu w Dokumentach Google zapewniają użytkownikom interaktywne, konfigurowalne menu wyboru w tekście dokumentu. Użytkownicy mogą kliknąć element menu, aby wybrać jedną z opcji z wstępnie zdefiniowanej listy. Każda z nich ma własny tekst wyświetlany i styl kolorów. Elementy menu są często używane do śledzenia projektów, aktualizowania stanu, przepływów pracy związanych z weryfikacją i etapów zatwierdzania.

Za pomocą interfejsu Docs API możesz:

  • Definiuj szablony menu, których można używać wielokrotnie, z dostosowanymi tytułami, nazwami opcji i kolorami.
  • Wstaw elementy menu w prawidłowym miejscu.
  • Aktualizowanie wybranej opcji pojedynczej instancji elementu menu.
  • Modyfikuj definicje udostępnionych menu w miejscu, aktualizując jednocześnie wszystkie powiązane z nimi elementy.
  • Bezpiecznie zastępuj lub wycofuj opcje, zachowując integralność referencyjną w przypadku istniejących elementów.
  • Usuń nieużywane szablony menu.

Architektura: definicje i instancje

W interfejsie Docs API elementy menu mają definicje i instancje. Definicja określa opcje instancji menu. Instancja menu to menu, z którym użytkownicy mogą wchodzić w interakcję, a które zapisuje informacje o wybranych opcjach.

Interfejs Docs API oddziela konfigurację szablonu menu od instancji wbudowanego elementu menu:

  1. DropdownDefinition: szablon na poziomie karty, który definiuje tytuł menu i zbiór opcji do wyboru (DropdownOption). Nie znajduje się w określonym miejscu w dokumencie, ale jest przechowywany na mapie definicji karty: document.tabs[].documentTab.dropdownDefinitions.
  2. Menu: pojedyncza instancja elementu wbudowana w element akapitu (ParagraphElement.dropdown). Każda instancja menu odwołuje się do elementu dropdownDefinitionId i przechowuje własny aktywny element selectedOptionId.

Zmiana DropdownDefinition (np. dodanie opcji lub zmiana nazwy) aktualizuje wszystkie powiązane z nią elementy na karcie bez konieczności aktualizowania każdego elementu z osobna.

Każda instancja Dropdown śledzi własną wartość selectedOptionId. Zmiana wybranej wartości pojedynczego elementu wpływa tylko na to konkretne wystąpienie.

Format identyfikatora i reguły sprawdzania poprawności

Identyfikatory przekazywane przez użytkowników muszą być zgodne z określonym formatem, prefiksem i długością:

Identyfikator Obowiązkowy prefiks Wyrażenie regularne do weryfikacji Limit długości Przykład
Identyfikator definicji menu (dropdownDefinitionId) kix. ^kix\.[a-zA-Z0-9_-]{2,14}$ 6–18 znaków kix.review_status
Identyfikator opcji menu (optionId) dropdownItem. ^dropdownItem\.[a-zA-Z0-9_-]{2,14}$ 15–27 znaków dropdownItem.pending

Generowanie identyfikatorów przekazywanych przez użytkowników

Aby wygenerować identyfikatory zgodne z wymaganiami dotyczącymi obowiązkowego prefiksu i wyrażenia regularnego, użyj tych funkcji pomocniczych. Te funkcje pomocnicze generują sufiksy alfanumeryczne w formacie base-36 pisane małymi literami, które są zgodne z formatem generowanym przez interfejs Dokumentów Google:

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();
  }
}

Tworzenie i wstawianie elementów menu

Tworzenie pojedynczego wsadu (zalecane)

Najbardziej wydajny wzorzec łączy CreateDropdownDefinitionRequest i InsertDropdownRequest w jednym wywołaniu documents.batchUpdate.

Poniższy przykład kodu tworzy definicję menu „Stan weryfikacji” z 3 opcjami oznaczonymi kolorami przy użyciu identyfikatorów podanych przez użytkownika i wstawia instancję na końcu dokumentu:

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();

Reguły sprawdzania poprawności i limity w przypadku menu

Podczas tworzenia lub modyfikowania definicji menu obowiązują te ograniczenia:

  • Liczba opcji: definicja menu musi zawierać od 2 do 50 opcji.
  • Długość tytułu: definicja title nie może być pusta i nie może przekraczać 200 znaków.
  • Długość wyświetlanej wartości: pole displayValue każdej opcji nie może być puste i nie może przekraczać 200 znaków.
  • Styl opcji: w DropdownOption.textStyle obsługiwane są tylko wartości foregroundColor i backgroundColor. Ustawienie innych właściwości stylu zwraca błąd 400 Bad Request.
  • Domyślny wybór: w InsertDropdownRequest, jeśli pominięto selectedOptionId, domyślnie wybierana jest pierwsza opcja zdefiniowana w DropdownDefinition.

Aktualizowanie wyboru poszczególnych elementów menu

Aby zmienić wybraną opcję istniejącej instancji elementu menu bez modyfikowania szablonu ani innych elementów, użyj tego symbolu: UpdateDropdownPropertiesRequest.

  • dropdownId: (Wymagany) identyfikator konkretnej instancji elementu menu do zaktualizowania.
  • tabId: Identyfikator karty zawierającej menu (jeśli zostanie pominięty, domyślnie używana jest pierwsza karta).
  • fields: ustaw na "selectedOptionId".

Poniższy przykładowy kod aktualizuje aktywny wybór elementu menu do wyboru do "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();

Aktualizowanie definicji i opcji menu

Aby zmodyfikować sam szablon (tytuł, listę opcji, etykiety wyświetlania lub kolory), kliknij UpdateDropdownDefinitionPropertiesRequest. Zaktualizowanie definicji spowoduje automatyczne zaktualizowanie wszystkich powiązanych z nią elementów menu na karcie.

Pełna lista opcji zastępowania

Jeśli w masce fields znajduje się znak dropdownDefinitionProperties.options, żądanie powoduje pełną wymianę listy. Musisz podać pełną listę opcji w wybranej kolejności. Serwer porównuje listę przychodzącą z bieżącą definicją:

  • Nowa opcja: dodanie opcji bez znaku optionId (lub z nowym znakiem optionId podanym przez użytkownika) powoduje dodanie jej do definicji.
  • Zaktualizowana opcja: podanie istniejącego elementu optionId ze zmodyfikowanymi aktualizacjami displayValue lub textStyle powoduje zaktualizowanie tej opcji.
  • Zmieniona kolejność opcji: lista opcji jest zapisywana w dokładnej kolejności podanej w żądaniu.
  • Usunięta opcja: pominięcie istniejącego elementu optionId powoduje usunięcie tej opcji z definicji.

Integralność referencyjna i zastępowanie opcji

Jeśli usuwana opcja jest wybrana w dowolnym elemencie menu w dokumencie, musisz podać mapowanie w selectedOptionIdReplacements (map<string, string>). Kluczami są identyfikatory usuwanych opcji, a wartościami identyfikatory opcji zastępczych.

Poniższy przykładowy kod pokazuje, jak zaktualizować definicję menu z zastąpieniami opcji:

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();

Usuwanie definicji menu

Aby usunąć nieużywany szablon menu, kliknij DeleteDropdownDefinitionRequest.

Poniższy przykładowy kod pokazuje, jak usunąć definicję menu:

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();

Obsługa błędów i rozwiązywanie problemów

Typowe błędy i sposoby ich rozwiązywania:

Kod stanu Przyczyna Rozdzielczość
400 INVALID_ARGUMENT Nieprawidłowy prefiks identyfikatora lub format wyrażenia regularnego. Sprawdź, czy dropdownDefinitionId zaczyna się od kix. (6–18 znaków), a optionId zaczyna się od dropdownItem. (15–27 znaków).
400 INVALID_ARGUMENT Liczba opcji poza zakresem. Definicja menu musi zawierać od 2 do 50 opcji.
400 INVALID_ARGUMENT Tytuł lub wyświetlana wartość jest pusta lub przekracza 200 znaków. Podaj niepusty ciąg znaków o długości od 1 do 200 znaków.
400 INVALID_ARGUMENT Nieobsługiwana właściwość stylu tekstu w opcji. W przypadku opcji menu obsługiwane są tylko wartości foregroundColor i backgroundColor. Usuń czcionkę, rozmiar lub inne atrybuty.
400 INVALID_ARGUMENT Brak mapowania zastępczego opcji podczas usuwania. Jeśli usuwasz opcję wybraną przez dowolny element, podaj prawidłową opcję zastępczą w polu selectedOptionIdReplacements.
400 INVALID_ARGUMENT Próbowano usunąć definicję, która jest używana. Zanim usuniesz definicję, usuń wszystkie instancje elementu menu lub zmień ich docelowy element.
400 INVALID_ARGUMENT Zduplikowany identyfikator przekazywany przez użytkownika. Upewnij się, że identyfikatory przekazywane przez użytkowników są unikalne na karcie dokumentu.