Dela via


Lägga till en instrumentpanelswidget

Azure DevOps Services | Azure DevOps Server 2022 – Azure DevOps Server 2019

Widgetar på en instrumentpanel implementeras som bidrag i tilläggsramverket. Ett enda tillägg kan ha flera bidrag. Lär dig hur du skapar ett tillägg med flera widgetar som bidrag.

Den här artikeln är uppdelad i tre delar, var och en bygger på föregående - börjar med en enkel widget och slutar med en omfattande widget.

Dricks

Läs vår senaste dokumentation om tilläggsutveckling med hjälp av Azure DevOps Extension SDK.

Förutsättningar

  • Kunskap: Viss kunskap om JavaScript, HTML, CSS krävs för widgetutveckling.
  • En organisation i Azure DevOps.
  • En textredigerare. I många av självstudierna använder vi Visual Studio Code.
  • Den senaste versionen av noden.
  • Plattformsoberoende CLI för Azure DevOps (tfx-cli) för att paketera dina tillägg.
    • tfx-cli kan installeras med , npmen komponent i Node.js genom att köra npm i -g tfx-cli
  • En hemkatalog för projektet. Den här katalogen kallas för home hela självstudien.

Filstruktur för tillägg:

|--- 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

I den här självstudien

  1. Del 1: Visar hur du skapar en ny widget som skriver ut ett enkelt "Hello World"-meddelande.
  2. Del 2: Bygger på den första delen genom att lägga till ett anrop till ett Azure DevOps REST API.
  3. Del 3: Förklarar hur du lägger till konfiguration i widgeten.

Kommentar

Om du har bråttom och vill lägga händerna på koden direkt kan du ladda ned exemplen. När du har laddat ned går du till widgets mappen och följer sedan steg 6 och steg 7 direkt för att publicera exempeltillägget som har de tre exempelwidgetarna med varierande komplexitet.

Kom igång med några grundläggande format för widgetar som vi tillhandahåller out-of-the-box och lite vägledning om widgetstruktur.

Del 1: Hello World

Del 1 visar en widget som skriver ut "Hello World" med JavaScript.

Skärmbild av översiktsinstrumentpanelen med en exempelwidget.

Steg 1: Hämta klient-SDK: VSS.SDK.min.js

SDK-kärnskriptet VSS.SDK.min.jsgör det möjligt för webbtillägg att kommunicera med värdens Azure DevOps-ram. Skriptet utför åtgärder som att initiera, meddela att tillägget läses in eller få kontext om den aktuella sidan. Hämta Client SDK-filen VSS.SDK.min.js och lägg till den i webbappen. Placera den i home/sdk/scripts mappen.

Om du vill hämta SDK:n använder du kommandot "npm install":

npm install vss-web-extension-sdk

Mer information finns på GitHub-sidan för Klient-SDK.

Steg 2: Konfigurera HTML-sidan – hello-world.html

HTML-sidan är limmet som håller ihop layouten och innehåller referenser till CSS och JavaScript. Du kan ge filen namnet vad som helst. Uppdatera alla referenser till hello-world med det namn du använder.

Widgeten är HTML-baserad och finns i en iframe. Lägg till följande HTML i hello-world.html. Vi lägger till den obligatoriska referensen till VSS.SDK.min.js filen och inkluderar ett h2 element som uppdateras med strängen Hello World i det kommande steget.

<!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>

Även om vi använder en HTML-fil ignoreras de flesta andra HTML-huvudelement än skript och länkar av ramverket.

Steg 3: Uppdatera JavaScript

Vi använder JavaScript för att återge innehåll i widgeten. I den här artikeln omsluter vi all JavaScript-kod i ett &lt;script&gt; element i HTML-filen. Du kan välja att ha den här koden i en separat JavaScript-fil och referera till den i HTML-filen. Koden renderar innehållet. Den här JavaScript-koden initierar även VSS SDK, mappar koden för widgeten till ditt widgetnamn och meddelar tilläggsramverket för widgetframgångar eller -fel. I vårt fall skriver följande kod ut "Hello World" i widgeten. Lägg till det här script elementet head i HTML-koden.

<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 initierar handskakningen mellan den iframe som är värd för widgeten och värdramen.
  • Vi skickar explicitNotifyLoaded: true så att widgeten uttryckligen kan meddela värden när den är klar med inläsningen. Med den här kontrollen kan vi meddela att belastningen har slutförts efter att de beroende modulerna har lästs in. Vi skickar usePlatformStyles: true så att widgeten kan använda Azure DevOps-kärnformat för HTML-element (till exempel brödtext, div och så vidare). Om widgeten föredrar att inte använda dessa formatmallar kan de skicka in usePlatformStyles: false.
  • VSS.require används för att läsa in de vss-skriptbibliotek som krävs. Ett anrop till den här metoden läser automatiskt in allmänna bibliotek som JQuery och JQueryUI. I vårt fall är vi beroende av WidgetHelpers-biblioteket, som används för att kommunicera widgetstatus till widgetramverket. Därför skickar vi motsvarande modulnamn TFS/Dashboards/WidgetHelpers och ett återanrop till VSS.require. Återanropet anropas när modulen har lästs in. Återanropet har resten av JavaScript-koden som behövs för widgeten. I slutet av återanropet anropar VSS.notifyLoadSucceeded vi för att meddela att belastningen har slutförts.
  • WidgetHelpers.IncludeWidgetStyles innehåller en formatmall med några grundläggande css för att komma igång. Om du vill använda dessa formatmallar omsluter du innehållet i ett HTML-element med klassen widget.
  • VSS.register används för att mappa en funktion i JavaScript, som unikt identifierar widgeten bland de olika bidragen i tillägget. Namnet ska matcha id det som identifierar ditt bidrag enligt beskrivningen i steg 5. För widgetar ska funktionen som skickas till VSS.register returnera ett objekt som uppfyller IWidget kontraktet, till exempel att det returnerade objektet ska ha en inläsningsegenskap vars värde är en annan funktion som har kärnlogik för att återge widgeten. I vårt fall är det att uppdatera texten i elementet h2 till "Hello World". Det är den här funktionen som anropas när widgetramverket instansierar din widget. Vi använder WidgetStatusHelper från WidgetHelpers för att returnera WidgetStatus som framgång.

Varning

Om namnet som används för att registrera widgeten inte matchar ID:t för bidraget i manifestet fungerar widgeten oväntat.

  • vss-extension.jsonbör alltid finnas i mappens rot (i den här guiden). HelloWorld För alla andra filer kan du placera dem i vilken struktur du vill i mappen. Se bara till att uppdatera referenserna på rätt sätt i HTML-filerna och i manifestet vss-extension.json .

Steg 4: Uppdatera tilläggslogotypen: logo.png

Logotypen visas på Marketplace och i widgetkatalogen när en användare installerar tillägget.

Du behöver en katalogikon på 98 x 98 px. Välj en bild, ge den logo.pngnamnet och placera den img i mappen.

Du kan namnge dessa bilder, men du vill så länge tilläggsmanifestet i nästa steg uppdateras med de namn som du använder.

Steg 5: Skapa tilläggsmanifestet: vss-extension.json

Varje tillägg måste ha en tilläggsmanifestfil.

{
    "manifestVersion": 1,
    "id": "azure-devops-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
        }
    ]
}

Mer information om obligatoriska attribut finns i tilläggsmanifestreferensen.

Kommentar

Ändra utgivaren till ditt utgivarnamn. Information om hur du skapar en utgivare finns i Paket/Publicera/Installera.

Ikoner

Ikonernas strof anger sökvägen till tilläggets ikon i manifestet.

Bidrag

Varje bidragspost definierar egenskaper.

  • ID :t för att identifiera ditt bidrag. Det här ID:t bör vara unikt i ett tillägg. Det här ID:t ska matcha det namn som du använde i steg 3 för att registrera widgeten.
  • Typ av bidrag. För alla widgetar ska typen vara ms.vss-dashboards-web.widget.
  • Matrisen med mål som bidraget bidrar till. För alla widgetar ska målet vara [ms.vss-dashboards-web.widget-catalog].
  • Egenskaperna är objekt som innehåller egenskaper för bidragstypen. För widgetar är följande egenskaper obligatoriska.
Property Beskrivning
name Namnet på widgeten som ska visas i widgetkatalogen.
description Beskrivning av widgeten som ska visas i widgetkatalogen.
catalogIconUrl Relativ sökväg till katalogikonen som du lade till i steg 4 för att visa i widgetkatalogen. Bilden ska vara 98 px x 98 px. Om du har använt en annan mappstruktur eller ett annat filnamn anger du lämplig relativ sökväg här.
previewImageUrl Relativ sökväg för den förhandsgranskningsbild som du lade till i steg 4 för att visa i widgetkatalogen. Bilden ska vara 330 px x 160 px. Om du har använt en annan mappstruktur eller ett annat filnamn anger du lämplig relativ sökväg här.
uri Relativ sökväg för HTML-filen som du lade till i steg 1. Om du har använt en annan mappstruktur eller ett annat filnamn anger du lämplig relativ sökväg här.
supportedSizes Matris med storlekar som stöds av widgeten. När en widget stöder flera storlekar är den första storleken i matrisen standardstorleken för widgeten. widget size anges för de rader och kolumner som används av widgeten i instrumentpanelens rutnät. En rad/kolumn motsvarar 160 px. Alla dimensioner som är större än 1 x 1 får 10 px extra som representerar rännstenen mellan widgetar. Till exempel är 160*3+10*2 en 3x2-widget bred och 160*2+10*1 lång. Den maximala storleken som stöds är 4x4.
supportedScopes För närvarande stöds endast teaminstrumentpaneler. Värdet måste vara project_team. Framtida uppdateringar kan innehålla fler alternativ för instrumentpanelsomfång.

Filer

I filens strof står de filer som du vill inkludera i paketet – HTML-sidan, skripten, SDK-skriptet och logotypen. Ange addressable till true såvida du inte inkluderar andra filer som inte behöver vara URL-adresserbara.

Kommentar

Mer information om tilläggsmanifestfilen, till exempel dess egenskaper och vad de gör, finns i referensen för tilläggsmanifestet.

Steg 6: Paketera, publicera och dela

När du har ditt skriftliga tillägg är nästa steg mot att få det till Marketplace att paketera alla dina filer tillsammans. Alla tillägg paketeras som VSIX 2.0-kompatibla .vsix-filer – Microsoft tillhandahåller ett plattformsoberoende kommandoradsgränssnitt (CLI) för att paketera tillägget.

Hämta förpackningsverktyget

Du kan installera eller uppdatera plattformsoberoende CLI för Azure DevOps (tfx-cli) med hjälp npmav , en komponent i Node.js, från kommandoraden.

npm i -g tfx-cli

Paketera tillägget

Det går inte att paketera tillägget i en .vsix-fil när du har tfx-cli. Gå till tilläggets hemkatalog och kör följande kommando.

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

Kommentar

En tilläggs-/integrationsversion måste ökas vid varje uppdatering.
När du uppdaterar ett befintligt tillägg uppdaterar du antingen versionen i manifestet eller skickar kommandoradsväxeln --rev-version . Detta ökar uppdateringsversionsnumret för tillägget och sparar den nya versionen i manifestet.

När du har paketerade tillägget i en .vsix-fil är du redo att publicera tillägget på Marketplace.

Skapa utgivare för tillägget

Alla tillägg, inklusive tillägg från Microsoft, identifieras som tillhandahållna av en utgivare. Om du inte redan är medlem i en befintlig utgivare skapar du en.

  1. Logga in på Visual Studio Marketplace-publiceringsportalen
  2. Om du inte redan är medlem i en befintlig utgivare måste du skapa en utgivare. Om du redan har en utgivare bläddrar du till och väljer Publicera tillägg under Relaterade webbplatser.
    • Ange en identifierare för utgivaren, till exempel: mycompany-myteam
      • Identifieraren används som värde för publisher attributet i tilläggens manifestfil.
    • Ange ett visningsnamn för utgivaren, till exempel: My Team
  3. Granska Marketplace Publisher-avtalet och välj Skapa.

Nu har utgivaren definierats. I en framtida version kan du bevilja behörighet att visa och hantera utgivarens tillägg.

Publiceringstillägg under en gemensam utgivare förenklar processen för team och organisationer och erbjuder en säkrare metod. Den här metoden eliminerar behovet av att distribuera en enda uppsättning autentiseringsuppgifter mellan flera användare, vilket förbättrar säkerheten och

vss-extension.json Uppdatera manifestfilen i exemplen för att ersätta utgivar-IDfabrikam:t för dummy med ditt utgivar-ID.

Publicera och dela tillägget

Nu kan du ladda upp ditt tillägg till Marketplace.

Välj Ladda upp nytt tillägg, gå till den paketerade .vsix-filen och välj Ladda upp.

Du kan också ladda upp tillägget via kommandoraden tfx extension publish genom att använda kommandot i stället för tfx extension create att paketera och publicera tillägget i ett steg. Du kan också använda --share-with för att dela tillägget med ett eller flera konton efter publiceringen. Du behöver också en personlig åtkomsttoken.

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

Steg 7: Lägg till widget från katalogen

  1. Logga in på projektet. http://dev.azure.com/{Your_Organization}/{Your_Project}

  2. Välj Översiktsinstrumentpaneler>.

  3. Välj Lägg till en widget.

  4. Markera widgeten och välj sedan Lägg till.

    Widgeten visas på instrumentpanelen.

Del 2: Hello World med Azure DevOps REST API

Widgetar kan anropa någon av REST-API:erna i Azure DevOps för att interagera med Azure DevOps-resurser. I följande exempel använder vi REST API för WorkItemTracking för att hämta information om en befintlig fråga och visa viss frågeinformation i widgeten under texten "Hello World".

Skärmbild av översiktsinstrumentpanelen med en exempelwidget med hjälp av REST API för WorkItemTracking.

Steg 1: Lägg till HTML-fil

Kopiera filen hello-world.html från föregående exempel och byt namn på kopian till hello-world2.html. Mappen ser nu ut som i följande exempel:

|--- README.md |--- node_modules
|--- SDK
|--- skript |--- VSS. SDK.min.js
|--- img
|--- logo.png
|--- skript
|--- hello-world.html // html-sida som ska användas för widgeten
|--- hello-world2.html // omdöpt kopia av hello-world.html |--- vss-extension.json // tilläggsmanifestet

Om du vill lagra frågeinformationen lägger du till ett nytt div element under h2. Uppdatera namnet på widgeten från HelloWorldWidget till HelloWorldWidget2 på raden där du anropar VSS.register. Med den här åtgärden kan ramverket unikt identifiera widgeten i tillägget.

<!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>

Steg 2: Få åtkomst till Azure DevOps-resurser

Om du vill aktivera åtkomst till Azure DevOps-resurser måste omfång anges i tilläggsmanifestet . Vi lägger till omfånget i vso.work vårt manifest.
Det här omfånget anger att widgeten behöver skrivskyddad åtkomst till frågor och arbetsobjekt. Se alla tillgängliga omfång här. Lägg till följande kod i slutet av tilläggsmanifestet.

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

Om du vill inkludera andra egenskaper bör du uttryckligen ange dem, till exempel:

{
    "name": "example-widget",
    "publisher": "example-publisher",
    "version": "1.0.0",
    "scopes": [
        "vso.work"
    ]
}

Varning

Det finns för närvarande inte stöd för att lägga till eller ändra omfång efter publicering av ett tillägg. Om du redan har laddat upp tillägget tar du bort det från Marketplace. Gå till Visual Studio Marketplace-publiceringsportalen, högerklicka på tillägget och välj Ta bort.

Steg 3: Gör REST API-anropet

Det finns många bibliotek på klientsidan som kan nås via SDK för att göra REST API-anrop i Azure DevOps. Dessa bibliotek kallas REST-klienter och är JavaScript-omslutningar runt Ajax-anrop för alla tillgängliga slutpunkter på serversidan. Du kan använda metoder som tillhandahålls av dessa klienter i stället för att skriva Ajax-anrop själv. Dessa metoder mappar API-svaren till objekt som koden kan använda.

I det här steget uppdaterar vi anropet VSS.require för att läsa in AzureDevOps/WorkItemTracking/RestClient, som tillhandahåller REST-klienten WorkItemTracking. Vi kan använda den här REST-klienten för att hämta information om en fråga som heter Feedback under mappen Shared Queries.

Inuti funktionen som vi skickar till VSS.registerskapar vi en variabel för att lagra det aktuella projekt-ID:t. Vi behöver den här variabeln för att hämta frågan. Vi skapar också en ny metod getQueryInfo för att använda REST-klienten. Den här metoden som sedan anropas från inläsningsmetoden.

Metoden getClient ger en instans av REST-klienten som vi behöver. Metoden getQuery returnerar frågan omsluten i ett löfte. Den uppdaterade VSS.require ser ut så här:

VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/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();
    });

Observera användningen av felmetoden från WidgetStatusHelper. Det gör att du kan ange för widgetramverket att ett fel uppstod och dra nytta av den standardfelupplevelse som tillhandahålls till alla widgetar.

Om du inte har Feedback frågan under Shared Queries mappen ersätter Shared Queries\Feedback du i koden med sökvägen till en fråga som finns i projektet.

Steg 4: Visa svaret

Det sista steget är att återge frågeinformationen i widgeten. Funktionen getQuery returnerar ett objekt av typen Contracts.QueryHierarchyItem inuti ett löfte. I det här exemplet visar vi fråge-ID, frågenamn och namnet på frågeskapare under texten "Hello World". Ersätt kommentaren // Do something with the query med följande kod:

// Create a list with query details                                
var $list = $('<ul>');                                
$list.append($('<li>').text("Query Id: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').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);

Din sista hello-world2.html är som i följande exempel:

<!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(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/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($('<li>').text("Query ID: " + query.id));
                                $list.append($('<li>').text("Query Name: " + query.name));
                                $list.append($('<li>').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>

Steg 5: Uppdatera tilläggsmanifest

I det här steget uppdaterar vi tilläggsmanifestet så att det innehåller en post för vår andra widget. Lägg till ett nytt bidrag till matrisen contributions i egenskapen och lägg till den nya filen hello-world2.html i matrisen i filegenskapen. Du behöver en annan förhandsgranskningsbild för den andra widgeten. Namnge detta preview2.png och placera det i img mappen.

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

Steg 6: Paketera, publicera och dela

Paketera, publicera och dela tillägget. Om du redan har publicerat tillägget kan du packa om tillägget och uppdatera det direkt till Marketplace.

Steg 7: Lägg till widget från katalogen

Gå nu till din teaminstrumentpanel på https:\//dev.azure.com/{Your_Organization}/{Your_Project}. Om den här sidan redan är öppen uppdaterar du den. Hovra på Redigera och välj Lägg till. Widgetkatalogen öppnas där du hittar widgeten som du har installerat. Om du vill lägga till den på instrumentpanelen väljer du widgeten och väljer Lägg till.

Del 3: Konfigurera Hello World

I del 2 av den här guiden såg du hur du skapar en widget som visar frågeinformation för en hårdkodad fråga. I den här delen lägger vi till möjligheten att konfigurera frågan som ska användas i stället för den hårdkodade. När användaren är i konfigurationsläge visas en liveförhandsgranskning av widgeten baserat på deras ändringar. Ändringarna sparas i widgeten på instrumentpanelen när användaren väljer Spara.

Skärmbild av översiktsinstrumentpanelens liveförhandsgranskning av widgeten baserat på ändringar.

Steg 1: Lägg till HTML-fil

Implementeringar av widgetar och widgetkonfigurationer är mycket lika. Båda implementeras i tilläggsramverket som bidrag. Båda använder samma SDK-fil, VSS.SDK.min.js. Båda baseras på HTML, JavaScript och CSS.

Kopiera filen html-world2.html från föregående exempel och byt namn på kopian till hello-world3.html. Lägg till en annan HTML-fil med namnet configuration.html. Mappen ser nu ut som i följande exempel:

|--- 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

Lägg till följande HTML i configuration.html. Vi lägger i princip till den obligatoriska referensen VSS.SDK.min.js till filen och ett select element för listrutan för att välja en fråga från en förinställd lista.

    <!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>

Steg 2: Konfigurera JavaScript

Använd JavaScript för att återge innehåll i widgetkonfigurationen precis som vi gjorde för widgeten i steg 3 i del 1 i den här guiden. Den här JavaScript-koden renderar innehåll, initierar VSS SDK, mappar koden för widgetkonfigurationen till konfigurationsnamnet och skickar konfigurationsinställningarna till ramverket. I vårt fall läser följande kod in widgetkonfigurationen. Öppna filen configuration.html och följande <script> element i <head>.

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

    VSS.require(["AzureDevOps/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.init, VSS.requireoch VSS.register spelar samma roll som de spelade för widgeten enligt beskrivningen i del 1. Den enda skillnaden är att för widgetkonfigurationer ska funktionen som skickas till VSS.register returnera ett objekt som uppfyller IWidgetConfiguration kontraktet.
  • Kontraktets load IWidgetConfiguration egenskap bör ha en funktion som värde. Den här funktionen har en uppsättning steg för att återge widgetkonfigurationen. I vårt fall är det att uppdatera det valda värdet för listruteelementet med eventuella befintliga inställningar. Den här funktionen anropas när ramverket instansierar din widget configuration
  • Kontraktets onSave IWidgetConfiguration egenskap bör ha en funktion som värde. Den här funktionen anropas av ramverket när användaren väljer Spara i konfigurationsfönstret. Om användarens indata är redo att sparas serialiserar du det till en sträng, bildar custom settings objektet och använder WidgetConfigurationSave.Valid() för att spara användarindata.

I den här guiden använder vi JSON för att serialisera användarens indata till en sträng. Du kan välja något annat sätt att serialisera användarens indata till strängen. Den är tillgänglig för widgeten via objektets customSettings-egenskap WidgetSettings . Widgeten måste deserialisera, vilket beskrivs i steg 4.

Steg 3: JavaScript – aktivera liveförhandsgranskning

Om du vill aktivera live-förhandsgranskningsuppdatering när användaren väljer en fråga i listrutan bifogar vi en ändringshändelsehanterare till knappen. Den här hanteraren meddelar det ramverk som konfigurationen ändrade. Den skickar även den customSettings som ska användas för att uppdatera förhandsversionen. För att meddela ramverket notify måste metoden anropas widgetConfigurationContext . Det tar två parametrar, namnet på händelsen, som i det här fallet är WidgetHelpers.WidgetEvent.ConfigurationChange, och ett EventArgs objekt för händelsen, som skapats customSettings från med hjälp av WidgetEvent.Args hjälpmetoden.

Lägg till följande kod i funktionen som tilldelats egenskapen 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);
 });

Reviderad: Se till att ramverket meddelas om konfigurationsändringen minst en gång för att aktivera knappen Spara .

I slutet ser ditt configuration.html ut som i följande exempel:

<!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(["AzureDevOps/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>

Steg 4: Implementera omläsning i widgeten – JavaScript

Vi konfigurerar widgetkonfiguration för att lagra den frågesökväg som användaren har valt. Nu måste vi uppdatera koden i widgeten för att använda den här lagrade konfigurationen i stället för den hårdkodade Shared Queries/Feedback från föregående exempel.

Öppna filen hello-world3.html och uppdatera namnet på widgeten från HelloWorldWidget2 till HelloWorldWidget3 på raden där du anropar VSS.register. Med den här åtgärden kan ramverket unikt identifiera widgeten i tillägget.

Funktionen som mappas till HelloWorldWidget3 via VSS.register returnerar för närvarande ett objekt som uppfyller IWidget kontraktet. Eftersom vår widget nu behöver konfiguration måste den här funktionen uppdateras för att returnera ett objekt som uppfyller IConfigurableWidget kontraktet. Det gör du genom att uppdatera retursatsen så att den innehåller en egenskap som kallas reload enligt följande kod. Värdet för den här egenskapen är en funktion som anropar getQueryInfo metoden en gång till. Den här omläsningsmetoden anropas av ramverket varje gång användarens indata ändras för att visa liveförhandsgranskningen. Den här omläsningsmetoden anropas också när konfigurationen sparas.

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

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

Den hårdkodade frågesökvägen i getQueryInfo bör ersättas med den konfigurerade frågesökvägen, som kan extraheras från parametern widgetSettings som skickas till metoden. Lägg till följande kod i början av getQueryInfo metoden och ersätt den hårdkodade frågesökvägen med 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();
}

Nu är widgeten redo att återges med de konfigurerade inställningarna.

load Både egenskaperna och reload har en liknande funktion. Detta gäller för de flesta enkla widgetar. För komplexa widgetar skulle det finnas vissa åtgärder som du skulle vilja köra bara en gång oavsett hur många gånger konfigurationen ändras. Eller så kan det finnas vissa tunga åtgärder som inte behöver köras mer än en gång. Sådana åtgärder skulle vara en del av funktionen som motsvarar load egenskapen och inte egenskapen reload .

Steg 5: Uppdatera tilläggsmanifest

Öppna filen om du vss-extension.json vill inkludera två nya poster i matrisen i egenskapen contributions . En för widgeten HelloWorldWidget3 och den andra för dess konfiguration. Du behöver ännu en förhandsgranskningsbild för den tredje widgeten. Namnge detta preview3.png och placera det i img mappen. Uppdatera matrisen files i egenskapen så att den innehåller de två nya HTML-filer som vi lade till i det här exemplet.

{
    ...
    "contributions": [
        ... , 
        {
             "id": "HelloWorldWidget3",
             "type": "ms.vss-dashboards-web.widget",
             "targets": [
                 "ms.vss-dashboards-web.widget-catalog",
                 "fabrikam.azuredevops-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
            }
        ],
        ...     
}

Bidraget för widgetkonfigurationen följer en något annorlunda modell än själva widgeten. En bidragspost för widgetkonfiguration har:

  • ID :t för att identifiera ditt bidrag. ID:t ska vara unikt i ett tillägg.
  • Typ av bidrag. För alla widgetkonfigurationer bör det vara ms.vss-dashboards-web.widget-configuration
  • Matrisen med mål som bidraget bidrar till. För alla widgetkonfigurationer har den en enda post: ms.vss-dashboards-web.widget-configuration.
  • Egenskaperna som innehåller en uppsättning egenskaper som innehåller namn, beskrivning och URI för HTML-filen som används för konfiguration.

För att stödja konfigurationen måste widgetbidraget också ändras. Matrisen med mål för widgeten måste uppdateras för att inkludera ID:t för konfigurationen i formatet>publisher< .><id for the extension.id for the configuration contribution<> som i det här fallet är .fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration

Varning

Om bidragsposten för din konfigurerbara widget inte riktar sig mot konfigurationen med rätt utgivare och tilläggsnamn enligt beskrivningen ovan visas inte knappen konfigurera för widgeten.

I slutet av den här delen ska manifestfilen innehålla tre widgetar och en konfiguration. Du kan hämta det fullständiga manifestet från exemplet här.

Steg 6: Paketera, publicera och dela

Om tillägget inte har publicerats kan du läsa det här avsnittet. Om du redan har publicerat tillägget kan du packa om tillägget och uppdatera det direkt till Marketplace.

Steg 7: Lägg till widget från katalogen

Gå nu till din teaminstrumentpanel på https://dev.azure.com/{Your_Organization}/{Your_Project}. Om den här sidan redan är öppen uppdaterar du den. Hovra på Redigera och välj Lägg till. Den här åtgärden bör öppna widgetkatalogen där du hittar widgeten som du har installerat. Om du vill lägga till widgeten på instrumentpanelen väljer du widgeten och väljer Lägg till.

Ett meddelande som liknar följande ber dig att konfigurera widgeten.

Skärmbild av översiktsinstrumentpanelen med en exempelwidget från katalogen.

Det finns två sätt att konfigurera widgetar. En är att hovra på widgeten, välja ellipsen som visas i det övre högra hörnet och sedan välja Konfigurera. Det andra är att välja knappen Redigera längst ned till höger på instrumentpanelen och sedan välja knappen Konfigurera som visas i det övre högra hörnet av widgeten. Antingen öppnas konfigurationsupplevelsen till höger och en förhandsgranskning av widgeten i mitten. Gå vidare och välj en fråga i listrutan. Live-förhandsgranskningen visar de uppdaterade resultaten. Välj Spara och widgeten visar de uppdaterade resultaten.

Steg 8: Konfigurera mer (valfritt)

Du kan lägga till så många HTML-formulärelement som du behöver i configuration.html för mer konfiguration. Det finns två konfigurerbara funktioner som är tillgängliga out-of-the-box: Widgetnamn och widgetstorlek.

Som standard lagras det namn som du anger för widgeten i tilläggsmanifestet som widgetnamn för varje instans av widgeten som någonsin läggs till på en instrumentpanel. Du kan tillåta användare att konfigurera, så att de kan lägga till valfritt namn som de vill i sin instans av widgeten. Om du vill tillåta en sådan konfiguration lägger du till isNameConfigurable:true i avsnittet egenskaper för widgeten i tilläggsmanifestet.

Om du anger mer än en post för widgeten i matrisen supportedSizes i tilläggsmanifestet kan användarna även konfigurera widgetens storlek.

Tilläggsmanifestet för det tredje exemplet i den här guiden skulle se ut som i följande exempel om vi aktiverar widgetens namn och storlekskonfiguration:

{
    ...
    "contributions": [
        ... , 
        {
             "id": "HelloWorldWidget3",
             "type": "ms.vss-dashboards-web.widget",
             "targets": [
                 "ms.vss-dashboards-web.widget-catalog",  
                 "fabrikam.azuredevops-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"]
             }
         },
         ...
    ]
}

Med föregående ändring packar du om och uppdaterar tillägget. Uppdatera instrumentpanelen som har den här widgeten (Hello World Widget 3 (med konfiguration)). Öppna konfigurationsläget för widgeten. Nu bör du kunna se alternativet att ändra widgetens namn och storlek.

Skärmbild som visar widgeten där namn och storlek kan konfigureras.

Välj en annan storlek i listrutan. Du ser att liveförhandsgranskningen ändras. Spara ändringen och widgeten på instrumentpanelen ändras också.

Om du ändrar namnet på widgeten resulterar det inte i någon synlig ändring i widgeten eftersom våra exempelwidgetar inte visar widgetnamnet någonstans. Låt oss ändra exempelkoden för att visa widgetnamnet i stället för den hårdkodade texten "Hello World".

Det gör du genom att ersätta den hårdkodade texten "Hello World" med widgetSettings.name på raden där vi anger elementets h2 text. Den här åtgärden säkerställer att widgetnamnet visas varje gång widgeten läses in vid siduppdatering. Eftersom vi vill att live-förhandsversionen ska uppdateras varje gång konfigurationen ändras bör vi även lägga till samma kod i den reload del av koden. Den slutliga retursatsen i hello-world3.html är följande:

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);
    }
}

Packa om och uppdatera tillägget igen. Uppdatera instrumentpanelen som har den här widgeten.

Alla ändringar av widgetens namn, i konfigurationsläget, uppdaterar widgettiteln nu.