新增儀錶板小工具

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

儀錶板上的小工具會在延伸模組架構實作為貢獻。 單一延伸模組可以有多個貢獻。 瞭解如何建立具有多個小工具作為貢獻的延伸模組。

本文分為三個部分,每個部分都是在上一個 - 從簡單的小工具開始,並以完整的小工具結尾。

提示

請參閱使用 Azure DevOps 擴充功能 SDK 進行擴充功能開發的最新檔。

本教學課程的準備和必要設定

若要建立 Azure DevOps 或 TFS 的擴充功能,您需要一些必要軟體和工具:

知識: 小工具開發需要 JavaScript、HTML、CSS 的一些知識。

  • Azure DevOps 中用來安裝和測試小工具的組織,如需詳細資訊,請參閱這裡
  • 文字編輯器。 針對許多教學課程,我們使用了 Visual Studio Code,您可以在這裡下載
  • 最新版的節點,可在這裡下載
  • 適用於 Azure DevOps 的跨平臺 CLI (tfx-cli) 可封裝您的延伸模組。
    • 您可以藉由執行 ,使用 npm來安裝 tfx-cli,這是 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. 二個部分是以第一個部分 為基礎,方法是新增對 Azure DevOps REST API 的呼叫。
  3. 三個部分 說明如何將組態新增至您的小工具。

注意

如果您急著想立即掌握程序代碼,您可以在這裡下載範例。 下載之後,請移至 widgets 資料夾,然後直接遵循 步驟 6步驟 7 來發布具有不同複雜度之三個範例小工具的範例延伸模組。

開始使用我們為您提供的一些 小工具基本樣式,以及小工具 結構的一些指引。

第 1 部分:Hello World

此部分提供使用 JavaScript 列印 「Hello World」 的小工具。

Overview dashboard with a sample widget

步驟 1:取得用戶端 SDK - VSS.SDK.min.js

核心 SDK 腳本 VSS.SDK.min.js可讓 Web 延伸模組與主機 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。 在中 hello-world.html新增下列 HTML。 我們會新增檔案的強制參考, 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 在 Widget 中轉譯內容。 在本文中,我們會將所有 JavaScript 程式代碼包裝在 &lt;script&gt; HTML 檔案中的 元素內。 您可以選擇將這個程式代碼放在個別的 JavaScript 檔案中,並在 HTML 檔案中加以參考。 程式代碼會轉譯內容。 此 JavaScript 程式代碼也會初始化 VSS SDK、將小工具的程式代碼對應至小工具名稱,並通知小工具成功或失敗的擴充架構。 在我們的案例中,以下是會在小工具中列印 「Hello World」 的程式代碼。 在 HTML 的 中head新增這個專案script

    <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 ,讓小工具可以在載入完成時明確通知主機。 此控件可讓我們在確保載入相依模組之後通知載入完成。 我們傳遞 usePlatformStyles: true ,讓 Widget 可以使用 html 元素的 Azure DevOps 核心樣式(例如 body、div 等等)。 如果小工具偏好不要使用這些樣式,則可以傳入 usePlatformStyles: false

VSS.require 是用來載入必要的 VSS 腳本連結庫。 對此方法的呼叫會自動載入一般連結庫,例如 JQueryJQueryUI。 在我們的案例中,我們相依於 WidgetHelpers 連結庫,用來將小工具狀態傳達給小工具架構。 因此,我們會將對應的模組名稱和 TFS/Dashboards/WidgetHelpersVSS.require呼傳遞給 。 載入模組之後,就會呼叫回呼。 回呼具有小工具所需的其餘 JavaScript 程式代碼。 在回呼結束時,我們會呼叫 VSS.notifyLoadSucceeded 來通知載入完成。

WidgetHelpers.IncludeWidgetStyles 包含具有一些 基本 css 的樣式表單,可讓您開始使用。 請務必使用 類別 widget 將內容包裝在 HTML 元素內,以使用這些樣式。

VSS.register 用來對應 JavaScript 中的函式,此函式可唯一識別延伸模組中不同貢獻之間的小工具。 名稱應該符合id識別您參與的 ,如步驟 5 中所述。 對於 Widget,傳遞給 VSS.register 的函式應該會傳回符合 IWidget 合約的物件,例如,傳回的對象應該有 load 屬性,其值是另一個具有核心邏輯來轉譯小工具的函式。 在我們的案例中,它會將元素的 h2 文字更新為 「Hello World」。。 當 Widget 架構具現化您的小工具時,就會呼叫此函式。 我們會使用 WidgetStatusHelper WidgetHelpers 中的 傳回 WidgetStatus 作為成功。

警告

如果用來註冊小工具的名稱不符合指令清單中參與專案的標識碼,則小工具會意外運作。

vss-extension.json應一律位於資料夾的根目錄 (本指南中為 HelloWorld)。 對於所有其他檔案,您可以將它們放在資料夾內您想要的任何結構中,只要確定在 HTML 檔案和 vss-extension.json 指令清單中適當地更新參考即可。

步驟 4:延伸模組的標誌: logo.png

您的標誌會顯示在 Marketplace 中,一旦使用者安裝您的延伸模組,就會顯示在 Widget 目錄中。

您需要 98 px x 98-px 目錄圖示。 選擇影像,將它 logo.png命名為 ,並將它 img 放在資料夾中。

若要支援 TFS 2015 Update 3,您需要 330 像素 x 160 像素的額外映像。 此預覽影像會顯示在此目錄中。 選擇影像,將它 preview.png命名為 ,並將它放在 img 資料夾中,如同之前一樣。

不過,只要下一個步驟中的延伸模組指令清單以您所使用的名稱更新,您就可以將這些映像命名為 。

步驟 5:延伸模組的指令清單: vss-extension.json

使用下列內容在home目錄中建立 json 檔案 (vss-extension.json例如) :

    {
        "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
            }
        ]
    }

如需必要屬性的詳細資訊,請參閱 延伸模組指令清單參考

注意

此處的 發行者 必須變更為您的發行者名稱。 若要立即建立發行者,請流覽 套件/發佈/安裝

圖示

圖示存根會指定指令清單中延伸模組圖示的路徑。

投稿文章

每個參與專案都會 定義屬性

  • 識別您參與的識別碼。 此標識碼在延伸模組內應該是唯一的。 此標識碼應該與您在步驟 3用來註冊小工具的名稱相符。
  • 參與 的類型 。 針對所有小工具,類型應該是 ms.vss-dashboards-web.widget
  • 參與所參與的目標陣列。 針對所有小工具,目標應該是 [ms.vss-dashboards-web.widget-catalog]
  • 屬性是包含參與類型屬性的物件。 對於小工具,下列屬性是必要屬性。
屬性 描述
NAME 小工具目錄中要顯示的小工具名稱。
description 小工具目錄中要顯示之小工具的描述。
catalogIconUrl 您在步驟 4新增的目錄圖示相對路徑,以顯示在小工具目錄中。 影像應該是 98 像素 x 98 像素。 如果您已使用不同的資料夾結構或不同的檔名,請在這裡指定適當的相對路徑。
previewImageUrl 您在步驟 4新增的預覽影像相對路徑,只顯示於 TFS 2015 Update 3 的 Widget 目錄中。 影像應該是 330 像素 x 160 像素。 如果您已使用不同的資料夾結構或不同的檔名,請在這裡指定適當的相對路徑。
uri 您在步驟 1新增之 HTML 檔案的相對路徑。 如果您已使用不同的資料夾結構或不同的檔名,請在這裡指定適當的相對路徑。
supportedSizes 小工具支援的大小陣列。 當小工具支援多個大小時,陣列中的第一個大小是小工具的預設大小。 widget size針對儀錶板方格中小工具所佔用的數據列和資料列指定 。 一個數據列/數據行對應至160圖元。 任何高於 1x1 的維度都會取得額外的 10 像素,代表小工具之間的排水溝。 例如,3x2 小工具寬 160*3+10*2160*2+10*1 高。 支援的大小上限為 4x4
supportedScopes 目前,我們僅支援小組儀錶板。 值必須是 project_team。 未來,當我們支援其他儀錶板範圍時,將會有更多選項可供選擇。

Files

這些 檔案 會說明您想要包含在套件中的檔案 - 您的 HTML 頁面、腳本、SDK 腳本和標誌。 true除非您包含不需要 URL 尋址的其他檔案,否則請設定addressable為 。

注意

如需延伸模組指令清單檔案的詳細資訊,例如其屬性及其用途,請參閱延伸模組指令清單參考

步驟 6:封裝、發佈和共用

一旦您撰寫延伸模塊之後,將它放入 Marketplace 的下一個步驟是將所有檔案封裝在一起。 所有延伸模組都會封裝為 VSIX 2.0 相容的 .vsix 檔案 - Microsoft 提供跨平臺命令行介面 (CLI) 來封裝您的延伸模組。

取得封裝工具

您可以從命令行使用 Node.js元件,安裝或更新適用於 Azure DevOps 的跨平臺 CLI(tfx-clinpm)。

npm i -g tfx-cli

封裝您的延伸模組

一旦您有 tfx-cli,將擴展名封裝到 .vsix 檔案是無費力的。 移至延伸模組的主目錄,然後執行下列命令。

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

注意

擴充功能/整合的版本必須在每次更新時遞增。
更新現有的擴充功能時,請更新指令清單中的版本,或傳遞 --rev-version 命令行參數。 這會遞增 擴充功能的修補程式 版本號碼,並將新版本儲存至指令清單。

在 .vsix 檔案中封裝擴展名之後,您就可以將擴充功能發佈至 Marketplace。

建立延伸模組的發行者

所有延伸模組,包括來自 Microsoft 的擴充功能,都會識別為發行者提供。 如果您還不是現有發行者的成員,您將建立一個。

  1. 登入 Visual Studio Marketplace 發佈入口網站
  2. 如果您還不是現有發行者的成員,系統會提示您建立發行者。 如果未提示您建立發行者,請向下卷動至頁面底部,然後選取 [相關網站] 下方的 [發佈延伸模組]。
    • 指定發行者的識別碼,例如: mycompany-myteam
      • 標識碼會作為擴充功能指令清單檔中 屬性的值 publisher
    • 指定發行者的顯示名稱,例如: My Team
  3. 檢閱 Marketplace 發行者合約 ,然後選取 [ 建立]

現在已定義您的發行者。 在未來版本中,您可以授與許可權,以檢視和管理發行者的延伸模組。 小組和組織在一般發行者下發佈延伸模組是容易且更安全的,但不需要在一組用戶之間共用一組認證。

vss-extension.json更新範例中的指令清單檔案,以將虛擬發行者標識碼fabrikam取代為您的發行者標識符。

發佈和共用延伸模組

建立發行者之後,您現在可以將延伸模塊上傳至 Marketplace。

  1. 尋找 [ 上傳新的擴展名] 按鈕、流覽至已封裝的 .vsix 檔案,然後選取 [上傳]。

您也可以透過命令行上傳延伸模組,方法是使用 tfx extension publish 命令,而不是 tfx extension create 在一個步驟中封裝和發佈延伸模組。 您可以選擇性地在發佈之後,使用 --share-with 來與一或多個帳戶共用延伸模組。 您也需要個人存取令牌。

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

Widget 可以呼叫 Azure DevOps 中的任何 REST API ,以與 Azure DevOps 資源互動。 在此範例中,我們使用 REST API for WorkItemTracking 來擷取現有查詢的相關信息,並在 「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' 元素,以保存查詢資訊。 將小工具的名稱從 'HelloWorldWidget' 更新為 'HelloWorldWidget2',在您呼叫 'VSS.register' 的行中。 這可讓架構唯一識別延伸模組內的小工具。
<!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 呼叫

有許多客戶端連結庫可透過 SDK 存取,以在 Azure DevOps 中呼叫 REST API。 這些連結庫稱為 REST 用戶端,而且是所有可用伺服器端端點的 Ajax 呼叫周圍的 JavaScript 包裝函式。 您可以使用這些用戶端所提供的方法,而不是自行撰寫 Ajax 呼叫。 這些方法會將 API 回應對應至程式代碼可取用的物件。

在此步驟中,我們會更新 VSS.require 載入 TFS/WorkItemTracking/RestClient的呼叫,以提供 WorkItemTracking REST 用戶端。 我們可以使用此 REST 用戶端來取得資料夾Shared Queries下所呼叫Feedback之查詢的相關信息。

在傳遞給 VSS.register的函式內,我們會建立變數來保存目前的專案標識碼。 我們需要這個變數來擷取查詢。 我們也會建立新的方法來 getQueryInfo 使用 REST 用戶端。 這個方法接著會從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。 它可讓您向 Widget 架構指出發生錯誤,並利用提供給所有小工具的標準錯誤體驗。

如果您沒有 Feedback 資料夾下的 Shared Queries 查詢,請將 程式代碼中的 取代 Shared Queries\Feedback 為專案中存在的查詢路徑。

步驟 4:顯示回應

最後一個步驟是在 Widget 內轉譯查詢資訊。 函 getQuery 式會傳回 promise 內類型 Contracts.QueryHierarchyItem 的物件。 在此範例中,我們會在 「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:擴充指令清單 更新

在此步驟中,我們會更新延伸模組指令清單,以包含第二個 Widget 的專案。 將新的貢獻新增至 屬性中的 contributions 陣列,並將新檔案 hello-world2.html 新增至 files 屬性中的陣列。 您需要第二個小工具的另一個預覽影像。 將它命名為 , 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:從類別目錄新增 Widget

現在,請移至 您的小組儀錶板。 https:\//dev.azure.com/{yourOrganization}/{yourProject}。 如果此頁面已經開啟,請重新整理。 將滑鼠停留在右下方的 [編輯] 按鈕上,然後選取 [新增] 按鈕。 小工具目錄隨即開啟,您可以在其中找到您安裝的 Widget。 選擇您的小工具,然後選取 [新增] 按鈕,將其新增至儀錶板。

第3部分:使用組態的 Hello World

本指南的第 2 部分中,您已瞭解如何建立小工具,以顯示硬式編碼查詢的查詢資訊。 在此部分中,我們會新增將查詢設定為使用的功能,而不是硬式編碼的查詢。 在設定模式中時,用戶會根據其變更查看小工具的即時預覽。 當使用者選取 [儲存] 時,這些變更會儲存至儀錶板上的小工具。

Overview dashboard live preview of the widget based on changes.

步驟 1:HTML

Widget 和 Widget 組態的實作非常類似。 這兩者都是在延伸模組架構中實作為貢獻。 兩者都使用相同的 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 - 設定

使用 JavaScript 在小工具組態中轉譯內容,就像本指南第 1 部分的步驟 3小工具所做的一樣。 此 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.requireVSS.register 會扮演與針對小工具所扮演的角色相同,如第 1 部分所述。 唯一的差別在於,針對小工具組態,傳遞給 VSS.register 的函式應該會傳回符合 IWidgetConfiguration 合約的物件。

合約 loadIWidgetConfiguration 屬性應該有函式做為其值。 此函式具有一組轉譯小工具組態的步驟。 在我們的案例中,如果有的話,請使用現有設定來更新下拉式清單元素的選取值。 當架構具現化您的時,會呼叫此函式 widget configuration

合約 onSaveIWidgetConfiguration 屬性應該有函式做為其值。 當使用者在組態窗格中選取 [儲存 ] 時,架構會呼叫此函式。 如果使用者輸入已準備好儲存,請將它串行化為字串,形成 custom settings 物件,並使用 WidgetConfigurationSave.Valid() 來儲存使用者輸入。

在本指南中,我們使用 JSON 將使用者輸入串行化為字串。 您可以選擇任何其他方式,將使用者輸入串行化為字串。 它可透過物件的自訂 設定 屬性WidgetSettings存取小工具。 小工具必須還原串行化此步驟,步驟 4涵蓋此專案。

步驟 3:JavaScript - 啟用即時預覽

若要在使用者從下拉式清單中選取查詢時啟用即時預覽更新,我們會將變更事件處理程式附加至按鈕。 此處理程式會通知架構組態已變更。 它也會傳遞 customSettings 要用於更新預覽的 。 若要通知架構, notify 必須呼叫 上的 widgetConfigurationContext 方法。 它會採用兩個EventArgs參數:事件的名稱,在此案例中為 WidgetHelpers.WidgetEvent.ConfigurationChange,以及使用 Helper 方法協助WidgetEvent.ArgscustomSettings 建立之 事件的物件。

在指派給 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);
 });

您必須至少通知組態架構變更一次,才能啟用 [儲存] 按鈕。

最後,您 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 - 在 Widget 中實作重載

我們已設定小工具組態,以儲存使用者所選取的查詢路徑。 我們現在必須更新小工具中的程序代碼,以使用此預存組態,而不是先前範例中的硬式編碼 Shared Queries/Feedback

開啟 檔案hello-world3.html,並將小工具的名稱從 HelloWorldWidget2 更新為 HelloWorldWidget3 ,以在呼叫 的行中。VSS.register 這可讓架構唯一識別延伸模組內的小工具。

透過 VSS.register 對應至 HelloWorldWidget3 的函式目前會傳回滿足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();
}

此時,您的小工具已準備好使用已設定的設定進行轉譯。

loadreload 屬性都有類似的函式。 這是大多數簡單小工具的情況。 對於複雜的小工具,不論組態變更多少次,您都會想要執行一次特定的作業。 或者,可能有一些繁重的作業不需要多次執行。 這類作業會是對應至 屬性之函式的 load 一部分,而不是 reload 屬性。

步驟 5:擴充指令清單 更新

開啟 檔案 vss-extension.json 以在 屬性中包含數位的 contributions 兩個新專案。 一個用於 HelloWorldWidget3 小工具,另一個用於其組態。 您需要第三個小工具的另一個預覽影像。 將它命名為 , preview3.png 並將它放在 img 資料夾中。 更新 屬性中的 files 陣列,以包含我們在此範例中新增的兩個新的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
            }
        ],
        ...     
}

請注意,小工具設定的貢獻會遵循與小工具本身稍有不同的模型。 小工具設定的貢獻專案具有:
  • 識別您參與的識別碼。 這在延伸模組內應該是唯一的。
  • 參與 的類型 。 針對所有小工具組態,這應該是 ms.vss-dashboards-web.widget-configuration
  • 參與所參與的目標陣列。 針對所有小工具組態,這具有單一專案: ms.vss-dashboards-web.widget-configuration
  • 包含一組屬性的屬性,其中包含用於組態之 HTML 檔案的名稱、描述和 URI。

若要支持設定,小工具貢獻也必須變更。 小工具的目標陣列必須更新,才能在表單>publisherid for the extension<><中包含組態的標識碼。id for the configuration contribution><在此情況下為 。fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration

警告

如果可設定小工具的貢獻專案未以正確的發行者和延伸模塊名稱作為設定的目標,如先前所述,則小工具不會顯示 [設定] 按鈕。

在此部分結束時,指令清單檔應該包含三個 Widget 和一個組態。 您可以從這裡從範例取得完整的指令清單。

步驟 6:封裝、發佈和共用

如果您尚未發佈延伸模組,請閱讀 本節 以封裝、發佈和共用您的延伸模組。 如果您已在此點之前發佈延伸模組,您可以重新封裝延伸模組,並將它直接更新至 Marketplace。

步驟 7:從類別目錄新增 Widget

現在,請移至您的小組儀錶板 https://dev.azure.com/{yourOrganization}/{yourProject}. 如果此頁面已開啟,請重新整理。 將滑鼠停留在右下方的 [編輯] 按鈕上,然後選取 [新增] 按鈕。 這應該會開啟小工具目錄,您可以在其中找到您安裝的 Widget。 選擇您的小工具,然後選取 [新增] 按鈕,將其新增至儀錶板。

您會看到一則訊息,要求您設定小工具。

Overview dashboard with a sample widget from the catalog.

有兩種方式可以設定小工具。 其中一個是將滑鼠停留在 Widget 上,選取出現在右上角的省略號,然後選取 [設定]。 另一個是選取儀錶板右下角的 [編輯] 按鈕,然後選取小工具右上角顯示的 [設定] 按鈕。 在右側開啟組態體驗,並在中央預覽您的小工具。 繼續從下拉式清單中選擇查詢。 即時預覽會顯示更新的結果。 選取 [儲存],您的小工具會顯示更新的結果。

步驟 8:設定更多 (選擇性)

您可以視需要在 中 configuration.html 新增多個 HTML 窗體專案,以進行其他設定。 有兩個現用的可設定功能:小工具名稱和小工具大小。

根據預設,您在延伸模組指令清單中為小工具提供的名稱會儲存為小工具的每個實例的小工具名稱,這些實例會新增至儀錶板。 您可以允許使用者進行此設定,讓他們可以將想要的任何名稱新增至小工具實例。 若要允許這類設定,請在延伸模組指令清單中的小工具的 [屬性] 區段中新增 isNameConfigurable:true

如果您在延伸模組指令清單的數位中 supportedSizes 為小工具提供多個專案,則使用者也可以設定小工具的大小。

如果啟用小工具名稱和大小設定,本指南中第三個範例的擴充指令清單看起來會像下面這樣:

{
    ...
    "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 Widget 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);
    }
}

重新封裝再次更新 您的延伸模組。 重新整理具有此小工具的儀錶板。 在設定模式中,小工具名稱的任何變更現在更新小工具標題。