google-apps-scriptgoogle-cloud-platformgoogle-workspace-add-ons

Google Workspace Addon Dynamic dropdown


What I want to achieve is to create a dynamic dropdown (fetch from a database and based on the selection display a list of items).

The problem is I can't get the onChangeAction to work this is on my home card so I configured the action to trigger the function of my home card.

              {
                widgets: [
                  {
                    selectionInput: {
                      type: 'DROPDOWN',
                      name: 'projects',
                      label: 'Select project',
                      items: projects.map((project) => ({
                        text: project.name,
                        value: project.id,
                        selected: project.id === selectedProjectId,
                      })),
                      onChangeAction: {
                        function:
                          'https://xxxxxxxx/onCalendarHomePageOpen',
                        parameters: [],
                      },
                    },
                  },
                ],
              }

I see this error:

Cannot find field: action in message google.apps.card.v1.SubmitFormResponse

Then I add render_actions and the onChange works, but the rest state breaks.

This same thing happens when I try to navigate back to the home card from another one.

I just noticed you mention renderActions in the common card ui generator, and then render_actions in the card ui generators, I'm not sure about what's the difference.

Thank you very much


Solution

  • I found the answer here: https://codelabs.developers.google.com/codelabs/workspace-add-on-nodejs-cloudrun#5

    You have to create a new endpoint, build the card and pass it to the updateCard method.

    Fetch tasks

    app.post('/', asyncHandler(async (req, res) => {
        const event = req.body;
        const user = await userInfo(event);
        const tasks = await listTasks(user.sub);
        const card = buildCard(req, tasks);
        const responsePayload = {
            action: {
                navigations: [{
                    pushCard: card
                }]
            }
        };
        res.json(responsePayload);
    }));
    

    Add task

    app.post('/newTask', asyncHandler(async (req, res) => {
        const event = req.body;
        const user = await userInfo(event);
    
        const formInputs = event.commonEventObject.formInputs || {};
        const newTask = formInputs.newTask;
        if (!newTask || !newTask.stringInputs) {
            return {};
        }
    
        const task = {
            text: newTask.stringInputs.value[0],
            created: new Date()
        };
        await addTask(user.sub, task);
    
        const tasks = await listTasks(user.sub);
        const card = buildCard(req, tasks);
        const responsePayload = {
            renderActions: {
                action: {
                    navigations: [{
                        updateCard: card
                    }],
                    notification: {
                        text: 'Task added.'
                    },
                }
            }
        };
        res.json(responsePayload);
    }));
    

    buildCard method

    function buildCard(req, tasks) {
        const baseUrl = `${req.protocol}://${req.hostname}${req.baseUrl}`;
        // Input for adding a new task
        const inputSection = {
            widgets: [
                {
                    textInput: {
                        label: 'Task to add',
                        name: 'newTask',
                        value: '',
                        onChangeAction: {
                            function: `${baseUrl}/newTask`,
                        },
                    }
                }
            ]
        };
        const taskListSection = {
            header: 'Your tasks',
            widgets: []
        };
        if (tasks && tasks.length) {
            // Create text & checkbox for each task
            tasks.forEach(task => taskListSection.widgets.push({
                decoratedText: {
                    text: task.text,
                    wrapText: true,
                    switchControl: {
                        controlType: 'CHECKBOX',
                        name: 'completedTasks',
                        value: task[datastore.KEY].id,
                        selected: false,
                        onChangeAction: {
                            function: `${baseUrl}/complete`,
                        }
                    }
                }
            }));
        } else {
            // Placeholder for empty task list
            taskListSection.widgets.push({
                textParagraph: {
                    text: 'Your task list is empty.'
                }
            });
        }
        const card = {
            sections: [
                inputSection,
                taskListSection,
            ]
        }
        return card;
    }