Angular で Office アドインを開発するDevelop Office Add-ins with Angular

この記事では、Angular 2+ を使って、単一ページのアプリケーションとして Office アドインを作成する方法を説明します。This article provides guidance for using Angular 2+ to create an Office Add-in as a single page application.

注意

Angular を使用して Office アドインを作成した経験を基に、何か投稿する内容がありますか。GitHub でこの記事に対して投稿するか、リポジトリで問題を提出することでフィードバックを提出できます。Do you have something to contribute based on your experience using Angular to create Office Add-ins? You can contribute to this article in GitHub or provide your feedback by submitting an issue in the repo.

Angular フレームワークを使用してビルドされる Office アドインのサンプルについては、「Angular でビルドする Word スタイル チェック アドイン」を参照してください。For an Office Add-ins sample that's built using the Angular framework, see Word Style Checking Add-in Built on Angular.

TypeScript 型の定義をインストールするInstall the TypeScript type definitions

nodejs ウィンドウを開き、コマンド ラインで次のように入力します:Open an nodejs window and enter the following at the command line:

npm install --save-dev @types/office-js

ブートス トラップは必ず Office.initialize 内にBootstrapping must be inside Office.initialize

Office、Word、Excel の JavaScript API を呼び出す任意のページで、コードでまずメソッドを Office.initialize プロパティに割り当てる必要があります。(初期化コードがない場合は、メソッド本文は空の "{}" 記号でも構いませんが、Office.initialize プロパティは未定義のままにはできません。詳細については、「アドインを初期化する」を参照してください。)Office の JavaScript ライブラリを初期化すると、すぐに Office でこのメソッドが呼び出されます。On any page that calls the Office, Word, or Excel JavaScript APIs, your code must first assign a method to the Office.initialize property. (If you have no initialization code, the method body can be just empty "{}" symbols, but you must not leave the Office.initialize property undefined. For details, see Initializing your add-in.) Office calls this method immediately after it has initialized the Office JavaScript libraries.

Angular のブートストラップ コードは Office.initialize に割り当てられたメソッドの中で呼び出すことで、Office の JavaScript ライブラリが最初に初期化されるようにする必要があります。以下は、これを行う方法を示した簡単な例です。このコードは、プロジェクトの main.ts ファイルの中にある必要があります。Your Angular bootstrapping code must be called inside the method that you assign to Office.initialize to ensure that the Office JavaScript libraries have initialized first. The following is a simple example that shows how to do this. This code should be in the main.ts file of the project.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

Office.initialize = function () {
  const platform = platformBrowserDynamic();
  platform.bootstrapModule(AppModule);
};

Angular アプリケーションで Hash Location Strategy を使うUse the hash location strategy in the Angular application

Hash Location Strategy を指定しないと、アプリケーションでルート間の移動が機能しない可能性があります。2 つの方法のいずれかでこれを行うことができます。1 つ目の方法は、次の例に示すとおり、アプリ モジュールでプロバイダーをロケーションの戦略に指定できます。これは app.module.ts ファイルに入ります。Navigating between routes in the application might not work if you don't specify the hash location strategy. You can do this in one of two ways. First, you can specify a provider for the location strategy in your app module, as shown in the following example. It goes into the app.module.ts file.

import { LocationStrategy, HashLocationStrategy } from '@angular/common';
// Other imports suppressed for brevity

@NgModule({
  providers: [
    { provide: LocationStrategy, useClass: HashLocationStrategy },
    // Other providers suppressed
  ],
  // Other module properties suppressed
})
export class AppModule { }

独立したルーティング モジュール内で、ルートを定義する場合は、Hash Location Strategy を指定する別の方法があります。ルーティング モジュールの .ts ファイルで、戦略を指定する forRoot 関数に構成オブジェクトを渡します。以下にコードの例を示します。If you define your routes in a separate routing module, there is an alternative way to specify the hash location strategy. In your routing module's .ts file, pass a configuration object to the forRoot function that specifies the strategy. The following code is an example.

import { RouterModule, Routes } from '@angular/router';
// Other imports suppressed for brevity

const routes: Routes = // route definitions go here

@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Fabric コンポーネントと Angular コンポーネントとのラッピングについて検討するConsider wrapping Fabric components with Angular components

アドインには Office UI Fabric のスタイルを使用することをお勧めしています。Fabric には、TypeScript に基づいたバージョンを含む、いくつかのバージョンに由来するコンポーネントが含まれています。Fabric コンポーネントを、Angular のコンポーネントでラッピングすることによってアドインで使用することを検討してください。これを行う方法を説明した例については、「Angular でビルドする Word スタイル チェック アドイン」を参照してください。たとえば、fabric.textfield.wrapper で定義されている Angular コンポーネントで Fabric ファイルの TextField.ts をインポートすると、その場所に Fabric コンポーネントが定義されます。We recommend using Office UI Fabric styling in your add-in. Fabric includes components that come in several versions, including a version based on TypeScript. Consider using Fabric components in your add-in by wrapping them in Angular components. For an example that shows you how to do this, see Word Style Checking Add-in Built on Angular. Note, for example, how the Angular component defined in fabric.textfield.wrapper imports the Fabric file TextField.ts, where the Fabric component is defined.

Angular で Office Dialog API を使用するUsing the Office Dialog API with Angular

Office のアドインの Dialog API を使えば、アドインは、メイン ページと情報をやり取りできるセミモーダル ダイアログ ボックスでページを開けることができます。通常、これは作業ウィンドウにあります。The Office add-in Dialog API enables your add-in to open a page in a semimodal dialog box that can exchange information with the main page, which is typically in a task pane.

DisplayDialogAsync メソッドは、ダイアログ ボックスで開くべきページの URL を指定するパラメーターを受け取ります。アドインでは、独立した HTML ページ (基本ページとは異なるページ) でこのパラメーターに渡すか、Angular アプリケーションでルートの URL を渡すことができます。The displayDialogAsync method takes a parameter that specifies the URL of the page that should open in the dialog box. Your add-in can have a separate HTML page (different from the base page) to pass to this parameter, or you can pass the URL of a route in your Angular appication.

重要な点として、ルートを渡すと、ダイアログ ボックスによって新しいウィンドウとその実行コンテキストが作成されることに注意してください。ダイアログ ボックスで、この新しいコンテキストに対して基本ページとそのすべての初期化、およびブートストラップ コードを再度実行し、すべての変数が初期値に設定されます。この手法により、ダイアログ ボックスで、単一ページのアプリケーションの 2 番目のインスタンスが起動します。ダイアログ ボックス内の変数を変更するコードは、同じ変数の作業ウィンドウのバージョンは変更しません。同様に、ダイアログ ボックスには、それ自体にセッション ストレージがあり、作業ウィンドウからコードでそこにアクセスすることはできません。It is important to remember, if you pass a route, that the dialog box creates a new window with its own execution context. Your base page and all its initialization and bootstrapping code run again in this new context, and any variables are set to their initial values in the dialog box. So this technique launches a second instance of your single page application in the dialog box. Code that changes variables in the dialog box does not change the task pane version of the same variables. Similarly, the dialog box has its own session storage, which is not accessible from code in the task pane.

UI の更新をトリガーするTrigger the UI update

Angular アプリでは UI が更新されない場合があります。これは、コード部分が Angular ゾーンの外から実行されるためです。解決策としては、次の例に示すように、ゾーン内にコードを配置します。In an Angular app, the UI sometimes does not update. This is because that part of the code runs out of the Angular zone. The solution is to put the code in the zone, as shown in the following example.

import { NgZone } from '@angular/core';

export class MyComponent {
  constructor(private zone: NgZone) { }

  myFunction() {
    this.zone.run(() => {
      // the codes that need update the UI
    });
  }
}

Observable を使用するUsing Observable

Angular は RxJS (JavaScript の事後対応型の拡張機能) を使用し、RxJS は ObservableObserver のオブジェクトを導入して非同期処理を実装します。このセクションでは、Observables の使い方についての概要を簡単に紹介しています。さらに詳細な情報については、RxJS の公式ドキュメントを参照してください。Angular uses RxJS (Reactive Extensions for JavaScript), and RxJS introduces Observable and Observer objects to implement asynchronous processing. This section provides a brief introduction to using Observables; for more detailed information, see the official RxJS documentation.

は、ある意味で Promise オブジェクトに似ています。非同期の呼び出しからすぐに返されますが、すぐには解決されない可能性があります。しかし、Promise は、単一の値 (配列オブジェクトのことがあります) なのに対し、Observable は、オブジェクトの配列 (メンバーが 1 つだけの可能性あり) です。そのため、コードで concatmapfilter などの配列メソッドObservable オブジェクトで呼び出すことができます。ObservableAn Observable is like a Promise object in some ways - it is returned immediately from an asynchronous call, but it might not resolve until some time later. However, while a Promise is a single value (which can be an array object), an Observable is an array of objects (possibly with only a single member). This enables code to call array methods, such as concat, map, and filter, on Observable objects.

プルではなくプッシュPushing instead of pulling

コードは Promise オブジェクトを変数に割り当てることによって "プル" しますが、Observable オブジェクトは、値を Observable登録するオブジェクトに、"プッシュ" します。サブスクライバーは、Observer オブジェクトです。プッシュ アーキテクチャの利点は、時間の経過と共に新しいメンバーを Observable 配列に追加できることです。新しいメンバーが追加されると、Observable に登録されるすべての Observer オブジェクトは通知を受信します。Your code "pulls" Promise objects by assigning them to variables, but Observable objects "push" their values to objects that subscribe to the Observable. The subscribers are Observer objects. The benefit of the push architecture is that new members can be added to the Observable array over time. When a new member is added, all the Observer objects that subscribe to the Observable receive a notification.

は、関数とともに新規の各オブジェクト ("next" オブジェクトと呼ばれる) を処理するように構成されます。(また、エラーと完了の通知に応答するようにも構成されます。例については、次のセクションを参照してください。)このため、Observable オブジェクトは、Promise オブジェクトよりも幅広いシナリオで使用できます。たとえば、AJAX 呼び出しから Observable を返すことに加えて、Promise を返し、Observable をテキスト ボックスの "変更" イベント ハンドラーなどのイベント ハンドラーから返すことができます。ユーザーがボックスにテキストを入力するたびに、登録されているすべての Observer オブジェクトが、最新のテキストや、アプリケーションの現在の状態を入力として使用することによって、すぐに対応します。ObserverThe Observer is configured to process each new object (called the "next" object) with a function. (It is also configured to respond to an error and a completion notification. See the next section for an example.) For this reason, Observable objects can be used in a wider range of scenarios than Promise objects. For example, in addition to returning an Observable from an AJAX call, the way you can return a Promise, an Observable can be returned from an event handler, such as the "changed" event handler for a text box. Each time a user enters text in the box, all the subscribed Observer objects react immediately using the latest text and/or the current state of the application as input.

すべての非同期呼び出しが完了するまで待機するWaiting until all asynchronous calls have completed

一連の Promise オブジェクトの各メンバーが解決されるときのみ確実にコールバックが実行されるようにしたい場合は、Promise.all() メソッドを使用します。When you want to ensure that a callback only runs when every member of a set of Promise objects has resolved, use the Promise.all() method.

myPromise.all([x, y, z]).then(
  // TODO: Callback logic goes here
)

オブジェクトで同じことを行うには、Observable.forkJoin() メソッドを使います。ObservableTo do the same thing with an Observable object, you use the Observable.forkJoin() method.

const source = Observable.forkJoin([x, y, z]);

const subscription = source.subscribe(
  x => {
    // TODO: Callback logic goes here
  },
  err => console.log('Error: ' + err),
  () => console.log('Completed')
);