You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Apps Script更新PropertiesService后跨卡片数据不刷新问题

如何更新PropertiesService中的数据并在其他Card中展示

开发Google Calendar插件时出现数据陈旧、卡片/UI始终显示旧值的问题,复现场景如下:

  1. 创建Google Calendar插件项目,在appsscript.json中配置首页构建触发器:
// appsscript.json
// set the trigger to build the home
....
....
 "addOns": {
    "calendar": {
      "homepageTrigger": {
        "enabled": true,
        "runFunction": "buildInitState"
      }
    },
    "common": {
      ....
....
  1. 编写初始化函数构建首页卡片,初始实现代码:
const userStore = PropertiesService.getUserProperties();

// setup userStore and add default values for date 
userStore.setProperties({
  date: (new Date()).getTime(),
});

const showSettingsPageAction =
  CardService.newAction().setFunctionName('showSettingsPage');

const handleDateChangeAction =
  CardService.newAction().setFunctionName('handleDateChange');

function handleDateChange(e) {
  //change the date to what user selects
  const newDate = (new Date(e.formInput.date_field.msSinceEpoch)).getTime();
  userStore.setProperty('date', newDate);
}

// Homepage card
function buildInitState(e) {
  const mainCard = CardService.newCardBuilder();

  // add a date picker
  const DatePicker = CardService.newDatePicker()
    .setTitle('set date')
    .setFieldName('date_field')
    .setValueInMsSinceEpoch((new Date()).getTime())
    .setOnChangeAction(
      CardService.newAction().setFunctionName('handleDateChangeAction')
    );

  // add a button to nav to another card
  const ButtonFindSlots = CardService.newTextButton()
    .setText('NAV TO SETTINGS CARD')
    .setOnClickAction(showSettingsPageAction);

  mainCard
  .addSection(DatePicker)
  .addSection(ButtonFindSlots)

  mainCard.build();
}
  1. 编写showSettingsPage函数构建设置页卡片,初始实现代码:
function showSettingsPage() {
  const card = CardService.newCardBuilder();
  const cardSection = CardService.newCardSection();

  let content = userStore.getProperty('date');
  let p = CardService.newTextParagraph().setText(content);

  card.addSection(cardSection);
  return card.build();
}

测试时用户在首页修改日期后导航到设置页,日期始终显示初始默认值,无法确认是数据未更新还是卡片未刷新。


问题原因与修复方案

代码存在4个核心错误,按优先级排序:

  • 回调函数名绑定错误:日期选择器的onChangeAction绑定的函数名为handleDateChangeAction,但实际实现的存值函数名为handleDateChange,修改日期的回调从未触发,值根本没有写入PropertiesService。
  • 全局逻辑污染:将userStore实例定义在全局作用域,且在全局作用域直接执行setProperties写入默认日期。Google Apps Script运行时会缓存全局上下文,既会导致读取到旧的属性实例,也会在每次脚本运行时覆盖用户已经保存的自定义日期。
  • Action回调无合法响应:即使回调正常触发,修改完属性后如果不返回ActionResponse通知UI更新,插件前端不会同步状态,后续导航可能拿到缓存的旧卡片状态。
  • 组件未正确挂载:showSettingsPage函数中创建了显示日期的文本段落,但未将其添加到卡片分区中,即使读到正确值也无法在页面渲染。

修复后的完整可运行代码如下:

// 日期修改回调
function handleDateChange(e) {
  // 每次执行时重新获取属性存储实例,禁止使用全局缓存的实例
  const userStore = PropertiesService.getUserProperties();
  const newDate = e.formInput.date_field.msSinceEpoch;
  userStore.setProperty('date', newDate);
  // 返回ActionResponse,通知UI更新当前首页卡片
  return CardService.newActionResponseBuilder()
    .setNavigation(CardService.newNavigation().updateCard(buildInitState(e)))
    .build();
}

// 首页构建函数
function buildInitState(e) {
  const userStore = PropertiesService.getUserProperties();
  // 仅当date属性不存在时才写入默认值,避免覆盖用户修改的内容
  if (!userStore.getProperty('date')) {
    userStore.setProperty('date', new Date().getTime());
  }
  // 读取存储中最新的日期值
  const savedDate = Number(userStore.getProperty('date'));

  // 定义页面跳转Action
  const showSettingsPageAction = CardService.newAction().setFunctionName('showSettingsPage');

  const mainCard = CardService.newCardBuilder();
  // 日期选择器默认值取存储中的最新值,不要每次初始化都用当前时间
  const datePicker = CardService.newDatePicker()
    .setTitle('set date')
    .setFieldName('date_field')
    .setValueInMsSinceEpoch(savedDate)
    // 绑定正确的回调函数名
    .setOnChangeAction(CardService.newAction().setFunctionName('handleDateChange'));

  const navSettingsBtn = CardService.newTextButton()
    .setText('NAV TO SETTINGS CARD')
    .setOnClickAction(showSettingsPageAction);

  // 组件需要先加到Section,再把Section加到Card
  const dateSection = CardService.newCardSection().addWidget(datePicker);
  const btnSection = CardService.newCardSection().addWidget(navSettingsBtn);

  return mainCard.addSection(dateSection).addSection(btnSection).build();
}

// 设置页构建函数
function showSettingsPage() {
  // 每次进入页面时重新获取属性实例、读取最新值
  const userStore = PropertiesService.getUserProperties();
  const card = CardService.newCardBuilder();
  const cardSection = CardService.newCardSection();

  const savedDate = userStore.getProperty('date');
  const textP = CardService.newTextParagraph().setText(`存储的日期时间戳:${savedDate}`);
  // 把文本组件添加到分区中
  cardSection.addWidget(textP);

  return card.addSection(cardSection).build();
}

开发时需要遵守三个规则避免同类问题:

  • 所有和PropertiesService交互的逻辑,不要把实例、属性值存在全局变量中,每次函数执行时重新获取,避免GAS全局缓存导致读到旧值。
  • 所有绑定到卡片组件的Action回调函数,必须返回ActionResponse,支持的操作包括更新当前卡片、推送/弹出新卡片、弹出通知等,否则UI状态不会同步。
  • 不要在全局作用域执行属性写入操作,全局代码会在每次脚本冷/热启动时执行,很容易覆盖用户已经保存的自定义值。

内容的提问来源于stack exchange,提问作者GN.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:48:32