この記事では、API モデルを使用して Excel、OneNote、PowerPoint、Visio、Wordでアドインをビルドする方法について説明します。 この説明では、Promise ベースの API の使用に基本的な主要な概念を説明します。
注:
Outlook クライアントと Project クライアントでは、このモデルはサポートされていません。 共通 API モデルを使用して、これらのアプリケーションを操作します。 フル プラットフォーム可用性のノートについては、「Office アドイン用 Office クライアント アプリケーションとプラットフォームの可用性」を参照してください。
ヒント
この記事の例では Excel JavaScript API を使用していますが、この概念は OneNote、PowerPoint、Visio、Word JavaScript API にも適用されます。 さまざまな Office アプリケーションでこれらの概念やその他の概念を使用する方法を示す完全な コード サンプルについては、「Office アドインのコード サンプル」を参照してください。
Promise ベース API の非同期の性質
Office アドインは、Excel などの Office アプリケーション内の Web ビュー コントロール内に表示される 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.run、OneNote.run、PowerPoint.run、および Word.run は、Excel、Word、OneNote に対して実行するアクションを指定する関数を実行します。
*.run は Office オブジェクトと対話するために使用できる要求コンテキストを自動的に作成します。
*.runが完了すると、promise が解決され、実行時に割り当てられたオブジェクトが自動的に解放されます。
次の例は、Excel.runの使用方法を説明しています。 OneNote、PowerPoint、Visio、Wordでも同じパターンが使用されます。
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 アプリケーションとアドインは、さまざまなプロセスで実行されます。 アドインはさまざまなランタイム環境を使用するため、ワークシート、範囲、段落、テーブルなどの Office オブジェクトに接続するには、 RequestContext オブジェクトが必要です。 この RequestContext オブジェクトは、 *.runを呼び出すときに引数として指定します。
プロキシ オブジェクト
Promise ベースの API と共にユーザーが宣言して使用する Office JavaScript オブジェクトはプロキシ オブジェクトです。 起動するメソッドや、プロキシ オブジェクトに設定または読み込まれるプロパティは、保留中のコマンドのキューに単純に追加されます。 要求コンテキスト上 (たとえば context.sync()) で sync()メソッドを呼び出すと、キューに入れられたコマンドは Office アプリケーションにディスパッチされて実行されます。 これらの API は、基本的にバッチ中心です。 要求コンテキストに必要なだけ変更内容をキューに登録し、sync() メソッドを呼び出して、キューに入れられたコマンドをバッチで実行することができます。
たとえば、次のコード スニペットでは、ローカル JavaScript Excel.Range オブジェクト、selectedRangeが Excel ワークブック内の選択範囲を参照することを宣言し、そのオブジェクトでいくつかのプロパティを設定します。
selectedRange オブジェクトはプロキシ オブジェクトであるため、設定されたプロパティと、そのオブジェクトに対して呼び出されたメソッドは、ユーザーのアドインが context.sync() を呼び出すまで Excel ドキュメントには反映されません。
const 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.
const 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 ドキュメント内のオブジェクトの状態を同期するバッチ関数を示しています。
await Excel.run(async (context) => {
const selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
await context.sync();
console.log('The selected range is: ' + selectedRange.address);
});
前の例では、selectedRange が設定されており、context.sync() が呼び出されると address プロパティが読み込まれます。
sync()は非同期操作であるため、スクリプトの実行を続行する前にsync()操作が完了するように、常に Promise オブジェクトを返します。 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を示しています 。
await Excel.run(async (context) => {
const sheetName = 'Sheet1';
const rangeAddress = 'A1:B2';
const myRange = context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);
myRange.load('address');
await context.sync();
console.log (myRange.address); // ok
//console.log (myRange.values); // not ok as it was not loaded
console.log('done');
});
注:
メソッドのみを呼び出す場合、またはプロキシ オブジェクトのプロパティを設定する場合は、 load() メソッドを呼び出す必要はありません。
load() メソッドは、プロキシ オブジェクト上でプロパティを読み取る場合のみ必要です。
プロキシ オブジェクトにプロパティを設定したり、メソッドを呼び出したりする要求と同様に、プロキシ オブジェクトのプロパティを読み込む要求は、要求コンテキストで保留中のコマンドのキューに追加され、次回 sync() メソッドを呼び出す際に実行されます。 要求コンテキストで必要な数の load() 呼び出しをキューに登録できます。
スカラー プロパティとナビゲーション プロパティ
プロパティには、スカラーとナビゲーションという 2 つのカテゴリがあります。 スカラー プロパティは、文字列、整数、JSON 構造体などの割り当て可能な型です。 ナビゲーション プロパティは、プロパティを直接割り当てるのではなく、読み取り専用のオブジェクトと、そのフィールドが割り当てられているオブジェクトのコレクションです。 たとえば、Excel.Worksheetのオブジェクトの name メンバーと position メンバーはスカラー プロパティですが、protection と tables はナビゲーション プロパティです。
アドインは、特定のスカラー プロパティを読み込むパスとしてナビゲーション プロパティを使用できます。 次のコードは、ほかの情報を読み込む必要なく、Excel.Range オブジェクトで使用されるフォント名のload コマンドをキューに入れられます。
someRange.load("format/font/name")
パスを詳しく調べることでナビゲーション プロパティのスカラー プロパティを設定できます。 たとえば、someRange.format.font.size = 10;を使用してExcel.Range のフォント サイズを設定できます。 設定前にプロパティを読み込む必要はありません。
オブジェクトの下のプロパティの中には、別のオブジェクトと同じ名前を持つものもあります。 例えば、format はExcel.Rangeオブジェクトの下のプロパティですが、format それ自体もオブジェクトです。 したがって、 range.load("format")などの呼び出しを行う場合、この呼び出しは range.format.load() (望ましくない空の load() ステートメント) と同じです。 この問題を回避するには、コードでオブジェクト ツリー内の "リーフ ノード" のみを読み込む必要があります。
コレクションからの読み込み
コレクションを操作する場合は、コレクションで load を使用して、コレクション内のすべてのオブジェクトのプロパティを読み込みます。
load、そのコレクション内の個々のオブジェクトの場合とまったく同じように使用します。
次のサンプル コードは、"Sample" ワークシート内のすべてのグラフに対して読み込まれ、ログに記録される name プロパティを示しています。
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const chartCollection = sheet.charts;
// Load the name property on every chart in the chart collection.
chartCollection.load("name");
await context.sync();
chartCollection.items.forEach((chart) => {
console.log(chart.name);
});
});
通常、load引数にはコレクションの items プロパティは含まれません。 項目プロパティを読み込む場合、すべての項目が読み込まれます。 ただし、コレクション内の項目をループしているが、項目の特定のプロパティを読み込む必要がない場合は、items プロパティをloadする必要があります。
次のサンプル コードは、"Sample" ワークシート内のすべてのグラフに設定されている name プロパティを示しています。
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const chartCollection = sheet.charts;
// Load the items property from the chart collection to set properties on individual charts.
chartCollection.load("items");
await context.sync();
chartCollection.items.forEach((chart, index) => {
chart.name = `Sample chart ${index}`;
});
});
パラメーターを使用せず (非推奨) に load を呼び出す
パラメーターを指定せずにオブジェクトまたはコレクションで load() メソッドを呼び出す場合は、オブジェクトまたはコレクションのオブジェクトのすべてのスカラー プロパティを読み込みます。 不要なデータを読み込むと、アドインの速度が低下します。 読み込むプロパティを常に明示的に指定します。
重要
パラメーターのない load ステートメントで返されるデータの量は、サービスのサイズ制限を超える場合があります。 古いアドインに対するリスクを軽減するために、サービスは明示的に要求せずに load することで一部のプロパティを返しません。 次のプロパティは、このような読み込み操作から除外されます。
Excel.Range.numberFormatCategories
ClientResult
プリミティブ型を返す promise ベースの API のメソッドは、 load/sync パラダイムと同様のパターンに従います。 たとえば、 Excel.TableCollection.getCount はコレクション内のテーブルの数を取得します。
getCountはClientResult<number>を返します。これは、返されるClientResultのvalue プロパティが数値であることを意味します。
context.sync() が呼び出されるまで、スクリプトはその値にアクセスできません。
次のコードは、Excel ワークブック内のテーブルの総数を取得し、その数をコンソールに記録します。
const tableCount = context.workbook.tables.getCount();
// This sync call implicitly loads tableCount.value.
// Any other ClientResult values are loaded too.
await context.sync();
// 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() メソッドを呼び出し、Range オブジェクト内のプロパティの構造をミラーするプロパティ名と型を持つ JavaScript オブジェクトを渡すことによって、範囲のいくつかの書式プロパティを設定します。 この例では、 B2:E2 の範囲にデータがあることを前提としています。
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:E2");
range.set({
format: {
fill: {
color: '#4472C4'
},
font: {
name: 'Verdana',
color: 'white'
}
}
});
range.format.autofitColumns();
await context.sync();
});
一部のプロパティを直接設定できません
書き込み可能であるにもかかわらず、一部のプロパティを設定できません。 これらのプロパティは、1 つのオブジェクトとして設定する必要がある親プロパティの一部です。 これは、親プロパティが特定の論理関係を持つサブプロパティに依存しているからです。 これらの親プロパティは、オブジェクトの個々のサブプロパティを設定するのではなく、オブジェクト全体を設定するためにオブジェクト リテラル表記を使用して設定する必要があります。 その 1 つの例は、PageLayoutにあります。
zoom プロパティは、次に示すように、単一の PageLayoutZoomOptions オブジェクトで設定する必要があります。
// PageLayout.zoom.scale must be set by assigning PageLayout.zoom to a PageLayoutZoomOptions object.
sheet.pageLayout.zoom = { scale: 200 };
前の例では、sheet.pageLayout.zoom.scale = 200; という値zoom直接割り当てることはできません。
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 値または改ざんをテストしないでください。
null、false、またはundefinedはありません。
次のコード サンプルは getItemOrNullObject() メソッドを使用して、"Data" という名前のワークシートの取得を試行します。 その名前のワークシートが存在しない場合、コードによって新しいシートが作成されます。 コードは、 isNullObject プロパティを読み込まないことに注意してください。 Office では、 context.sync が呼び出されたときにこのプロパティが自動的に読み込まれるので、 dataSheet.load('isNullObject')などと共に明示的に読み込む必要はありません。
await Excel.run(async (context) => {
let dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");
await context.sync();
if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}
// Set `dataSheet` to be the second worksheet in the workbook.
dataSheet.position = 1;
});
サポートを元に戻す
元に戻すは、アプリケーション固有の Office JavaScript API で部分的にサポートされています。 つまり、ユーザーは、元に戻すコマンドを使用してアドインによって行われた変更を元に戻すことができます。 特定の API で元に戻すことがサポートされていない場合、アプリケーションの元に戻すスタックはクリアされます。 つまり、その API を呼び出す前に、その API または何かの効果を元に戻すことはできません。
元に戻すための API サポートは引き続き拡張されていますが、現在は不完全です。 元に戻すサポートに依存するようにアドインを設計することをお勧めします。
関連項目
Office Add-ins