ダッシュボード ウィジェットの追加

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

ダッシュボード上のウィジェットは、拡張機能フレームワークコントリビューションとして実装されます。 1 つの拡張機能に複数のコントリビューションを含めることができます。 複数のウィジェットをコントリビューションとして使用して拡張機能を作成する方法について説明します。

この記事は 3 つの部分に分かれています。各部分は前の上に構築されています。単純なウィジェットから始まり、包括的なウィジェットで終わるのです。

ヒント

Azure DevOps 拡張機能 SDK を使用した拡張機能開発に関する最新のドキュメントを確認してください。

このチュートリアルの準備と必要なセットアップ

Azure DevOps または TFS の拡張機能を作成するには、いくつかの前提条件となるソフトウェアとツールが必要です。

ナレッジ: ウィジェットの開発には、JavaScript、HTML、CSS に関する知識が必要です。

  • ウィジェットをインストールしてテストするための Azure DevOps の組織については、こちらを参照してください
  • テキスト エディター。 多くのチュートリアルでは、 を使用Visual Studio Codeしました。このチュートリアルはここからダウンロードできます。
  • 最新バージョンのノード(ここからダウンロードできます)
  • 拡張機能をパッケージ化するための Azure DevOps 用クロスプラットフォーム CLI (tfx-cli )。
    • tfx-cli は を使用して npmインストールできます。これは、 を実行してNode.jsのコンポーネントです。 npm i -g tfx-cli
  • プロジェクトのホーム ディレクトリ。 このディレクトリは、チュートリアル全体で と home 呼ばれます。

拡張ファイルの構造:

|--- README.md
|--- sdk    
    |--- node_modules           
    |--- scripts
        |--- VSS.SDK.min.js       
|--- img                        
    |--- logo.png                           
|--- scripts                        
|--- hello-world.html               // html page to be used for your widget  
|--- vss-extension.json             // extension's manifest

チュートリアルで見つかる内容

  1. このガイドの最初の部分では、単純な "Hello World" メッセージを出力する新しいウィジェットを作成する方法について説明します。
  2. 2 番目のパートでは、Azure DevOps REST API への呼び出しを追加することで、最初の部分を基に構築します。
  3. 3 番目のパートでは、ウィジェットに構成を追加する方法について説明します。

Note

急いですぐにコードを入手したい場合は、 こちらのサンプルをダウンロードできます。 ダウンロードしたら、フォルダーに widgets 移動し、 手順 6手順 7 に直接従って、さまざまな複雑さの 3 つのサンプル ウィジェットを含むサンプル拡張機能を公開します。

すぐに使用できる ウィジェットの基本的なスタイルと、ウィジェット 構造に関するいくつかのガイダンスを開始します。

パート 1: Hello World

このパートでは、JavaScript を使用して "Hello World" を出力するウィジェットを示します。

Overview dashboard with a sample widget

手順 1: クライアント SDK を取得する - VSS.SDK.min.js

コア SDK スクリプト は、 VSS.SDK.min.jsWeb 拡張機能がホストの Azure DevOps フレームと通信できるようにします。 スクリプトは、初期化、拡張機能の読み込み通知、または現在のページに関するコンテキストの取得などの操作を実行します。 クライアント SDK VSS.SDK.min.js ファイルを取得し、Web アプリに追加します。 フォルダーに home/sdk/scripts 配置します。

'npm install' コマンドを使用して SDK を取得します。

npm install vss-web-extension-sdk

SDK の詳細については、 クライアント SDK の GitHub ページを参照してください。

手順 2: HTML ページ - hello-world.html

HTML ページは、レイアウトを一緒に保持し、CSS と JavaScript への参照を含む接着です。 このファイルには何でも名前を付けることができます。必ず、使用する名前ですべての参照を hello-world 更新してください。

ウィジェットは HTML ベースで、 iframe でホストされています。 に以下の HTML を hello-world.html追加します。 必須参照を file にVSS.SDK.min.js追加し、 に 要素をh2含めます。この要素は、今後の手順でHello World文字列で更新されます。

    <!DOCTYPE html>
    <html>
        <head>          
            <script src="sdk/scripts/VSS.SDK.min.js"></script>              
        </head>
        <body>
            <div class="widget">
                <h2 class="title"></h2>
            </div>
        </body>
    </html>

HTML ファイルを使用していても、スクリプトとリンク以外の HTML ヘッド要素のほとんどはフレームワークによって無視されます。

手順 3: JavaScript

JavaScript を使用して、ウィジェット内のコンテンツをレンダリングします。 この記事では、すべての JavaScript コードを HTML ファイルの 要素内 &lt;script&gt; でラップします。 このコードを別の JavaScript ファイルに含め、HTML ファイルで参照することもできます。 コードはコンテンツをレンダリングします。 この JavaScript コードでは、VSS SDK も初期化され、ウィジェットのコードがウィジェット名にマップされ、ウィジェットの成功または失敗が拡張機能フレームワークに通知されます。 ここでは、ウィジェットに "Hello World" を出力するコードを次に示します。 この script 要素を head HTML の の に追加します。

    <script type="text/javascript">
        VSS.init({                        
            explicitNotifyLoaded: true,
            usePlatformStyles: true
        });

        VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
            WidgetHelpers.IncludeWidgetStyles();
            VSS.register("HelloWorldWidget", function () {                
                return {
                    load: function (widgetSettings) {
                        var $title = $('h2.title');
                        $title.text('Hello World');

                        return WidgetHelpers.WidgetStatusHelper.Success();
                    }
                }
            });
            VSS.notifyLoadSucceeded();
        });
    </script>

VSS.init は、ウィジェットをホストしている iframe とホスト フレームの間のハンドシェイクを初期化します。 読み込みが完了したときにウィジェットがホストに明示的に通知できるように、 を渡 explicitNotifyLoaded: true します。 このコントロールを使用すると、依存モジュールが読み込まれたことを確認した後、読み込みの完了を通知できます。 ウィジェットで html 要素 (body、div など) の Azure DevOps コア スタイルを使用できるように、 を渡 usePlatformStyles: true します。 ウィジェットでこれらのスタイルを使用しない場合は、 を渡 usePlatformStyles: falseすことができます。

VSS.require は、必要な VSS スクリプト ライブラリを読み込むのに使用されます。 このメソッドを呼び出すと、 JQuery や JQueryUI などの一般的なライブラリが自動的に読み込 まれます。 ここでは、ウィジェット フレームワークにウィジェットの状態を伝えるために使用される WidgetHelpers ライブラリに依存しています。 そのため、対応するモジュール名 TFS/Dashboards/WidgetHelpers とコールバックを に VSS.require渡します。 コールバックは、モジュールが読み込まれた後に呼び出されます。 コールバックには、ウィジェットに必要な残りの JavaScript コードがあります。 コールバックの最後に を呼び出 VSS.notifyLoadSucceeded して、読み込みの完了を通知します。

WidgetHelpers.IncludeWidgetStyles には、作業を開始するための 基本的な css を含むスタイルシートが含まれています。 これらのスタイルを使用するには、HTML 要素内のコンテンツを クラス widget でラップしてください。

VSS.register は、拡張機能のさまざまなコントリビューション間でウィジェットを一意に識別する JavaScript の関数をマップするために使用されます。 この名前は、手順 5 で説明されているように、コントリビューションを識別する と一致しているid必要があります。 ウィジェットの場合、 に VSS.register 渡される関数はコントラクトを満たす IWidget オブジェクトを返す必要があります。たとえば、返されるオブジェクトには、ウィジェットをレンダリングするコア ロジックを持つ別の関数である値を持つ load プロパティが必要です。 ここでは、要素のテキストh2を "Hello World" に更新します。 これは、ウィジェット フレームワークがウィジェットをインスタンス化するときに呼び出されるこの関数です。 From WidgetHelpers を WidgetStatusHelper 使用して、 を WidgetStatus 成功として返します。

警告

ウィジェットの登録に使用される名前がマニフェスト内のコントリビューションの ID と一致しない場合、ウィジェットは予期せず機能します。

vss-extension.json 常に フォルダーのルートにある必要があります (このガイド HelloWorldでは )。 他のすべてのファイルについては、フォルダー内の任意の構造に配置できます。HTML ファイルと vss-extension.json マニフェストで参照を適切に更新してください。

手順 4: 拡張機能のロゴ: logo.png

ロゴは Marketplace に表示され、ユーザーが拡張機能をインストールするとウィジェット カタログに表示されます。

98 px x 98 px のカタログ アイコンが必要です。 イメージを選択し、 という名前を付け logo.png、 フォルダーに img 配置します。

TFS 2015 Update 3 をサポートするには、330 px x 160 px の追加イメージが必要です。 このプレビュー イメージは、このカタログに表示されます。 イメージを選択し、 という名前を preview.png付けて、以前と同様にフォルダーに img 配置します。

これらのイメージには、次の手順の拡張機能マニフェストが使用する名前で更新されている限り、必要に応じて名前を付けることができます。

手順 5: 拡張機能のマニフェスト: vss-extension.json

次の内容を含む json ファイル (vss-extension.jsonたとえば) を home ディレクトリに作成します。

    {
        "manifestVersion": 1,
        "id": "vsts-extensions-myExtensions",
        "version": "1.0.0",
        "name": "My First Set of Widgets",
        "description": "Samples containing different widgets extending dashboards",
        "publisher": "fabrikam",
        "categories": ["Azure Boards"],
        "targets": [
            {
                "id": "Microsoft.VisualStudio.Services"
            }
        ],
        "icons": {
            "default": "img/logo.png"
        },
        "contributions": [
            {
                "id": "HelloWorldWidget",
                "type": "ms.vss-dashboards-web.widget",
                "targets": [
                    "ms.vss-dashboards-web.widget-catalog"
                ],
                "properties": {
                    "name": "Hello World Widget",
                    "description": "My first widget",
                    "catalogIconUrl": "img/CatalogIcon.png",
                    "previewImageUrl": "img/preview.png",                            
                    "uri": "hello-world.html",
                    "supportedSizes": [
                         {
                                "rowSpan": 1,
                                "columnSpan": 2
                            }
                        ],
                    "supportedScopes": ["project_team"]
                }
            }
        ],
        "files": [
            {
                "path": "hello-world.html", "addressable": true
            },
            {
                "path": "sdk/scripts", "addressable": true
            },
            {
                "path": "img", "addressable": true
            }
        ]
    }

必要な属性の詳細については、「拡張機能マニフェストリファレンス」を参照してください。

Note

ここで の発行元 は、発行元名に変更する必要があります。 発行元を今すぐ作成するには、 パッケージ/発行/インストールに関するページを参照してください。

アイコン

アイコン スタンザは、マニフェスト内の拡張機能のアイコンへのパスを指定します。

貢献

各コントリビューション エントリはプロパティを定義 します

  • 投稿を識別する ID 。 この ID は拡張機能内で一意である必要があります。 この ID は、 手順 3 でウィジェットを登録するために使用した名前と一致している必要があります。
  • コントリビューションの 種類 。 すべてのウィジェットの場合、型は である ms.vss-dashboards-web.widget必要があります。
  • コントリビューションが貢献している ターゲット の配列。 すべてのウィジェットのターゲットは である [ms.vss-dashboards-web.widget-catalog]必要があります。
  • プロパティは、コントリビューション型のプロパティを含むオブジェクトです。 ウィジェットの場合は、次のプロパティが必須です。
プロパティ 内容
name ウィジェット カタログに表示するウィジェットの名前。
description ウィジェット カタログに表示するウィジェットの説明。
catalogIconUrl ウィジェット カタログに表示する 手順 4 で追加したカタログ アイコンの相対パス。 画像は 98 px x 98 px にする必要があります。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。
previewImageUrl TFS 2015 Update 3 のウィジェット カタログにのみ表示する 手順 4 で追加したプレビュー イメージの相対パス。 画像は 330 px x 160 px にする必要があります。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。
uri 手順 1 で追加した HTML ファイルの相対パス。 別のフォルダー構造または別のファイル名を使用した場合は、ここで適切な相対パスを指定します。
supportedSizes ウィジェットでサポートされているサイズの配列。 ウィジェットで複数のサイズがサポートされている場合、配列の最初のサイズはウィジェットの既定のサイズです。 widget sizeは、ダッシュボード グリッド内のウィジェットによって占有される行と列に対して指定されます。 1 つの行/列が 160 px に対応します。 1x1 を超えるディメンションでは、ウィジェット間の余白を表す追加の 10 px が取得されます。 たとえば、3x2 ウィジェットは幅が 160*3+10*2 広く 160*2+10*1 高いです。 サポートされる最大サイズは です 4x4
supportedScopes 現時点では、チーム ダッシュボードのみがサポートされています。 値は である必要があります project_team。 今後、他のダッシュボード スコープをサポートする場合は、ここから選択できるオプションがさらに増えます。

ファイル

ファイル スタンザには、パッケージに含めるファイル (HTML ページ、スクリプト、SDK スクリプト、ロゴ) が示されます。 true URL アドレス指定可能である必要のない他のファイルを含めない限り、 を に設定addressableします。

Note

拡張機能マニフェスト ファイルのプロパティや実行内容など、拡張機能マニフェスト ファイルの詳細については、拡張機能マニフェスト リファレンスを参照してください

手順 6: パッケージ化、発行、共有

拡張機能を記述したら、それを Marketplace に取り込むための次の手順は、すべてのファイルをまとめてパッケージ化することです。 すべての拡張機能は、VSIX 2.0 互換の .vsix ファイルとしてパッケージ化されています。Microsoft は、拡張機能をパッケージ化するためのクロスプラットフォーム コマンド ライン インターフェイス (CLI) を提供します。

パッケージ化ツールを取得する

コマンド ラインから、Node.jsのコンポーネントである を使用してnpm、Azure DevOps 用クロスプラットフォーム CLI (tfx-cli) をインストールまたは更新できます。

npm i -g tfx-cli

拡張機能をパッケージ化する

tfx-cli を使用すると、拡張子を .vsix ファイルに簡単にパッケージ化できます。 拡張機能のホーム ディレクトリに移動し、次のコマンドを実行します。

tfx extension create --manifest-globs vss-extension.json

Note

拡張機能/統合のバージョンは、更新するたびにインクリメントする必要があります。
既存の拡張機能を更新する場合は、マニフェストのバージョンを更新するか、コマンド ライン スイッチを --rev-version 渡します。 これにより、拡張機能の パッチ バージョン番号がインクリメントされ、新しいバージョンがマニフェストに保存されます。

.vsix ファイルにパッケージ化された拡張機能を作成したら、拡張機能を Marketplace に発行する準備が整います。

拡張機能の発行元を作成する

Microsoft の拡張機能を含むすべての拡張機能は、発行元によって提供されていると識別されます。 既存のパブリッシャーのメンバーでない場合は、作成します。

  1. Visual Studio Marketplace 発行ポータルにサインインする
  2. 既存のパブリッシャーのメンバーでない場合は、発行元の作成を求められます。 発行元の作成を求めるメッセージが表示されない場合は、ページの下部まで下にスクロールし、[関連サイト] の下にある [拡張機能の発行] を選択します。
    • 発行元の識別子を指定します。次に例を示します。 mycompany-myteam
      • 識別子は、拡張機能のマニフェスト ファイル内の publisher 属性の値として使用されます。
    • 次のように、発行元の表示名を指定します。 My Team
  3. Marketplace パブリッシャー契約を確認し、[作成] を選択します

これで、発行元が定義されました。 今後のリリースでは、発行元の拡張機能を表示および管理するためのアクセス許可を付与できます。 チームと組織は、共通の発行元の下で拡張機能を簡単かつ安全に公開できますが、一連のユーザー間で資格情報のセットを共有する必要はありません。

サンプルのマニフェスト ファイルを vss-extension.json 更新して、ダミーの発行元 ID を発行元 ID fabrikam に置き換えます。

拡張機能を発行して共有する

パブリッシャーを作成した後、拡張機能を Marketplace にアップロードできるようになりました。

  1. [ 新しい拡張機能のアップロード ] ボタンを見つけて、パッケージ化された .vsix ファイルに移動し、[アップロード] を選択 します

また、1 つの手順で拡張機能をパッケージ化して発行する代わりに tfx extension create コマンドをtfx extension publish使用して、コマンド ラインを使用して拡張機能をアップロードすることもできます。 必要に応じて を使用 --share-with して、公開後に拡張機能を 1 つ以上のアカウントと共有できます。 個人用アクセス トークンも必要です。

tfx extension publish --manifest-globs your-manifest.json --share-with yourOrganization

手順 7: カタログからウィジェットを追加する

  1. Azure DevOps でプロジェクトに移動します。 http://dev.azure.com/{yourOrganization}/{yourProject}

  2. [ 概要] を選択し、[ダッシュボード] を選択 します

  3. [ ウィジェットの追加] を選択します

  4. ウィジェットを強調表示し、[ 追加] を選択します。

    ウィジェットがダッシュボードに表示されます。

パート 2: Azure DevOps REST API を使用したHello World

ウィジェットは、Azure DevOps 内の 任意の REST API を 呼び出して、Azure DevOps リソースと対話できます。 この例では、WorkItemTracking 用 REST API を使用して、既存のクエリに関する情報を取得し、"Hello World" テキストのすぐ下にあるウィジェットにクエリ情報を表示します。

Overview dashboard with a sample widget using the REST API for WorkItemTracking.

手順 1: HTML

前の例のファイル hello-world.html をコピーし、コピーの名前を に hello-world2.html変更します。 フォルダーは次のようになります。

|--- README.md
|--- sdk    
    |--- node_modules           
    |--- scripts
        |--- VSS.SDK.min.js       
|--- img                        
    |--- logo.png                           
|--- scripts                        
|--- hello-world.html               // html page to be used for your widget  
|--- hello-world2.html              // renamed copy of hello-world.html
|--- vss-extension.json             // extension's manifest

クエリ情報を保持するために、'h2' のすぐ下に新しい 'div' 要素を追加します。 "VSS.register" を呼び出す行で、ウィジェットの名前を "HelloWorldWidget" から "HelloWorldWidget2" に更新します。 これにより、フレームワークは拡張機能内のウィジェットを一意に識別できます。
<!DOCTYPE html>
<html>
    <head>                          
        <script src="sdk/scripts/VSS.SDK.min.js"></script>              
        <script type="text/javascript">
            VSS.init({
                explicitNotifyLoaded: true,
                usePlatformStyles: true
            });

            VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
                WidgetHelpers.IncludeWidgetStyles();
                VSS.register("HelloWorldWidget2", function () {                
                    return {
                        load: function (widgetSettings) {
                            var $title = $('h2.title');
                            $title.text('Hello World');

                            return WidgetHelpers.WidgetStatusHelper.Success();
                        }
                    }
                });
                VSS.notifyLoadSucceeded();
            });       
        </script>
    </head>
    <body>
        <div class="widget">
            <h2 class="title"></h2>
            <div id="query-info-container"></div>
        </div>
    </body>
</html>

手順 2: Azure DevOps リソースにアクセスする

Azure DevOps リソースへのアクセスを有効にするには、拡張機能マニフェストで スコープ を指定する必要があります。 スコープを vso.work マニフェストに追加します。
このスコープは、ウィジェットがクエリと作業項目への読み取り専用アクセス権を必要とすることを示します。 使用可能なすべてのスコープについては、 こちらを参照してください。 拡張機能マニフェストの末尾に以下を追加します。

{
    ...,
    "scopes":[
        "vso.work"
    ]
}

警告

拡張機能の発行後にスコープを追加または変更することは現在サポートされていません。 拡張機能を既にアップロードしている場合は、Marketplace から削除します。 Visual Studio Marketplace Publishing Portalに移動し、拡張機能を右クリックして [削除] を選択します。

手順 3: REST API 呼び出しを行う

AZURE DevOps で REST API 呼び出しを行うために SDK を介してアクセスできるクライアント側ライブラリは多数あります。 これらのライブラリは REST クライアントと呼ばれ、使用可能なすべてのサーバー側エンドポイントの Ajax 呼び出しに関する JavaScript ラッパーです。 Ajax 呼び出しを自分で記述する代わりに、これらのクライアントによって提供されるメソッドを使用できます。 これらのメソッドは、コードで使用できるオブジェクトに API 応答をマップします。

この手順では、呼び出しを VSS.require 更新して読み込みます TFS/WorkItemTracking/RestClient。これにより、WorkItemTracking REST クライアントが提供されます。 この REST クライアントを使用して、 フォルダー Shared Queriesの下にある というFeedbackクエリに関する情報を取得できます。

に渡す関数内に VSS.register、現在のプロジェクト ID を保持する変数を作成します。 クエリをフェッチするには、この変数が必要です。 また、REST クライアントを使用する新しいメソッド getQueryInfo も作成します。 その後、load メソッドから呼び出されるこのメソッド。

メソッド getClient は、必要な REST クライアントのインスタンスを提供します。 メソッド getQuery は、promise でラップされたクエリを返します。 更新された VSS.require 内容は次のようになります。

VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/WorkItemTracking/RestClient"], 
    function (WidgetHelpers, TFS_Wit_WebApi) {
        WidgetHelpers.IncludeWidgetStyles();
        VSS.register("HelloWorldWidget2", function () { 
            var projectId = VSS.getWebContext().project.id;

            var getQueryInfo = function (widgetSettings) {
                // Get a WIT client to make REST calls to Azure DevOps Services
                return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
                    .then(function (query) {
                        // Do something with the query

                        return WidgetHelpers.WidgetStatusHelper.Success();
                    }, function (error) {                            
                        return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
                    });
            }

            return {
                load: function (widgetSettings) {
                    // Set your title
                    var $title = $('h2.title');
                    $title.text('Hello World');

                    return getQueryInfo(widgetSettings);
                }
            }
        });
        VSS.notifyLoadSucceeded();
    });

から Failure メソッド WidgetStatusHelperを使用していることに注意してください。 これにより、エラーが発生したことをウィジェット フレームワークに示し、すべてのウィジェットに提供される標準エラー エクスペリエンスを利用できます。

フォルダーの下Shared QueriesFeedbackクエリがない場合は、コード内をプロジェクトに存在するクエリのパスに置き換えますShared Queries\Feedback

手順 4: 応答を表示する

最後の手順では、ウィジェット内にクエリ情報をレンダリングします。 関数は getQuery 、promise 内の型 Contracts.QueryHierarchyItem のオブジェクトを返します。 この例では、クエリ ID、クエリ名、クエリ作成者の名前を "Hello World" テキストの下に表示します。 コメントを // Do something with the query 次のように置き換えます。

    // Create a list with query details                                
    var $list = $('<ul>');                                
    $list.append($('- ').text("Query Id: " + query.id));
    $list.append($('- ').text("Query Name: " + query.name));
    $list.append($('- ').text("Created By: " + ( query.createdBy? query.createdBy.displayName: "<unknown>" ) ) );                                                            

    // Append the list to the query-info-container
    var $container = $('#query-info-container');
    $container.empty();
    $container.append($list);

最終的な hello-world2.html 内容は次のとおりです。

<!DOCTYPE html>
<html>
<head>    
    <script src="sdk/scripts/VSS.SDK.min.js"></script>
    <script type="text/javascript">
        VSS.init({
            explicitNotifyLoaded: true,
            usePlatformStyles: true
        });

        VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/WorkItemTracking/RestClient"], 
            function (WidgetHelpers, TFS_Wit_WebApi) {
                WidgetHelpers.IncludeWidgetStyles();
                VSS.register("HelloWorldWidget2", function () {                
                    var projectId = VSS.getWebContext().project.id;

                    var getQueryInfo = function (widgetSettings) {
                        // Get a WIT client to make REST calls to Azure DevOps Services
                        return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
                            .then(function (query) {
                                // Create a list with query details                                
                                var $list = $('<ul>');
                                $list.append($('- ').text("Query ID: " + query.id));
                                $list.append($('- ').text("Query Name: " + query.name));
                                $list.append($('- ').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));

                                // Append the list to the query-info-container
                                var $container = $('#query-info-container');
                                $container.empty();
                                $container.append($list);

                                // Use the widget helper and return success as Widget Status
                                return WidgetHelpers.WidgetStatusHelper.Success();
                            }, function (error) {
                                // Use the widget helper and return failure as Widget Status
                                return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
                            });
                    }

                    return {
                        load: function (widgetSettings) {
                            // Set your title
                            var $title = $('h2.title');
                            $title.text('Hello World');

                            return getQueryInfo(widgetSettings);
                        }
                    }
                });
            VSS.notifyLoadSucceeded();
        });       
    </script>

</head>
<body>
    <div class="widget">
        <h2 class="title"></h2>
        <div id="query-info-container"></div>
    </div>
</body>
</html>

手順 5: 拡張機能マニフェストの更新

この手順では、拡張機能マニフェストを更新して、2 番目のウィジェットのエントリを含めます。 プロパティの 配列に新しいコントリビューションを contributions 追加し、新しいファイル hello-world2.html を files プロパティの配列に追加します。 2 番目のウィジェットには別のプレビュー イメージが必要です。 これに preview2.png 名前を付け、 フォルダーに img 配置します。

 {
     ...,
     "contributions":[
         ...,
        {
             "id": "HelloWorldWidget2",
             "type": "ms.vss-dashboards-web.widget",
             "targets": [
                 "ms.vss-dashboards-web.widget-catalog"
             ],
             "properties": {
                 "name": "Hello World Widget 2 (with API)",
                 "description": "My second widget",
                 "previewImageUrl": "img/preview2.png",                            
                 "uri": "hello-world2.html",
                 "supportedSizes": [
                      {
                             "rowSpan": 1,
                             "columnSpan": 2
                         }
                     ],
                 "supportedScopes": ["project_team"]
             }
         }

     ],
     "files": [
         {
             "path": "hello-world.html", "addressable": true
         },
         {
             "path": "hello-world2.html", "addressable": true
         },      
         {
             "path": "sdk/scripts", "addressable": true
         },
         {
             "path": "img", "addressable": true
         }
     ],
     "scopes":[
         "vso.work"
     ]
 }

手順 6: パッケージ化、発行、共有

拡張機能をパッケージ化、発行、共有します。 拡張機能を既に公開している場合は、拡張機能を再パッケージ化し、Marketplace に直接更新できます。

手順 7: カタログからウィジェットを追加する

次に、 のチーム ダッシュボードに https:\//dev.azure.com/{yourOrganization}/{yourProject}移動します。 このページが既に開いている場合は、更新します。 右下にある [編集] ボタンにカーソルを合わせ、[追加] ボタンを選択します。 ウィジェット カタログが開き、インストールしたウィジェットが見つかります。 ウィジェットを選択し、[追加] ボタンを選択してダッシュボードに追加します。

パート 3: 構成を使用したHello World

このガイドの パート 2 では、ハードコーディングされたクエリのクエリ情報を表示するウィジェットを作成する方法について説明しました。 このパートでは、ハードコーディングされたクエリではなく、使用するクエリを構成する機能を追加します。 構成モードの場合、ユーザーは変更に基づいてウィジェットのライブ プレビューを表示します。 ユーザーが [保存] を選択すると、これらの変更がダッシュボードのウィジェットに 保存されます

Overview dashboard live preview of the widget based on changes.

手順 1: HTML

ウィジェットとウィジェット構成の実装は、よく似ています。 どちらも、コントリビューションとして拡張フレームワークで実装されます。 どちらも同じ SDK ファイル を使用します VSS.SDK.min.js。 どちらも HTML、JavaScript、CSS に基づいています。

前の例のファイル html-world2.html をコピーし、コピーの名前を に hello-world3.html変更します。 という名前 configuration.htmlの別の HTML ファイルを追加します。 フォルダーは次の例のようになります。

|--- README.md
|--- sdk    
    |--- node_modules           
    |--- scripts
        |--- VSS.SDK.min.js       
|--- img                        
    |--- logo.png                           
|--- scripts          
|--- configuration.html                          
|--- hello-world.html               // html page to be used for your widget  
|--- hello-world2.html              // renamed copy of hello-world.html
|--- hello-world3.html              // renamed copy of hello-world2.html
|--- vss-extension.json             // extension's manifest

'configuration.html' に以下の HTML を追加します。 基本的に、'VSS' への必須参照を追加します。SDK.min.js' ファイルとドロップダウンの 'select' 要素を使用して、プリセット リストからクエリを選択します。
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>                          
            <script src="sdk/scripts/VSS.SDK.min.js"></script>              
        </head>
        <body>
            <div class="container">
                <fieldset>
                    <label class="label">Query: </label>
                    <select id="query-path-dropdown" style="margin-top:10px">
                        <option value="" selected disabled hidden>Please select a query</option>
                        <option value="Shared Queries/Feedback">Shared Queries/Feedback</option>
                        <option value="Shared Queries/My Bugs">Shared Queries/My Bugs</option>
                        <option value="Shared Queries/My Tasks">Shared Queries/My Tasks</option>                        
                    </select>
                </fieldset>             
            </div>
        </body>
    </html>

手順 2: JavaScript - 構成

このガイドのパート 1 の 手順 3 でウィジェットに対して行ったように、JavaScript を使用してウィジェット構成のコンテンツをレンダリングします。 この JavaScript コードは、コンテンツをレンダリングし、VSS SDK を初期化し、ウィジェット構成のコードを構成名にマップし、構成設定をフレームワークに渡します。 ここでは、ウィジェット構成を読み込むコードを次に示します。 ファイル configuration.html と以下 <script> の要素を に <head>開きます。

    <script type="text/javascript">
        VSS.init({                        
            explicitNotifyLoaded: true,
            usePlatformStyles: true
        });

        VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
            VSS.register("HelloWorldWidget.Configuration", function () {   
                var $queryDropdown = $("#query-path-dropdown"); 

                return {
                    load: function (widgetSettings, widgetConfigurationContext) {
                        var settings = JSON.parse(widgetSettings.customSettings.data);
                        if (settings && settings.queryPath) {
                             $queryDropdown.val(settings.queryPath);
                         }

                        return WidgetHelpers.WidgetStatusHelper.Success();
                    },
                    onSave: function() {
                        var customSettings = {
                            data: JSON.stringify({
                                    queryPath: $queryDropdown.val()
                                })
                        };
                        return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings); 
                    }
                }
            });
            VSS.notifyLoadSucceeded();
        });
    </script>

VSS.initVSS.require、および は、パート 1 で説明されているように、ウィジェットで実行したのとVSS.register同じ役割を果たします。 唯一の違いは、ウィジェット構成の場合、 に VSS.register 渡される関数はコントラクトを満たすオブジェクトを IWidgetConfiguration 返す必要があるということです。

loadコントラクトの プロパティには、IWidgetConfigurationその値として 関数が必要です。 この関数には、ウィジェット構成をレンダリングするための一連の手順があります。 ここでは、ドロップダウン要素の選択した値を既存の設定があれば更新します。 この関数は、フレームワークが をインスタンス化するときに呼び出されます。 widget configuration

onSaveコントラクトの プロパティには、IWidgetConfigurationその値として 関数が必要です。 この関数は、ユーザーが構成ウィンドウで [保存] を選択すると、フレームワークによって呼び出されます。 ユーザー入力を保存する準備ができたら、それを文字列にシリアル化し、 オブジェクトを custom settings 形成し、 を使用 WidgetConfigurationSave.Valid() してユーザー入力を保存します。

このガイドでは、JSON を使用して、ユーザー入力を文字列にシリアル化します。 ユーザー入力を文字列にシリアル化するその他の方法を選択できます。 これは、オブジェクトの customSettings プロパティを使用してウィジェットから WidgetSettings アクセスできます。 ウィジェットでは、これを逆シリアル化する必要があります。これについては 、手順 4 で説明します。

手順 3: JavaScript - ライブ プレビューを有効にする

ユーザーがドロップダウンからクエリを選択したときにライブ プレビューの更新を有効にするには、ボタンに変更イベント ハンドラーをアタッチします。 このハンドラーは、構成が変更されたことをフレームワークに通知します。 また、プレビューの customSettings 更新に使用する を渡します。 フレームワークに通知するには、 の notify メソッドを widgetConfigurationContext 呼び出す必要があります。 2 つのパラメーターを受け取ります。この例 WidgetHelpers.WidgetEvent.ConfigurationChangeでは、 イベントの名前と EventArgs 、ヘルパー メソッドの助けを借りて から customSettings 作成された イベントの WidgetEvent.Args オブジェクトです。

プロパティに割り当てられた関数に以下を load 追加します。

 $queryDropdown.on("change", function () {
     var customSettings = {
        data: JSON.stringify({
                queryPath: $queryDropdown.val()
            })
     };
     var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
     var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
     widgetConfigurationContext.notify(eventName, eventArgs);
 });

[保存] ボタンを有効にできるように、構成変更のフレームワークに少なくとも 1 回通知する必要があります。

最後に、次 configuration.html のようになります。

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
        <head>                          
            <script src="sdk/scripts/VSS.SDK.min.js"></script>      
            <script type="text/javascript">
                VSS.init({                        
                    explicitNotifyLoaded: true,
                    usePlatformStyles: true
                });

                VSS.require("TFS/Dashboards/WidgetHelpers", function (WidgetHelpers) {
                    VSS.register("HelloWorldWidget.Configuration", function () {   
                        var $queryDropdown = $("#query-path-dropdown");

                        return {
                            load: function (widgetSettings, widgetConfigurationContext) {
                                var settings = JSON.parse(widgetSettings.customSettings.data);
                                if (settings && settings.queryPath) {
                                     $queryDropdown.val(settings.queryPath);
                                 }

                                 $queryDropdown.on("change", function () {
                                     var customSettings = {data: JSON.stringify({queryPath: $queryDropdown.val()})};
                                     var eventName = WidgetHelpers.WidgetEvent.ConfigurationChange;
                                     var eventArgs = WidgetHelpers.WidgetEvent.Args(customSettings);
                                     widgetConfigurationContext.notify(eventName, eventArgs);
                                 });

                                return WidgetHelpers.WidgetStatusHelper.Success();
                            },
                            onSave: function() {
                                var customSettings = {data: JSON.stringify({queryPath: $queryDropdown.val()})};
                                return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings); 
                            }
                        }
                    });
                    VSS.notifyLoadSucceeded();
                });
            </script>       
        </head>
        <body>
            <div class="container">
                <fieldset>
                    <label class="label">Query: </label>
                    <select id="query-path-dropdown" style="margin-top:10px">
                        <option value="" selected disabled hidden>Please select a query</option>
                        <option value="Shared Queries/Feedback">Shared Queries/Feedback</option>
                        <option value="Shared Queries/My Bugs">Shared Queries/My Bugs</option>
                        <option value="Shared Queries/My Tasks">Shared Queries/My Tasks</option>                        
                    </select>
                </fieldset>     
            </div>
        </body>
    </html>

手順 4: JavaScript - ウィジェットに再読み込みを実装する

ユーザーが選択したクエリ パスを格納するようにウィジェット構成を設定しました。 ここで、前の例のハードコーディング Shared Queries/Feedback ではなく、この格納された構成を使用するようにウィジェット内のコードを更新する必要があります。

ファイルhello-world3.htmlを開き、 を呼び出VSS.registerす行の から にHelloWorldWidget3ウィジェットHelloWorldWidget2の名前を更新します。 これにより、フレームワークは拡張機能内のウィジェットを一意に識別できます。

を介して HelloWorldWidget3VSS.register マップされた 関数は、現在、コントラクトを満たす オブジェクトを IWidget 返します。 ウィジェットには構成が必要になったため、コントラクトを満たすオブジェクトを返すには、この関数を更新する IConfigurableWidget 必要があります。 これを行うには、return ステートメントを更新して、次のように reload というプロパティを含めます。 このプロパティの値は、 メソッドをもう一度呼び出す getQueryInfo 関数です。 この再読み込みメソッドは、ユーザー入力がライブ プレビューを表示するように変更されるたびに、フレームワークによって呼び出されます。 これは、構成を保存するときにも呼び出されます。

return {
    load: function (widgetSettings) {
        // Set your title
        var $title = $('h2.title');
        $title.text('Hello World');

        return getQueryInfo(widgetSettings);
    },
    reload: function (widgetSettings) {
        return getQueryInfo(widgetSettings);
    }
}

'getQueryInfo' のハードコーディングされたクエリ パスは、構成されたクエリ パスに置き換える必要があります。これは、メソッドに渡されるパラメーター 'widget設定' から抽出できます。 'getQueryInfo' メソッドの先頭に以下を追加し、ハードコーディングされたクエリパスを 'settings.queryPath' に置き換えます。
var settings = JSON.parse(widgetSettings.customSettings.data);
if (!settings || !settings.queryPath) {
    var $container = $('#query-info-container');
    $container.empty();
    $container.text("Sorry nothing to show, please configure a query path.");

    return WidgetHelpers.WidgetStatusHelper.Success();
}

この時点で、ウィジェットは構成された設定でレンダリングする準備ができました。

loadプロパティと プロパティの両方にreload同様の関数があります。 これは、ほとんどの単純なウィジェットの場合です。 複雑なウィジェットの場合、構成の変更回数に関係なく、1 回だけ実行する必要がある特定の操作があります。 または、複数回実行する必要のない重い操作が存在する場合もあります。 このような操作は、 プロパティではなく、 プロパティに load 対応する関数の reload 一部になります。

手順 5: 拡張機能マニフェストの更新

ファイルを開き、 vss-extension.json プロパティの配列に 2 つの新しいエントリを contributions 含めます。 1 つはウィジェット用 HelloWorldWidget3 、もう 1 つはウィジェットの構成用です。 3 番目のウィジェットには、さらに別のプレビュー イメージが必要です。 この preview3.png 名前を付け、 フォルダーに img 配置します。 プロパティの配列を更新して、 files この例で追加した 2 つの新しい HTML ファイルを含めます。

{
    ...
    "contributions": [
        ... , 
        {
             "id": "HelloWorldWidget3",
             "type": "ms.vss-dashboards-web.widget",
             "targets": [
                 "ms.vss-dashboards-web.widget-catalog",
                 "fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration"
             ],
             "properties": {
                 "name": "Hello World Widget 3 (with config)",
                 "description": "My third widget",
                 "previewImageUrl": "img/preview3.png",                       
                 "uri": "hello-world3.html",
                 "supportedSizes": [
                      {
                             "rowSpan": 1,
                             "columnSpan": 2
                         }
                     ],
                 "supportedScopes": ["project_team"]
             }
         },
         {
             "id": "HelloWorldWidget.Configuration",
             "type": "ms.vss-dashboards-web.widget-configuration",
             "targets": [ "ms.vss-dashboards-web.widget-configuration" ],
             "properties": {
                 "name": "HelloWorldWidget Configuration",
                 "description": "Configures HelloWorldWidget",
                 "uri": "configuration.html"
             }
         }
    ],
    "files": [
            {
                "path": "hello-world.html", "addressable": true
            },
             {
                "path": "hello-world2.html", "addressable": true
            },
            {
                "path": "hello-world3.html", "addressable": true
            },
            {
                "path": "configuration.html", "addressable": true
            },
            {
                "path": "sdk/scripts", "addressable": true
            },
            {
                "path": "img", "addressable": true
            }
        ],
        ...     
}

ウィジェット構成のコントリビューションは、ウィジェット自体とは少し異なるモデルに従います。 ウィジェット構成のコントリビューション エントリには、次の項目があります。
  • 投稿を識別する ID 。 これは拡張機能内で一意である必要があります。
  • コントリビューションの 種類 。 すべてのウィジェット構成では、 ms.vss-dashboards-web.widget-configuration
  • コントリビューションが貢献している ターゲット の配列。 すべてのウィジェット構成に対して、1 つのエントリが含まれます。 ms.vss-dashboards-web.widget-configuration
  • 構成に使用される HTML ファイルの名前、説明、URI を含む一連のプロパティを含む プロパティ

構成をサポートするには、ウィジェットのコントリビューションも変更する必要があります。 ウィジェットのターゲットの配列を更新して、構成の ID を 形式<>>>publisherid for the extension<<id for the configuration contributionで含める必要があります。この場合は です。fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration

警告

構成可能なウィジェットのコントリビューション エントリが、前述のように適切な発行元と拡張機能の名前を使用して構成を対象としていない場合、ウィジェットの [構成] ボタンは表示されません。

このパートの最後には、マニフェスト ファイルに 3 つのウィジェットと 1 つの構成が含まれている必要があります。 サンプルから完全なマニフェストを取得できます。

手順 6: パッケージ化、発行、共有

拡張機能をまだ発行していない場合は、 このセクション を読んで拡張機能のパッケージ化、発行、共有を行います。 この時点より前に拡張機能を既に公開している場合は、拡張機能を再パッケージ化し、Marketplace に直接更新できます。

手順 7: カタログからウィジェットを追加する

次に、 のチーム ダッシュボードに移動します。 https://dev.azure.com/{yourOrganization}/{yourProject}. このページが既に開いている場合は、更新します。 右下の [編集] ボタンをポイントし、[追加] ボタンを選択します。 これにより、インストールしたウィジェットが見つかるウィジェット カタログが開きます。 ウィジェットを選択し、[追加] ボタンを選択してダッシュボードに追加します。

ウィジェットの構成を求めるメッセージが表示されます。

Overview dashboard with a sample widget from the catalog.

ウィジェットを構成するには、2 つの方法があります。 1 つは、ウィジェットにカーソルを合わせ、右上隅に表示される省略記号を選択し、[構成] を選択することです。 もう 1 つは、ダッシュボードの右下にある [編集] ボタンを選択し、ウィジェットの右上隅に表示される構成ボタンを選択することです。 どちらかが右側に構成エクスペリエンスを開き、中央にウィジェットのプレビューを開きます。 先に進み、ドロップダウンからクエリを選択します。 ライブ プレビューには、更新された結果が表示されます。 [保存] を選択すると、ウィジェットに更新された結果が表示されます。

手順 8: 詳細を構成する (省略可能)

に必要な数の HTML フォーム要素を追加して、追加の構成を configuration.html 行うことができます。 すぐに使用できる構成可能な機能は、ウィジェット名とウィジェット サイズの 2 つあります。

既定では、拡張機能マニフェストでウィジェットに指定した名前は、ダッシュボードに追加されるウィジェットのすべてのインスタンスのウィジェット名として格納されます。 ユーザーがウィジェットのインスタンスに任意の名前を追加できるように、これを構成できます。 このような構成を許可するには、拡張機能マニフェストのウィジェットの [プロパティ] セクションに を追加 isNameConfigurable:true します。

拡張機能マニフェストの配列に supportedSizes ウィジェットに複数のエントリを指定した場合、ユーザーはウィジェットのサイズも構成できます。

このガイドの 3 番目のサンプルの拡張機能マニフェストは、ウィジェットの名前とサイズの構成を有効にした場合、次のようになります。

{
    ...
    "contributions": [
        ... , 
        {
             "id": "HelloWorldWidget3",
             "type": "ms.vss-dashboards-web.widget",
             "targets": [
                 "ms.vss-dashboards-web.widget-catalog",  "fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration"
             ],
             "properties": {
                 "name": "Hello World Widget 3 (with config)",
                 "description": "My third widget",
                 "previewImageUrl": "img/preview3.png",                       
                 "uri": "hello-world3.html",
                 "isNameConfigurable": true,
                 "supportedSizes": [
                    {
                        "rowSpan": 1,
                        "columnSpan": 2
                    },
                    {
                        "rowSpan": 2,
                        "columnSpan": 2
                    }
                 ],
                 "supportedScopes": ["project_team"]
             }
         },
         ...
}

前の変更により、拡張機能を 再パッケージ化 して 更新 します。 このウィジェットを含むダッシュボードを更新します (Hello Worldウィジェット 3 (構成あり) )。 ウィジェットの構成モードを開くと、ウィジェットの名前とサイズを変更するオプションが表示されます。

Widget where name and size can be configured

先に進み、ドロップダウンから別のサイズを選択します。 ライブ プレビューのサイズが変更されます。 変更を保存すると、ダッシュボードのウィジェットのサイズも変更されます。

警告

既にサポートされているサイズを削除すると、ウィジェットは正しく読み込めませんでした。 今後のリリースの修正プログラムに取り組んでいます。

ウィジェットの名前を変更しても、ウィジェットに表示される変更はありません。 これは、サンプル ウィジェットにウィジェット名がどこにも表示されないためです。 ハードコーディングされたテキスト "Hello World" ではなく、ウィジェット名を表示するようにサンプル コードを変更してみましょう。

これを行うには、ハードコーディングされたテキスト "Hello World" をwidgetSettings.name、 要素のテキストを設定する行で にh2置き換えます。 これにより、ページ更新時にウィジェットが読み込まれるたびにウィジェット名が表示されるようになります。 構成が変更されるたびにライブ プレビューを更新する必要があるため、コードの部分にも同じコードを reload 追加する必要があります。 の最後の hello-world3.html return ステートメントは次のとおりです。

return {
    load: function (widgetSettings) {
        // Set your title
        var $title = $('h2.title');
        $title.text(widgetSettings.name);

        return getQueryInfo(widgetSettings);
    },
    reload: function (widgetSettings) {
        // Set your title
        var $title = $('h2.title');
        $title.text(widgetSettings.name);

        return getQueryInfo(widgetSettings);
    }
}

拡張機能をもう一度再パッケージ化 して 更新 します。 このウィジェットを含むダッシュボードを更新します。 ウィジェット名に変更が加えられた場合は、構成モードでウィジェットのタイトルを更新します。