アプリケーション固有の API モデルの使用

この記事では、Excel、Word、OneNote でアドインを構築するために API モデルを使用する方法について説明します。 この説明では、Promise ベースの API の使用に基本的な主要な概念を説明します。

注意

このモデルは、Office 2013 クライアントではサポートされていません。 これらの Office バージョンを使用しながら、共通のAPIモデル を使用します。 フル プラットフォーム可用性のノートについては、「Office アドイン用 Office クライアント アプリケーションとプラットフォームの可用性」を参照してください。

ヒント

このページの例では Excel JavaScript API を使用しますが、概念は OneNote、Visio、Word JavaScript API にも適用されます。

Promise ベース API の非同期の性質

Office アドインは、Excel などの Office アプリケーション内のブラウザー コンテナー内に表示される Web サイトです。 コンテナーは、Office on Windows などのデスクトップ ベースのプラットフォーム上の Office アプリケーションに組み込まれ、Office on the Web の HTML iFrame 内で実行されます。 パフォーマンスの考慮事項により、Office.js API は、すべてのプラットフォームの Office アプリケーションと同期して対話することはできません。 このため、sync()Office.js 内の API 呼び出しは Office アプリケーションが要求された読み取りまたは書き込み操作を完了したときに解決された Promiseを返します。 また、操作ごとに別個の要求として送信する代わりに、プロパティの設定やメソッドの起動など、複数の操作をキューに登録し、sync()への 1 回の呼び出しでコマンドのバッチとしてそれらを実行することもできます。 次のセクションでは、run() および sync() API を使用してこれを実行する方法について説明します。

*.run 関数

Excel.runWord.runOneNote.runは、Excel、Word、OneNote に対して実行するアクションを指定する関数を実行できます。 *.run は Office オブジェクトと対話するために使用できる要求コンテキストを自動的に作成します。 *.runが完了すると、Promose が解決され、実行時に割り当てられたすべてのオブジェクトが自動的に解放されます。

次の例は、Excel.runの使用方法を説明しています。 Word と OneNote でも同じパターンが使用されます。

Excel.run(function (context) {
    // Add your Excel JS API calls here that will be batched and sent to the workbook.
    console.log('Your code goes here.');
}).catch(function (error) {
    // Catch and log any errors that occur within `Excel.run`.
    console.log('error: ' + error);
    if (error instanceof OfficeExtension.Error) {
        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
    }
});

要求コンテキスト

Office アプリケーションとユーザーのアドインは、2 つの異なるプロセスで実行されます。 それらは異なるランタイム環境を使用するため、アドインは、ワークシート、範囲、グラフ、表など、Office のオブジェクトにユーザーのアドインを接続するために RequestContext オブジェクトが必要です。 この RequestContext オブジェクトは、*.runを呼び出す際に引数として提供されます。

プロキシ オブジェクト

Promise ベースの API と共にユーザーが宣言して使用する Office JavaScript オブジェクトはプロキシ オブジェクトです。 起動するメソッドや、プロキシ オブジェクトに設定または読み込まれるプロパティは、保留中のコマンドのキューに単純に追加されます。 要求コンテキスト上 (たとえば context.sync()) で sync()メソッドを呼び出すと、キューに入れられたコマンドは Office アプリケーションにディスパッチされて実行されます。 これらの API は、基本的にバッチ中心です。 要求コンテキストに必要なだけ変更内容をキューに登録し、sync() メソッドを呼び出して、キューに入れられたコマンドをバッチで実行することができます。

たとえば、次のコード スニペットでは、ローカル JavaScript Excel.Range オブジェクト、selectedRangeが Excel ワークブック内の選択範囲を参照することを宣言し、そのオブジェクトでいくつかのプロパティを設定します。 selectedRange オブジェクトはプロキシ オブジェクトであるため、設定されたプロパティと、そのオブジェクトに対して呼び出されたメソッドは、ユーザーのアドインが context.sync() を呼び出すまで Excel ドキュメントには反映されません。

var selectedRange = context.workbook.getSelectedRange();
selectedRange.format.fill.color = "#4472C4";
selectedRange.format.font.color = "white";
selectedRange.format.autofitColumns();

作業のヒント: 作成されたプロキシ オブジェクトの数を最小限にする

同じプロキシ オブジェクトを繰り返し作成することは避けるようにします。 代わりに、複数の操作で同じプロキシ オブジェクトが必要な場合は、一度作成して変数に割り当ててから、その変数をコードで使用します。

// BAD: Repeated calls to .getRange() to create the same proxy object.
worksheet.getRange("A1").format.fill.color = "red";
worksheet.getRange("A1").numberFormat = "0.00%";
worksheet.getRange("A1").values = [[1]];

// GOOD: Create the range proxy object once and assign to a variable.
var range = worksheet.getRange("A1")
range.format.fill.color = "red";
range.numberFormat = "0.00%";
range.values = [[1]];

// ALSO GOOD: Use a "set" method to immediately set all the properties without even needing to create a variable!
worksheet.getRange("A1").set({
    numberFormat: [["0.00%"]],
    values: [[1]],
    format: {
        fill: {
            color: "red"
        }
    }
});

sync()

要求コンテキストで sync()メソッドを呼び出すと、プロキシ オブジェクトと Officeドキュメント内のオブジェクトの状態が同期されます。 sync() メソッドは、要求コンテキストのキューに登録されたすべてのコマンドを実行し、プロキシ オブジェクトに読み込まれるプロパティの値を取得します。 sync()メソッドは非同期で実行されて Promise を返します。これは、sync() メソッドが完了すると解決されます。

次の例は、ローカル JavaScript proxy オブジェクト (selectedRange) を定義し、そのオブジェクトのプロパティを読み込み、JavaScript の Promises パターンを使用して context.sync() を呼び出し、プロキシ オブジェクトと Excel ドキュメント内のオブジェクトの状態を同期するバッチ関数を示しています。

Excel.run(function (context) {
    var selectedRange = context.workbook.getSelectedRange();
    selectedRange.load('address');
    return context.sync()
      .then(function () {
        console.log('The selected range is: ' + selectedRange.address);
    });
}).catch(function (error) {
    console.log('error: ' + error);
    if (error instanceof OfficeExtension.Error) {
        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
    }
});

前の例では、selectedRange が設定されており、context.sync() が呼び出されると address プロパティが読み込まれます。

sync()が非同期操作である場合、スクリプトが引き続き実行される前に、Promise オブジェクトを返して、sync()の操作が完了するのを確認する必要があります。 TypeScript または ES6+ JavaScript を使用している場合は、Promise を返す代わりに context.sync() の呼び出しをawait にできます。

作業のこつ: 同期呼び出しの数を最小限にする

Excel JavaScript API では、sync() は唯一の非同期操作で、状況によっては遅くなる可能性があり、Excel on the web の場合は特にその傾向があります。 パフォーマンスを最適化するには、sync() を呼び出す前にできるだけ多くの変更をキューイングして、呼び出しの数を最小限にします。 パフォーマンスをsync()で最適化する方法の詳細については、「ループで context.sync メソッドの使用を避ける」をご参照ください。

load()

プロキシ オブジェクトのプロパティを読み取るには、まず Office ドキュメントからプロキシ オブジェクトとデータを入力するためにプロパティを明確に読み込み、context.sync()を呼び出す必要があります。 たとえば、選択範囲を操作するプロキシ オブジェクトを作成してから選択範囲のaddress プロパティを読み取る場合、読み取る前にaddress プロパティを読み込む必要があります。 読み込むプロキシ オブジェクトのプロパティを要求するには、オブジェクトに対して load() メソッドを呼び出し、読み込むプロパティを指定します。 次の例は、myRangeに読み込まれているプロパティ Range.addressを示しています 。

Excel.run(function (context) {
    var sheetName = 'Sheet1';
    var rangeAddress = 'A1:B2';
    var myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);

    myRange.load('address');

    return context.sync()
      .then(function () {
        console.log (myRange.address);   // ok
        //console.log (myRange.values);  // not ok as it was not loaded
        });
    }).then(function () {
        console.log('done');
}).catch(function (error) {
    console.log('Error: ' + error);
    if (error instanceof OfficeExtension.Error) {
        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
    }
});

注意

プロキシ オブジェクト上でメソッドを呼び出す、またはプロパティを設定するだけの場合は、load() メソッドを呼び出す必要はありません。 load() メソッドは、プロキシ オブジェクト上でプロパティを読み取る場合のみ必要です。

プロキシ オブジェクトに対してプロパティを設定、またはメソッドを呼び出す要求と同じように、プロキシ オブジェクトに対してプロパティを読み込む要求も、要求コンテキストで保留中のコマンドのキューに追加され、次回 sync() メソッドを呼び出すときに実行されます。load() の呼び出しは、必要なだけ要求コンテキストのキューに入れることができます。

スカラー プロパティとナビゲーション プロパティ

プロパティには、スカラーナビゲーション という 2 つのカテゴリがあります。 スカラー プロパティは、文字列、整数、JSON 構造体などの割り当て可能な型です。 ナビゲーション プロパティは、プロパティを直接割り当てるのではなく、読み取り専用のオブジェクトと、そのフィールドが割り当てられているオブジェクトのコレクションです。 たとえば、Excel.Worksheetのオブジェクトの name メンバーと position メンバーはスカラー プロパティですが、protectiontables はナビゲーション プロパティです。

アドインは、特定のスカラー プロパティを読み込むパスとしてナビゲーション プロパティを使用できます。 次のコードは、ほかの情報を読み込む必要なく、Excel.Range オブジェクトで使用されるフォント名のload コマンドをキューに入れられます。

someRange.load("format/font/name")

パスを詳しく調べることでナビゲーション プロパティのスカラー プロパティを設定できます。 たとえば、someRange.format.font.size = 10;を使用してExcel.Range のフォント サイズを設定できます。 設定前にプロパティを読み込む必要はありません。

オブジェクトの下の「プロパティ」の中には、別のオブジェクトと同じ名前を持つものがあることに注意してください。 例えば、formatExcel.Rangeオブジェクトの下のプロパティですが、format それ自体もオブジェクトです。 そのため、range.load("format")などの呼び出しを行った場合、これは range.format.load() (望ましくない空の空白のステートメントload()) と同等になります。 これを避けるには、コードがオブジェクト ツリー内の "リーフノード" のみをロードするようにしてください。

パラメーターを指定せずにオブジェクト (またはコレクション) の load() メソッドを呼び出すと、オブジェクトのすべてのスカラー プロパティ (またはコレクション内のオブジェクト) が読み込まれます。 不要なデータを読み込むと、アドインの速度が低下します。 常に読み込むプロパティを明示的に指定する必要があります。

重要

パラメーターのない load ステートメントで返されるデータの量は、サービスのサイズ制限を超える場合があります。 古いアドインのリスクを軽減するために、明示的に要求しない限り load によって返されないプロパティがあります。 次のプロパティは、そのような読み込み操作で除外されます。

  • Excel.Range.numberFormatCategories

ClientResult

プリミティブ型を返す、Promise ベースの API 内のメソッドは、load/syncパラダイムと同様のパターンを持っています。 たとえば、Excel.TableCollection.getCount はコレクション内のテーブルの数を取得します。 getCountClientResult<number> を返します。つまり、返されるClientResultvalueプロパティは数値になります。 context.sync() が呼び出されるまで、スクリプトはその値にアクセスできません。

次のコードは、Excel ワークブック内のテーブルの総数を取得し、その数をコンソールに記録します。

var tableCount = context.workbook.tables.getCount();

// This sync call implicitly loads tableCount.value.
// Any other ClientResult values are loaded too.
return context.sync()
    .then(function () {
        // Trying to log the value before calling sync would throw an error.
        console.log (tableCount.value);
    });

set()

入れ子になったナビゲーション プロパティを持つオブジェクトのプロパティを設定するのは面倒です。 前述のナビゲーション パスを使用してプロパティを個別に設定する代わりに、Promise ベースの JavaScript API のオブジェクトで使用できる、object.set()メソッドを使用できます。 このメソッドを使用すると、同じ Office.js 型の別のオブジェクト、またはメソッドが呼び出されるオブジェクトのプロパティと同様に構造化されたプロパティを持つ JavaScript オブジェクトを渡すことによって、オブジェクトの複数のプロパティを一度に設定できます。

次のコード サンプルは、set() メソッドを呼び出し、RangeRange オブジェクトのプロパティの構造を反映するプロパティ名と型を持つ JavaScript オブジェクトを渡すことによって、範囲のいくつかの書式プロパティを設定します。この例では、範囲 B2:E2 にデータがあると仮定します。

Excel.run(function (ctx) {
    var sheet = ctx.workbook.worksheets.getItem("Sample");
    var range = sheet.getRange("B2:E2");
    range.set({
        format: {
            fill: {
                color: '#4472C4'
            },
            font: {
                name: 'Verdana',
                color: 'white'
            }
        }
    });
    range.format.autofitColumns();

    return ctx.sync();
}).catch(function(error) {
    console.log("Error: " + error);
    if (error instanceof OfficeExtension.Error) {
        console.log("Debug info: " + JSON.stringify(error.debugInfo));
    }
});

一部のプロパティを直接設定できません

書き込み可能であるにもかかわらず、一部のプロパティを設定できません。 これらのプロパティは、1 つのオブジェクトとして設定する必要がある親プロパティの一部です。 これは、親プロパティが特定の論理関係を持つサブプロパティに依存しているからです。 これらの親プロパティは、オブジェクトの個々のサブプロパティを設定するのではなく、オブジェクト全体を設定するためにオブジェクト リテラル表記を使用して設定する必要があります。 その 1 つの例は、PageLayoutにあります。 zoomプロパティは、1 つの PageLayoutZoomOptionsオブジェクト を使用して、以下のように設定する必要があります。

// PageLayout.zoom.scale must be set by assigning PageLayout.zoom to a PageLayoutZoomOptions object.
sheet.pageLayout.zoom = { scale: 200 };

前の例では、zoom 値: sheet.pageLayout.zoom.scale = 200;を直接割り当てることは できません。 このステートメントは、zoom が読み込まれないので、エラーを発生させます。 zoom が読み込まれるような場合でも、スケール セットは有効化されません。 すべてのコンテキスト操作は zoom上、でアドインのプロキシオブジェクトを更新し、ローカルに設定された値を上書きする場合に発生します。

この動作は、Range.formatなど、ナビゲーション プロパティ とは異なります。 ここに示されているように、formatのプロパティはオブジェクト ナビゲーションを使用して設定できます。

// This will set the font size on the range during the next `content.sync()`.
range.format.font.size = 10;

読み取り専用の修飾キーを確認することで、サブプロパティを直接設定できないプロパティを識別できます。 読み取り専用プロパティはすべて、読み取り専用以外のサブプロパティを直接設定できます。 PageLayout.zoom のような書き可能なプロパティは、そのレベルのオブジェクトで設定する必要があります。 まとめると、以下のようになります。

  • 読み取り専用プロパティ: ナビゲーション経由でサブプロパティを設定できます。
  • 書き込み可能なプロパティ: サブプロパティをナビゲーションを介して設定することはできません (最初の親オブジェクトの一部として設定する必要があります)。

*OrNullObject メソッドとプロパティ

一部のアクセサリ方法とプロパティでは、目的のオブジェクトが存在しない場合に例外をスローします。 たとえば、ブックに存在しないワークシート名を指定して Excel ワークシートを取得しようとすると、getItem() メソッドは ItemNotFound 例外を返します。 アプリケーション固有のライブラリを使用すると、例外処理コードを必要とせずに、コードがドキュメント エンティティの存在をテストできます。 これは、*OrNullObjectメソッドのバリエーションとプロパティ を使用して行います。 これらのバリエーションは、 isNullObject プロパティが trueに設定されているオブジェクトを返します (指定したアイテムが存在しない場合は、例外をスローしません)。

たとえば、Worksheets などのコレクションで getItemOrNullObject() メソッドを呼び出して、コレクションからのアイテムの取得を試行できます。 getItemOrNullObject() メソッドは、指定された項目が存在する場合はその項目を返し、それ以外の場合は isNullObjectプロパティが trueに設定されているオブジェクトを返します。 コードは、このプロパティを評価して、オブジェクトが存在するかどうかを判断できます。

注意

*OrNullObject のバリエーションは、JavaScript 値nullを返すことはありません。 通常の Office プロキシ オブジェクトを返します。 オブジェクトが表すエンティティが存在しない場合は、オブジェクトの isNullObject プロパティが trueに設定されます。 返されたオブジェクトの null 値または 真偽性はテストしません。 これは、決して nullfalseundefinedではありません。

次のコード サンプルは getItemOrNullObject() メソッドを使用して、"Data" という名前のワークシートの取得を試行します。 その名前のワークシートが存在しない場合は、新しいシートが作成されます。 コードはisNullObjectプロパティを読み込まないことにご注意ください。 Office は、context.syncが呼ばれると、自動的にこのプロパティを読み込みます。ですから、datasheet.load('isNullObject')のような名前で明示的に読み込む必要はありません。

var dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");

return context.sync()
    .then(function () {
        if (dataSheet.isNullObject) {
            dataSheet = context.workbook.worksheets.add("Data");
        }

        // Set `dataSheet` to be the second worksheet in the workbook.
        dataSheet.position = 1;
    });

関連項目