Оновіть дані, отримані користувацькою функцією в Google Sheet


92

Я написав власний сценарій Google Apps, який буде отримувати idта отримувати інформацію від веб-служби (ціна).

Я використовую цей скрипт у таблиці, і він чудово працює. Моя проблема полягає в тому, що ці ціни змінюються, і моя електронна таблиця не оновлюється.

Як я можу змусити його повторно запустити сценарій та оновити комірки (не переходячи вручну по кожній комірці)?


1
Так, ось пояснення, яке я зробив з цього приводу: stackoverflow.com/questions/9022984/…
Енріке Г. Абре

Я прочитав ваше пояснення в рамках свого дослідження. Дуже корисно, дякую. Я додав посилання до своєї відповіді.
tbkn23

Для тих, хто стикається з подібною (визначеною та логічною, але іноді прикрою) поведінкою, може допомогти проголосувати за цей запит на функцію в Google Issue Tracker: issueetracker.google.com/issues/36763858 .
Тімоті Джонс


Реальні обхідні шляхи: stackoverflow.com/a/56802017 і stackoverflow.com/a/61946592
Майстер

Відповіді:


92

Гаразд, схоже, моєю проблемою було те, що Google поводиться дивно - він не запускає сценарій повторно, поки параметри сценарію схожі, він використовує кешовані результати попередніх запусків. Отже, він не підключається до API і не отримує ціну, він просто повертає попередній результат сценарію, який був кешований.

Докладніше див. Тут: https://code.google.com/p/google-apps-script-issues/issues/detail?id=888

і тут: Сценарій для узагальнення даних, які не оновлюються

Моїм рішенням було додати до мого сценарію ще один параметр, який я навіть не використовую. Тепер, коли ви викликаєте функцію з параметром, який відрізняється від попередніх викликів, їй доведеться перезапустити сценарій, оскільки результат для цих параметрів не буде в кеші.

Тому, коли я викликаю функцію, для додаткового параметра я передаю "$ A $ 1". Я також створив пункт меню, який називається оновлення, і коли я його запускаю, він поміщає поточну дату та час в A1, отже, всі виклики сценарію з $ A $ 1 як другим параметром доведеться перерахувати. Ось код з мого сценарію:

function onOpen() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var entries = [{
    name : "Refresh",
    functionName : "refreshLastUpdate"
  }];
  sheet.addMenu("Refresh", entries);
};

function refreshLastUpdate() {
  SpreadsheetApp.getActiveSpreadsheet().getRange('A1').setValue(new Date().toTimeString());
}

function getPrice(itemId, datetime) {
  var headers =
      {
        "method" : "get",
        "contentType" : "application/json",
        headers : {'Cache-Control' : 'max-age=0'}
      };

  var jsonResponse = UrlFetchApp.fetch("http://someURL?item_id=" + itemId, headers);
  var jsonObj = eval( '(' + jsonResponse + ')' );
  return jsonObj.Price;
  SpreadsheetApp.flush();
}   

І коли я хочу поставити ціну елемента з ідентифікатором 5 у клітинку, я використовую наступну формулу:

=getPrice(5, $A$1)

Коли я хочу оновити ціни, я просто натискаю пункт «Оновити» -> «Оновити». Пам'ятайте, що вам потрібно перезавантажити електронну таблицю після зміни onOpen()сценарію.


4
чому б не використовувати зараз () як додатковий параметр?
Розширений

5
Розумієте, подібно до того, як моя функція не буде переоцінена, оскільки її параметри не змінилися (тобто значення клітинок), зараз () також не буде переглянута, оскільки вона не має параметрів, отже, її повернене значення буде не змінюється, тому параметри моєї функції не змінюються. Крім того, використання now () змусить мою функцію весь час переоцінювати, що є трохи важким, враховуючи те, що воно генерує кілька викликів HTTP ...
tbkn23

2
Гарна знахідка. У мене виникла ця проблема під час використання названих діапазонів як вхідних даних. Використовуючи вашу відповідь, я зрозумів, що часто досить добре передавати фіктивну суму через діапазон вхідних даних, як у "sum (B: D)", де рядки BD знаходяться в діапазоні, який шукає спеціальна функція. Зміна будь-якої комірки спричинить зміну суми та оновлення користувацької функції. До речі, схоже, користувацька функція навіть не повинна оголошувати ігнорований параметр.
Шон Гувер,

1
Ганьба, що це все-таки найкраще вирішення цієї проблеми. Замість того, щоб Google просто дозволяв нам вимкнути кеш для певних викликів функцій, ми довільно збільшуємо те, що зберігається в кеші, лише щоб переконатись, що не отримаємо кешоване значення ... Все-таки, дякую за допис,
GrayedFox

Спробуйте використати параметр для власної функції, як у цьому прикладі
Антени,

33

Я знаю, це трохи давнє запитання. Але цей метод не вимагає жодних дій користувача, окрім внесення змін.

Те, що я зробив, було схоже на tbkn23.

Функція, яку я хочу переглянути, має додатковий невикористаний параметр, $ A $ 1. Отже, виклик функції є

=myFunction(firstParam, $A$1)

Але в коді є підпис функції

function myFunction(firstParam)

Замість того, щоб мати функцію Refresh, я використав функцію onEdit (e), як це

function onEdit(e)
{
   SpreadsheetApp.getActiveSheet().getRange('A1').setValue(Math.random());
}

Ця функція спрацьовує щоразу, коли редагується будь-яка комірка електронної таблиці. Отже, тепер ви редагуєте комірку, випадкове число поміщається в A1, це оновлює список параметрів, як запропоновано tbkn23, що спричиняє повторну оцінку користувацької функції.


5
Чудово працює. Недоліком є ​​те, що історія скасування (ctrl + z) переплутана.
Джон

1
Хороший фокус із надокучливим недоліком, як зазначив Джон ... Сподіваюсь, хтось це покращить :-)
Еніссей

Невеликий і педантичний застереження з цією відповіддю; у неймовірно малоймовірному випадку Math.random повертає той самий номер, і з цього приводу він не буде оновлений, оскільки це конкретне число вже було кешоване.
Вуді Пейн,

12

Це дуже пізно, і я не знаю, що це було б корисно, але насправді тут є налаштування, які ви можете зробити NOW()оновлення автоматично

введіть тут опис зображення


1
NOW()є вбудованою функцією, а не спеціальною функцією.
Рубен

@ Rubén Сенс у тому, що ви можете включити функцію NOW () у будь-яку користувацьку функцію, яку хочете оновити
Thaina,

13
На даний момент аргументи користувацьких функцій повинні бути детермінованими, іншими словами, вони не виділяють NOW () як аргумент. Див. Developers.google.com/apps-script/guides/sheets/functions
Рубен

3
Це видає помилку, якщо ви намагаєтесь використовувати NOW () всередині користувацької функції
Стефано Джаконе,

6

Якщо ваша спеціальна функція знаходиться всередині певного стовпця, просто впорядкуйте таблицю за цим стовпцем.

Дія впорядкування змушує оновити дані, що викликає вашу користувацьку функцію для всіх рядків цього стовпця одночасно.


1

Це може бути супер стара тема, але може бути корисною для тих, хто намагається це зробити, як я був зараз.

Опрацьовуючи сценарій Lexi як є, він, здається, вже не працював з поточними Таблицями, але якщо я додаю фіктивну змінну у свою функцію як параметр (не потрібно насправді використовувати її всередині функції), це справді буде змусити аркуші Google знову оновити сторінку.

Отже, оголошення типу: function myFunction (firstParam, dummy), а потім його виклик буде таким, як було запропоновано. Це спрацювало для мене.

Крім того, якщо випадкова змінна з’являється на всіх ваших аркушах, які ви редагуєте, це неприємно, простим засобом обмеження одним аркушем є такий:

function onEdit(e)
{
  e.source.getSheetByName('THESHEETNAME').getRange('J1').setValue(Math.random());
}

1
@ Rubén це дуже схоже, але з великою неприємністю; замість того, щоб використовувати активний аркуш, він використовує визначену назву аркуша, покращуючи вашу історію
Jonas D.

1

Логіка сценарію:

  • Спеціальні функції не оновлюються, якщо аргументи не змінюються.
  • Створіть тригер onChange, щоб змінити всі аргументи всіх користувацьких функцій у таблиці за допомогою textFinder

Фрагмент:

/*@customfunction*/
function sheetNames(e) {
  return SpreadsheetApp.getActive()
    .getSheets()
    .map(function(sheet) {
      return sheet.getName();
    });
}

/*Create a installable trigger to listen to grid changes on the sheet*/
function onChange(e) {
  if (!/GRID/.test(e.changeType)) return; //Listen only to grid change
  SpreadsheetApp.getActive()
    .createTextFinder('=SHEETNAMES\\([^)]*\\)')
    .matchFormulaText(true)
    .matchCase(false)
    .useRegularExpression(true)
    .replaceAllWith(
      '=SHEETNAMES(' + (Math.floor(Math.random() * 500) + 1) + ')'
    );
}

Читати:


1

Як зазначалося раніше:

Спеціальні функції не оновлюються, якщо аргументи не змінюються.

Можливим рішенням є створення прапорця в одній комірці та використання цієї комірки як аргументу для користувацької функції:

  1. Створіть прапорець: виберіть безкоштовну комірку, наприклад [A1], перейдіть до [Вставити]> [Позначка]
  2. Зробіть цю комірку аргументом: =myFunction(A1)
  3. Поставте прапорець, щоб оновити формулу

-3

Якщо ви написали нестандартну функцію і використовували її у своїй електронній таблиці як формулу, то кожного разу, коли ви відкриваєте електронну таблицю або будь-яку комірку посилання, формула перераховується.

Якщо ви хочете просто продовжувати дивитись на електронну таблицю і хочете, щоб її значення змінювалися, тоді подумайте про додавання синхронізованого тригера, який оновить комірки. Детальніше про тригери читайте тут


10
Це було б просто чудово, за винятком того, що це не працює ... Перезавантаження сторінки не оновлює значення. Більш того, видалення комірки та повторне введення того самого виклику функції все ще зберігає старе значення. Якщо я викличу ту саму функцію точно з іншої комірки, вона покаже нове значення, але не в старій комірці.
tbkn23,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.