onOpen / onEditなどで出るエラーの対処方法 – Google Apps Script・スプレッドシート

2022年3月21日

Google Apps Scriptでは関数名にonOpenonEditなどを使えば、スプレッドシートが開かれたときや編集されたときにスクリプトを走らせることが可能です。

が、なぜかエラーが出たりスクリプトが無反応だったりするがあるんですよね。コードは間違っていないはずなのに。

今回はこれらシンプルトリガーでスクリプトが動かない原因と対処方法を解説します。

自分がハマったから記録残したいだけなんですけどね。いわゆる個人ブログの備忘録ってヤツ! 有能かは知らん。

原因はいろいろ考えられるけれど

実のところ、関数名をつけるだけでトリガーとなるonOpen / onEditはシンプルトリガーと呼ばれるもの。

// スプレッドシートを開くと発火するトリガー
function onOpen() {
  Browser.msgBox('Hello, how low')
}

// 値を編集すると発火するトリガー
function onEdit() {
  Browser.msgBox('Hello, how low')
}

上記はあくまでも一例で、ほかonInstall()、onSelectionChange()、doGet()、doPost()を含めて全6種類。

シークレットもあるよ! うそだよ!

(シークレットはウソですが、結構コロコロ変わるので増減する可能性はあり)

で、肝心の原因ですけど、主に考えられるのは下記。

  • コンテナバインドじゃない(ライブラリで呼び出してる)
  • Gmailなど承認が必要なサービスに関わる
  • ファイルが読み取り専用
  • 実行時間が30秒を超える

(ただしdoGetとdoPost除く)

解決方法

インストーラブルトリガーを使う

一番簡単な解決方法はシンプルトリガーをやめてインストーラブルトリガーを利用すること。

インストーラブルトリガーはどういうイベントでスクリプトが走るかを別途設定する必要があります。

その代わり、コンテナバインドじゃなくても使えたり、関数名を自由に設定できたり、定期的に自動で動かしたりできたりといろいろ便利。

(ただ、シンプルトリガーと同じく読み取り専用の場合はNG)

インストーラブルトリガーの使い方

GAS画面、左メニュー内にある時計マークのトリガーをクリック

インストーラブルトリガーはスクリプト上に書き込むのではなく、Google Apps Scriptのプロジェクト上で設定するトリガーです。

下記手順で設定します。

  1. 左側にある時計マークのトリガー
  2. 右下の+トリガーを追加
  3. もろもろ設定する
右下の +トリガーを追加をクリック

右下の「+トリガーを追加」をクリック

上記なら「onOpen」と同じくスプレッドシートファイルを開いたときにexampleFunctionが発火する

そのまんま、「実行する関数を選択する」にはトリガーで実行させたい関数を設定。

「イベントの種類を選択」の箇所にスクリプトが発火するタイミングを設定します。

起動時はonOpen()と同じくスプシを開いたとき。編集時はonEdit()と同じくスプシに変更が行われたとき。

編集時と変更時の違い

インストーラブルのトリガーの「編集時」と「変更時」の違いは、トリガーが反応する作業の範囲。

  • 編集時:セルの編集、行や列の追加・削除などで発火 変更時:セルの編集のみ発火
  • 編集時:セルの編集、行や列の追加・削除などで発火 変更時:セルの編集のみ発火

つまりセルの修正に限定されて反応する「変更時」に対し、「編集時」は行列の増減などより多くのケースでトリガーが反応するわけです。

シンプルトリガーのonEdit()はセル修正のみに反応する「変更時」と同じなので、行列の増減では反応しません。

以前はシンプルトリガー版「編集時」としてonChange()が使えたのですが、廃止されていまは使えない模様(2022年3月現在)。

シンプルトリガーにはない機能

ほか、インストーラブルトリガーなら時間・日程・曜日などタイマーで定期的に発火させたり、Googleカレンダーの内容を編集することで発火するトリガーも設定可能。

「イベントのソースを選択」の選択を変えることで設定できます。

タイマー型のトリガーは「日時手動型」、Googleカレンダーは「カレンダーから」を選択して設定します。

日時主導型はホントよく使いますよね。いろいろ自動化するのにすごくいい。

インストーラブルトリガーをスクリプトで仕込むことも可能

インストーラブルトリガーをスクリプトでセットすることもできます。さすがGAS。

トリガーをセットするスクリプトの例

let ss = SpreadsheetApp.getActiveSpreadsheet();

let functionName = 'exampleFunction'; // ①セットしたい関数の名前

function exampleFunction() {
  Browser.msgBox('スクリプト発火!')
}

function init() {
  try{
    let triggers = ScriptApp.getProjectTriggers();
    let target = triggers.findIndex(trigger => trigger.getHandlerFunction() === functionName);
    
    if(target !== -1) {
      Browser.msgBox('すでにトリガー登録済みです');
      return 0;
    }
    
    ScriptApp.newTrigger(functionName)
    .forSpreadsheet(ss)
    .onEdit() // ② トリガーを設定 例:onEdit()は編集時 / onOpen()は起動時(スプシを開いた時)
    .create();
    
  }catch(e){
    // エラー
    Logger.log(e);
    Browser.msgBox('下記エラーが出ました¥n' + e);
  }
}

主に編集が必要なのは①と②。

3行目①の変数部分にはインストーラブルトリガーセットしたい関数名を入れてください。トリガー設定画面で設定する「実行する関数を選択する」の箇所です。

21行目②はonOpen、onEditなどトリガーイベントの種類を入れてください。トリガー設定画面の「イベントの種類を選択」の箇所です。

ちなみに本コードは下記AGOHACKさんのサイトを参考にしました。

あと、トリガーの部分はGASレファレンスサイトで公開しているトリガービルダーを使うと色々作れます。

(公式が英語なのでGoogle翻訳にURLぶちこんで日本語に翻訳しています)

なお、スプシファイルごとコピーした場合、スクリプト内のコードはコピーされますが、設定してあるインストーラブルトリガーはコピーされません。トリガーはまっさらな状態でコピーされます。

本来、コピーで運用したいときはonOpenなどシンプルトリガーの方が適しているといえます。ちゃんと動いてくれればですけど……