Delen via


Een dashboardwidget toevoegen

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

Widgets op een dashboard worden geïmplementeerd als bijdragen in het extensieframework. Eén extensie kan meerdere bijdragen hebben. Meer informatie over het maken van een extensie met meerdere widgets als bijdragen.

Dit artikel is onderverdeeld in drie delen, elk gebouw op het vorige - beginnend met een eenvoudige widget en eindigend met een uitgebreide widget.

Tip

Bekijk onze nieuwste documentatie over uitbreidingsontwikkeling met behulp van de Azure DevOps Extension SDK.

Vereisten

  • Kennis: Enige kennis van JavaScript, HTML, CSS is vereist voor widgetontwikkeling.
  • Een organisatie in Azure DevOps.
  • Een teksteditor. Voor veel van de zelfstudies gebruiken we Visual Studio Code.
  • De nieuwste versie van het knooppunt.
  • Platformoverschrijdende CLI voor Azure DevOps (tfx-cli) om uw extensies te verpakken.
    • tfx-cli kan worden geïnstalleerd met behulp npmvan, een onderdeel van Node.js door uit te voeren npm i -g tfx-cli
  • Een basismap voor uw project. Deze map wordt in de zelfstudie genoemd home .

Bestandsstructuur extensie:

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

In deze zelfstudie

  1. Deel 1: Laat zien hoe u een nieuwe widget maakt, waarmee een eenvoudig bericht 'Hallo wereld' wordt afgedrukt.
  2. Deel 2: bouwt voort op het eerste deel door een aanroep toe te voegen aan een Azure DevOps REST API.
  3. Deel 3: Hierin wordt uitgelegd hoe u configuratie toevoegt aan uw widget.

Notitie

Als u haast hebt en meteen de code wilt gebruiken, kunt u de voorbeelden downloaden. Nadat u het bestand hebt gedownload, gaat u naar de widgets map en volgt u stap 6 en stap 7 rechtstreeks om de voorbeeldextensie te publiceren met de drie voorbeeldwidgets met verschillende complexiteiten.

Ga aan de slag met enkele basisstijlen voor widgets die we kant-en-klare bieden en enkele richtlijnen voor widgetstructuur.

Deel 1: Hallo wereld

Deel 1 geeft een widget weer waarmee 'Hallo wereld' wordt afgedrukt met behulp van JavaScript.

Schermopname van het dashboard Overzicht met een voorbeeldwidget.

Stap 1: de client-SDK ophalen - VSS.SDK.min.js

Met het SDK-kernscript VSS.SDK.min.jskunnen webextensies communiceren met het Azure DevOps-frame van de host. Het script voert bewerkingen uit zoals initialiseren, het melden van de extensie wordt geladen of het ophalen van context over de huidige pagina. Haal het client-SDK-bestand VSS.SDK.min.js op en voeg het toe aan uw web-app. Plaats deze in de home/sdk/scripts map.

Gebruik de opdracht npm install om de SDK op te halen:

npm install vss-web-extension-sdk

Zie de GitHub-pagina client-SDK voor meer informatie.

Stap 2: uw HTML-pagina instellen - hello-world.html

Uw HTML-pagina is de lijm die uw indeling bij elkaar houdt en verwijzingen naar CSS en JavaScript bevat. U kunt dit bestand een naam opgeven. Werk alle verwijzingen naar hello-world bij met de naam die u gebruikt.

Uw widget is gebaseerd op HTML en wordt gehost in een iframe. Voeg de volgende HTML toe in hello-world.html. We voegen de verplichte verwijzing naar VSS.SDK.min.js het bestand toe en voegen een h2 element toe, dat wordt bijgewerkt met de tekenreeks Hallo wereld in de volgende stap.

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

Hoewel we een HTML-bestand gebruiken, worden de meeste HTML-hoofdelementen anders dan script en koppeling genegeerd door het framework.

Stap 3: JavaScript bijwerken

We gebruiken JavaScript om inhoud in de widget weer te geven. In dit artikel verpakken we al onze JavaScript-code in een &lt;script&gt; element in het HTML-bestand. U kunt ervoor kiezen om deze code in een afzonderlijk JavaScript-bestand te plaatsen en ernaar te verwijzen in het HTML-bestand. De code geeft de inhoud weer. Met deze JavaScript-code wordt ook de VSS SDK geïnitialiseerd, wordt de code voor uw widget toegewezen aan de naam van de widget en wordt het extensieframework van geslaagde of mislukte widgets op de hoogte gesteld. In ons geval wordt met de volgende code 'Hallo wereld' in de widget afgedrukt. Voeg dit script element toe aan de head HTML-code.

<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 initialiseert de handshake tussen het iframe dat als host fungeert voor de widget en het hostframe.
  • We geven door explicitNotifyLoaded: true , zodat de widget de host expliciet kan informeren wanneer deze klaar is met laden. Met dit besturingselement kunnen we de voltooiing van de belasting melden nadat we ervoor hebben gezorgd dat de afhankelijke modules worden geladen. We geven door usePlatformStyles: true , zodat de widget kernstijlen van Azure DevOps kan gebruiken voor HTML-elementen (zoals hoofdtekst, div, enzovoort). Als de widget deze stijlen liever niet gebruikt, kunnen ze doorgeven usePlatformStyles: false.
  • VSS.require wordt gebruikt om de vereiste VSS-scriptbibliotheken te laden. Met een aanroep van deze methode worden automatisch algemene bibliotheken, zoals JQuery en JQueryUI, geladen. In ons geval zijn we afhankelijk van de WidgetHelpers-bibliotheek, die wordt gebruikt om de widgetstatus aan het widgetframework te communiceren. Daarom geven we de bijbehorende modulenaam TFS/Dashboards/WidgetHelpers en een callback door aan VSS.require. De callback wordt aangeroepen zodra de module is geladen. De callback bevat de rest van de JavaScript-code die nodig is voor de widget. Aan het einde van de callback roepen VSS.notifyLoadSucceeded we aan om de voltooiing van de belasting te melden.
  • WidgetHelpers.IncludeWidgetStyles bevat een opmaakmodel met enkele basis-css om u op weg te helpen. Als u deze stijlen wilt gebruiken, verpakt u uw inhoud in een HTML-element met klasse widget.
  • VSS.register wordt gebruikt om een functie toe te wijzen in JavaScript, waarmee de widget uniek wordt geïdentificeerd tussen de verschillende bijdragen in uw extensie. De naam moet overeenkomen met de id naam die uw bijdrage identificeert, zoals beschreven in stap 5. Voor widgets moet de functie die wordt doorgegeven VSS.register een object retourneren dat voldoet aan het IWidget contract, bijvoorbeeld dat het geretourneerde object een load-eigenschap moet hebben waarvan de waarde een andere functie is die de kernlogica heeft om de widget weer te geven. In ons geval is het om de tekst van het h2 element bij te werken naar 'Hallo wereld'. Dit is deze functie die wordt aangeroepen wanneer het widgetframework uw widget instantieert. We gebruiken de WidgetStatusHelper widgethelpers om de WidgetStatus als succes te retourneren.

Waarschuwing

Als de naam die wordt gebruikt om de widget te registreren niet overeenkomt met de id voor de bijdrage in het manifest, werkt de widget onverwacht.

  • vss-extension.json moet altijd in de hoofdmap van de map staan (in deze handleiding, HelloWorld). Voor alle andere bestanden kunt u ze in de gewenste structuur in de map plaatsen, zorg ervoor dat u de verwijzingen op de juiste manier bijwerkt in de HTML-bestanden en in het vss-extension.json manifest.

Stap 4: Werk uw extensielogo bij: logo.png

Uw logo wordt weergegeven in Marketplace en in de widgetcatalogus zodra een gebruiker uw extensie installeert.

U hebt een cataloguspictogram van 98 px x 98 px nodig. Kies een afbeelding, geef deze logo.pngeen naam en plaats deze in de img map.

U kunt deze installatiekopieën de gewenste naam opgeven zolang het extensiemanifest in de volgende stap wordt bijgewerkt met de namen die u gebruikt.

Stap 5: Maak uw extensiemanifest: vss-extension.json

Elke extensie moet een extensiemanifestbestand hebben.

  • Lees de referentie voor het extensiemanifest.
  • Meer informatie over de bijdragepunten in Uitbreidbaarheidspunten.
  • Maak een json-bestand (vss-extension.jsonbijvoorbeeld) in de home map met de volgende inhoud:
{
    "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
        }
    ]
}

Zie de referentie voor het extensiemanifest voor meer informatie over vereiste kenmerken.

Notitie

Wijzig de uitgever in de naam van uw uitgever. Als u een uitgever wilt maken, raadpleegt u Package/Publish/Install.

Pictogrammen

De pictogrammen stanza geeft het pad naar het pictogram van uw extensie in uw manifest.

Bijdragen

Elke bijdragevermelding definieert eigenschappen.

  • De id om uw bijdrage te identificeren. Deze id moet uniek zijn binnen een extensie. Deze id moet overeenkomen met de naam die u in stap 3 hebt gebruikt om uw widget te registreren.
  • Het type bijdrage. Voor alle widgets moet het type zijn ms.vss-dashboards-web.widget.
  • De matrix met doelen waaraan de bijdrage bijdraagt. Voor alle widgets moet het doel zijn [ms.vss-dashboards-web.widget-catalog].
  • De eigenschappen zijn objecten met eigenschappen voor het bijdragetype. Voor widgets zijn de volgende eigenschappen verplicht.
Eigenschappen Beschrijving
naam De naam van de widget die moet worden weergegeven in de widgetcatalogus.
beschrijving Beschrijving van de widget die moet worden weergegeven in de widgetcatalogus.
catalogIconUrl Relatief pad van het cataloguspictogram dat u in stap 4 hebt toegevoegd om weer te geven in de widgetcatalogus. De afbeelding moet 98 px x 98 px zijn. Als u een andere mapstructuur of een andere bestandsnaam hebt gebruikt, geeft u hier het juiste relatieve pad op.
previewImageUrl Relatief pad van de voorbeeldafbeelding die u in stap 4 hebt toegevoegd om weer te geven in de widgetcatalogus. De afbeelding moet 330 px x 160 px zijn. Als u een andere mapstructuur of een andere bestandsnaam hebt gebruikt, geeft u hier het juiste relatieve pad op.
uri Relatief pad van het HTML-bestand dat u in stap 1 hebt toegevoegd. Als u een andere mapstructuur of een andere bestandsnaam hebt gebruikt, geeft u hier het juiste relatieve pad op.
supportedSizes Matrix met grootten die door uw widget worden ondersteund. Wanneer een widget meerdere grootten ondersteunt, is de eerste grootte in de matrix de standaardgrootte van de widget. De widget size waarde wordt opgegeven voor de rijen en kolommen die worden bezet door de widget in het dashboardraster. Eén rij/kolom komt overeen met 160 px. Elke dimensie groter dan 1x1 krijgt een extra 10 px die de rugmarge tussen widgets vertegenwoordigt. Een 3x2-widget is 160*3+10*2 bijvoorbeeld breed en 160*2+10*1 hoog. De maximale ondersteunde grootte is 4x4.
supportedScopes Momenteel worden alleen teamdashboards ondersteund. De waarde moet zijn project_team. Toekomstige updates bevatten mogelijk meer opties voor dashboardbereiken.

Bestanden

In de bestanden worden de bestanden vermeld die u wilt opnemen in uw pakket: uw HTML-pagina, uw scripts, het SDK-script en uw logo. Ingesteld addressable op true tenzij u andere bestanden opneemt die niet url-adresseerbaar hoeven te zijn.

Notitie

Raadpleeg de referentie voor het extensiemanifest voor meer informatie over het extensiemanifestbestand, zoals de eigenschappen en wat ze doen.

Stap 6: Verpakken, publiceren en delen

Zodra u uw geschreven extensie hebt, is de volgende stap bij het inpakken van de marketplace om al uw bestanden samen te verpakken. Alle extensies zijn verpakt als VSIX 2.0-compatibele .vsix-bestanden. Microsoft biedt een platformoverschrijdende opdrachtregelinterface (CLI) om uw extensie te verpakken.

Het verpakkingshulpmiddel ophalen

U kunt de platformoverschrijdende CLI voor Azure DevOps (tfx-cli) installeren of bijwerken met behulp npmvan een onderdeel van Node.js vanaf de opdrachtregel.

npm i -g tfx-cli

Uw extensie verpakken

Het verpakken van uw extensie in een .vsix-bestand is moeiteloos zodra u de tfx-cli hebt. Ga naar de basismap van uw extensie en voer de volgende opdracht uit.

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

Notitie

Bij elke update moet een extensie-/integratieversie worden verhoogd.
Wanneer u een bestaande extensie bijwerkt, werkt u de versie in het manifest bij of geeft u de --rev-version opdrachtregelswitch door. Hiermee wordt het patchversienummer van uw extensie verhoogd en wordt de nieuwe versie opgeslagen in uw manifest.

Nadat u de extensie in een .vsix-bestand hebt verpakt, kunt u de extensie publiceren naar Marketplace.

Uitgever voor de extensie maken

Alle extensies, inclusief extensies van Microsoft, worden geïdentificeerd als geleverd door een uitgever. Als u nog geen lid bent van een bestaande uitgever, maakt u er een.

  1. Meld u aan bij de Visual Studio Marketplace-publicatieportal
  2. Als u nog geen lid bent van een bestaande uitgever, moet u een uitgever maken. Als u al een uitgever hebt, bladert u naar en selecteert u Extensies publiceren onder Verwante sites.
    • Geef een id op voor uw uitgever, bijvoorbeeld: mycompany-myteam
      • De id wordt gebruikt als de waarde voor het kenmerk in het publisher manifestbestand van uw extensies.
    • Geef een weergavenaam op voor uw uitgever, bijvoorbeeld: My Team
  3. Controleer de Marketplace Publisher-overeenkomst en selecteer Maken.

De uitgever is nu gedefinieerd. In een toekomstige release kunt u machtigingen verlenen om de extensies van uw uitgever weer te geven en te beheren.

Het publiceren van extensies onder een algemene uitgever vereenvoudigt het proces voor teams en organisaties en biedt een veiligere aanpak. Deze methode elimineert de noodzaak om één set referenties te distribueren tussen meerdere gebruikers, waardoor de beveiliging wordt verbeterd en

Werk het vss-extension.json manifestbestand in de voorbeelden bij om de dummy-uitgever-id te vervangen door uw uitgevers-id fabrikam .

De extensie publiceren en delen

Nu kunt u uw extensie uploaden naar Marketplace.

Selecteer De nieuwe extensie Uploaden, ga naar het verpakte .vsix-bestand en selecteer Uploaden.

U kunt uw extensie ook uploaden via de opdrachtregel met behulp van de tfx extension publish opdracht in plaats van tfx extension create uw extensie in één stap te verpakken en te publiceren. U kunt eventueel --share-with uw extensie delen met een of meer accounts na publicatie. U hebt ook een persoonlijk toegangstoken nodig.

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

Stap 7: Widget toevoegen uit de catalogus

  1. Meld u aan bij uw project. http://dev.azure.com/{Your_Organization}/{Your_Project}

  2. Selecteer Overzichtsdashboards>.

  3. Selecteer Een widget toevoegen.

  4. Markeer uw widget en selecteer vervolgens Toevoegen.

    De widget wordt weergegeven op uw dashboard.

Deel 2: Hallo wereld met Azure DevOps REST API

Widgets kunnen een van de REST API's in Azure DevOps aanroepen om te communiceren met Azure DevOps-resources. In het volgende voorbeeld gebruiken we de REST API voor WorkItemTracking om informatie over een bestaande query op te halen en enkele querygegevens weer te geven in de widget onder de tekst 'Hallo wereld'.

Schermopname van het overzichtsdashboard met een voorbeeldwidget met behulp van de REST API for WorkItemTracking.

Stap 1: HTML-bestand toevoegen

Kopieer het bestand hello-world.html uit het vorige voorbeeld en wijzig de naam van de kopie in hello-world2.html. Uw map ziet er nu uit zoals in het volgende voorbeeld:

|--- README.md |--- node_modules
|--- SDK
|--- scripts |--- VSS. SDK.min.js |--- img |--- logo.png |--- scripts
|--- hello-world.html // HTML-pagina die moet worden gebruikt voor uw widget |--- hello-world2.html // hernoemde kopie van hello-world.html |--- vss-extension.json // extensiemanifest

Als u de querygegevens wilt opslaan, voegt u een nieuw div element toe onder de h2. Werk de naam van de widget bij van HelloWorldWidget naar HelloWorldWidget2 in de regel waar u aanroept VSS.register. Met deze actie kan het framework de widget in de extensie uniek identificeren.

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

Stap 2: Toegang tot Azure DevOps-resources

Als u toegang tot Azure DevOps-resources wilt inschakelen, moeten bereiken worden opgegeven in het extensiemanifest. We voegen het vso.work bereik toe aan ons manifest.
Dit bereik geeft aan dat de widget alleen-lezentoegang nodig heeft tot query's en werkitems. Bekijk hier alle beschikbare bereiken. Voeg de volgende code toe aan het einde van het extensiemanifest.

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

Als u andere eigenschappen wilt opnemen, moet u ze expliciet vermelden, bijvoorbeeld:

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

Waarschuwing

Het toevoegen of wijzigen van bereiken nadat een extensie is gepubliceerd, wordt momenteel niet ondersteund. Als u uw extensie al hebt geüpload, verwijdert u deze uit Marketplace. Ga naar de Visual Studio Marketplace-publicatieportal, selecteer met de rechtermuisknop uw extensie en selecteer Verwijderen.

Stap 3: De REST API-aanroep maken

Er zijn veel bibliotheken aan de clientzijde die toegankelijk zijn via de SDK om REST API-aanroepen te maken in Azure DevOps. Deze bibliotheken worden REST-clients genoemd en zijn JavaScript-wrappers rond Ajax-aanroepen voor alle beschikbare eindpunten aan de serverzijde. U kunt methoden van deze clients gebruiken in plaats van ajax-aanroepen zelf te schrijven. Met deze methoden worden de API-antwoorden toegewezen aan objecten die door uw code kunnen worden gebruikt.

In deze stap wordt de VSS.require aanroep bijgewerkt die moet worden geladen AzureDevOps/WorkItemTracking/RestClient. Dit biedt de WorkItemTracking REST-client. We kunnen deze REST-client gebruiken om informatie op te halen over een query die wordt aangeroepen Feedback onder de map Shared Queries.

Binnen de functie die we doorgeven VSS.register, maken we een variabele voor het opslaan van de huidige project-id. We hebben deze variabele nodig om de query op te halen. We maken ook een nieuwe methode getQueryInfo voor het gebruik van de REST-client. Deze methode die vervolgens wordt aangeroepen vanuit de load-methode.

De methode getClient geeft een exemplaar van de REST-client die we nodig hebben. De methode getQuery retourneert de query die in een belofte is verpakt. De bijgewerkte VSS.require versie ziet er als volgt uit:

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

Let op het gebruik van de methode Failure van WidgetStatusHelper. Hiermee kunt u aangeven dat er een fout is opgetreden in het widgetframework en kunt u profiteren van de standaardfoutervaring die aan alle widgets is verstrekt.

Als u de query niet onder de Feedback Shared Queries map hebt, vervangt Shared Queries\Feedback u de code door het pad van een query die in uw project bestaat.

Stap 4: Het antwoord weergeven

De laatste stap is het weergeven van de querygegevens in de widget. De getQuery functie retourneert een object van het type Contracts.QueryHierarchyItem binnen een belofte. In dit voorbeeld geven we de query-id, de naam van de query en de naam van de maker van de query weer onder de tekst 'Hallo wereld'. Vervang de opmerking // Do something with the query door de volgende code:

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

Uw laatste hello-world2.html ziet eruit als in het volgende voorbeeld:

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

Stap 5: Uitbreidingsmanifest bijwerken

In deze stap werken we het extensiemanifest bij om een vermelding voor onze tweede widget op te nemen. Voeg een nieuwe bijdrage toe aan de matrix in de contributions eigenschap en voeg het nieuwe bestand hello-world2.html toe aan de matrix in de bestandseigenschap. U hebt een andere voorbeeldafbeelding nodig voor de tweede widget. Geef deze een naam en plaats deze preview2.png in de img map.

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

Stap 6: Verpakken, publiceren en delen

Uw extensie verpakken, publiceren en delen. Als u de extensie al hebt gepubliceerd, kunt u de extensie opnieuw verpakken en deze rechtstreeks bijwerken naar Marketplace.

Stap 7: Widget toevoegen uit de catalogus

Ga nu naar uw teamdashboard op https:\//dev.azure.com/{Your_Organization}/{Your_Project}. Als deze pagina al is geopend, vernieuwt u deze. Beweeg de muisaanwijzer over Bewerken en selecteer Toevoegen. De widgetcatalogus wordt geopend waar u de widget vindt die u hebt geïnstalleerd. Als u het aan uw dashboard wilt toevoegen, kiest u uw widget en selecteert u Toevoegen.

Deel 3: Hallo wereld configureren

In deel 2 van deze handleiding hebt u gezien hoe u een widget maakt waarin querygegevens voor een in code vastgelegde query worden weergegeven. In dit deel voegen we de mogelijkheid toe om de query te configureren die moet worden gebruikt in plaats van de in code vastgelegde query. In de configuratiemodus krijgt de gebruiker een livevoorbeeld van de widget te zien op basis van hun wijzigingen. Deze wijzigingen worden opgeslagen in de widget op het dashboard wanneer de gebruiker Opslaan selecteert.

Schermopname van livevoorbeeld van het overzichtsdashboard van de widget op basis van wijzigingen.

Stap 1: HTML-bestand toevoegen

Implementaties van widgets en widgetconfiguraties zijn veel hetzelfde. Beide worden geïmplementeerd in het extensieframework als bijdragen. Beide gebruiken hetzelfde SDK-bestand, VSS.SDK.min.js. Beide zijn gebaseerd op HTML, JavaScript en CSS.

Kopieer het bestand html-world2.html uit het vorige voorbeeld en wijzig de naam van de kopie in hello-world3.html. Voeg nog een HTML-bestand toe met de naam configuration.html. Uw map ziet er nu uit zoals in het volgende voorbeeld:

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

Voeg de volgende HTML toe in configuration.html. We voegen in feite de verplichte verwijzing toe aan het VSS.SDK.min.js bestand en een select element voor de vervolgkeuzelijst om een query te selecteren in een vooraf ingestelde lijst.

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

Stap 2: JavaScript configureren

Gebruik JavaScript om inhoud weer te geven in de widgetconfiguratie, net zoals we hebben gedaan voor de widget in stap 3 van deel 1 in deze handleiding. Deze JavaScript-code geeft inhoud weer, initialiseert de VSS SDK, wijst de code voor uw widgetconfiguratie toe aan de configuratienaam en geeft de configuratie-instellingen door aan het framework. In ons geval wordt met de volgende code de widgetconfiguratie geladen. Open het bestand configuration.html en het volgende <script> element naar de <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.requireen VSS.register spelen dezelfde rol als voor de widget zoals beschreven in deel 1. Het enige verschil is dat voor widgetconfiguraties de functie die wordt doorgegeven VSS.register , een object moet retourneren dat voldoet aan het IWidgetConfiguration contract.
  • De load eigenschap van het IWidgetConfiguration contract moet een functie hebben als waarde. Deze functie bevat de set stappen om de widgetconfiguratie weer te geven. In ons geval is het om de geselecteerde waarde van het vervolgkeuzeelement bij te werken met bestaande instellingen, indien van toepassing. Deze functie wordt aangeroepen wanneer het framework uw instantie instantieert widget configuration
  • De onSave eigenschap van het IWidgetConfiguration contract moet een functie hebben als waarde. Deze functie wordt aangeroepen door het framework wanneer de gebruiker Opslaan selecteert in het configuratiedeelvenster. Als de gebruikersinvoer gereed is om op te slaan, serialiseert u deze in een tekenreeks, maakt u het custom settings object en gebruikt WidgetConfigurationSave.Valid() u deze om de gebruikersinvoer op te slaan.

In deze handleiding gebruiken we JSON om de gebruikersinvoer in een tekenreeks te serialiseren. U kunt een andere manier kiezen om de gebruikersinvoer naar tekenreeks te serialiseren. Het is toegankelijk voor de widget via de eigenschap customSettings van het WidgetSettings object. De widget moet deserialiseren, wat wordt behandeld in stap 4.

Stap 3: JavaScript : livevoorbeeld inschakelen

Als u livevoorbeeldupdate wilt inschakelen wanneer de gebruiker een query selecteert in de vervolgkeuzelijst, voegen we een wijzigingsgebeurtenishandler toe aan de knop. Deze handler meldt het framework dat de configuratie is gewijzigd. Het geeft ook de customSettings gegevens door die moeten worden gebruikt voor het bijwerken van de preview. Om het framework op de hoogte te stellen, moet de notify methode op de widgetConfigurationContext aangeroepen methode worden aangeroepen. Er zijn twee parameters nodig: de naam van de gebeurtenis, die in dit geval is WidgetHelpers.WidgetEvent.ConfigurationChange, en een EventArgs object voor de gebeurtenis, gemaakt op basis customSettings van WidgetEvent.Args de helpermethode.

Voeg de volgende code toe aan de functie die is toegewezen aan de load eigenschap.

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

Herzien: Zorg ervoor dat het framework ten minste eenmaal op de hoogte wordt gesteld van de configuratiewijziging om de knop Opslaan in te schakelen.

Aan het einde ziet uw configuration.html afbeelding eruit als in het volgende voorbeeld:

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

Stap 4: Opnieuw laden implementeren in de widget - JavaScript

We hebben de widgetconfiguratie ingesteld om het querypad op te slaan dat door de gebruiker is geselecteerd. We moeten nu de code in de widget bijwerken om deze opgeslagen configuratie te gebruiken in plaats van de in code vastgelegde Shared Queries/Feedback code uit het vorige voorbeeld.

Open het bestand hello-world3.html en werk de naam van de widget bij van HelloWorldWidget2 naar HelloWorldWidget3 in de regel waar u aanroept VSS.register. Met deze actie kan het framework de widget in de extensie uniek identificeren.

De functie die via momenteel is toegewezen HelloWorldWidget3 , VSS.register retourneert een object dat voldoet aan het IWidget contract. Omdat onze widget nu configuratie nodig heeft, moet deze functie worden bijgewerkt om een object te retourneren dat voldoet aan het IConfigurableWidget contract. Werk hiervoor de retourinstructie bij om een eigenschap met de naam opnieuw laden op te nemen volgens de volgende code. De waarde voor deze eigenschap is een functie die de getQueryInfo methode nog één keer aanroept. Deze herloadmethode wordt door het framework aangeroepen telkens wanneer de invoer van de gebruiker verandert om de livevoorbeeld weer te geven. Deze herloadmethode wordt ook aangeroepen wanneer de configuratie wordt opgeslagen.

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

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

Het in code vastgelegde querypad moet worden vervangen door het geconfigureerde querypad getQueryInfo , dat kan worden geëxtraheerd uit de parameter widgetSettings die wordt doorgegeven aan de methode. Voeg de volgende code toe aan het begin van de getQueryInfo methode en vervang het in code vastgelegde querypad door 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();
}

Op dit moment is uw widget klaar om te worden weergegeven met de geconfigureerde instellingen.

Zowel de load eigenschappen als de reload eigenschappen hebben een vergelijkbare functie. Dit is het geval voor de meest eenvoudige widgets. Voor complexe widgets zijn er bepaalde bewerkingen die u slechts één keer wilt uitvoeren, ongeacht hoe vaak de configuratie verandert. Of er zijn enkele zware bewerkingen die niet meer dan één keer hoeven te worden uitgevoerd. Dergelijke bewerkingen maken deel uit van de functie die overeenkomt met de load eigenschap en niet de reload eigenschap.

Stap 5: Uitbreidingsmanifest bijwerken

Open het vss-extension.json bestand om twee nieuwe vermeldingen op te nemen in de matrix in de contributions eigenschap. Een voor de HelloWorldWidget3 widget en de andere voor de configuratie. U hebt nog een voorbeeldafbeelding nodig voor de derde widget. Geef deze een naam en plaats deze preview3.png in de img map. Werk de matrix in de files eigenschap bij om de twee nieuwe HTML-bestanden op te nemen die we in dit voorbeeld hebben toegevoegd.

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

De bijdrage voor widgetconfiguratie volgt een iets ander model dan de widget zelf. Een bijdrage-vermelding voor widgetconfiguratie heeft:

  • De id om uw bijdrage te identificeren. De id moet uniek zijn binnen een extensie.
  • Het type bijdrage. Voor alle widgetconfiguraties moet dit zijn ms.vss-dashboards-web.widget-configuration
  • De matrix met doelen waaraan de bijdrage bijdraagt. Voor alle widgetconfiguraties heeft deze één vermelding: ms.vss-dashboards-web.widget-configuration.
  • De eigenschappen die een set eigenschappen bevatten met de naam, beschrijving en de URI van het HTML-bestand dat wordt gebruikt voor configuratie.

Ter ondersteuning van de configuratie moet ook de widgetbijdrage worden gewijzigd. De matrix met doelen voor de widget moet worden bijgewerkt om de id voor de configuratie in het formulier <>publisheropid for the extension>< te nemen.<id for the configuration contribution> Dit is fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configurationin dit geval .

Waarschuwing

Als de bijdragevermelding voor uw configureerbare widget niet is gericht op de configuratie met behulp van de juiste uitgever en extensienaam zoals eerder beschreven, wordt de knop Configureren niet weergegeven voor de widget.

Aan het einde van dit deel moet het manifestbestand drie widgets en één configuratie bevatten. U kunt hier het volledige manifest ophalen uit het voorbeeld.

Stap 6: Verpakken, publiceren en delen

Als uw extensie niet is gepubliceerd, raadpleegt u deze sectie. Als u de extensie al hebt gepubliceerd, kunt u de extensie opnieuw verpakken en deze rechtstreeks bijwerken naar Marketplace.

Stap 7: Widget toevoegen uit de catalogus

Ga nu naar uw teamdashboard op https://dev.azure.com/{Your_Organization}/{Your_Project}. Als deze pagina al is geopend, vernieuwt u deze. Beweeg de muisaanwijzer over Bewerken en selecteer Toevoegen. Met deze actie moet u de widgetcatalogus openen waar u de widget vindt die u hebt geïnstalleerd. Als u de widget aan uw dashboard wilt toevoegen, kiest u uw widget en selecteert u Toevoegen.

Een bericht dat lijkt op het volgende, vraagt u om de widget te configureren.

Schermopname van overzichtsdashboard met een voorbeeldwidget uit de catalogus.

Er zijn twee manieren om widgets te configureren. U kunt de muisaanwijzer op de widget plaatsen, het beletselteken selecteren dat in de rechterbovenhoek wordt weergegeven en vervolgens Configureren selecteren. Het andere is om de knop Bewerken in de rechterbenedenhoek van het dashboard te selecteren en vervolgens de knop Configureren te selecteren die in de rechterbovenhoek van de widget wordt weergegeven. Hiermee opent u de configuratie-ervaring aan de rechterkant en een voorbeeld van uw widget in het midden. Kies een query in de vervolgkeuzelijst. In het livevoorbeeld worden de bijgewerkte resultaten weergegeven. Selecteer Opslaan en de widget geeft de bijgewerkte resultaten weer.

Stap 8: Meer configureren (optioneel)

U kunt zoveel HTML-formulierelementen toevoegen als u nodig hebt in de configuration.html voor meer configuratie. Er zijn twee configureerbare functies beschikbaar: widgetnaam en widgetgrootte.

Standaard wordt de naam die u opgeeft voor uw widget in het extensiemanifest opgeslagen als de widgetnaam voor elk exemplaar van uw widget dat ooit aan een dashboard wordt toegevoegd. U kunt gebruikers toestaan om te configureren, zodat ze elke gewenste naam kunnen toevoegen aan hun exemplaar van uw widget. Als u een dergelijke configuratie wilt toestaan, voegt isNameConfigurable:true u de sectie Eigenschappen voor uw widget toe in het extensiemanifest.

Als u meer dan één vermelding opgeeft voor uw widget in de supportedSizes matrix in het extensiemanifest, kunnen gebruikers ook de grootte van de widget configureren.

Het uitbreidingsmanifest voor het derde voorbeeld in deze handleiding zou er als volgt uitzien als we de configuratie van de widgetnaam en grootte inschakelen:

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

Met de vorige wijziging kunt u de extensie opnieuw verpakken en bijwerken . Vernieuw het dashboard met deze widget (Hallo wereld Widget 3 (met configuratie)). Open de configuratiemodus voor uw widget. U kunt nu de optie zien om de naam en grootte van de widget te wijzigen.

Schermopname van widget waarin de naam en grootte kunnen worden geconfigureerd.

Kies een andere grootte in de vervolgkeuzelijst. U ziet dat het formaat van de livevoorbeeld wordt gewijzigd. Sla de wijziging op en de widget op het dashboard wordt ook aangepast.

Als u de naam van de widget wijzigt, resulteert dit niet in een zichtbare wijziging in de widget, omdat de widgetnaam nergens wordt weergegeven in onze voorbeeldwidgets. Laten we de voorbeeldcode wijzigen om de widgetnaam weer te geven in plaats van de in code vastgelegde tekst 'Hallo wereld'.

Vervang hiervoor de in code vastgelegde tekst 'Hallo wereld' widgetSettings.name door in de regel waar we de tekst van het h2 element instellen. Deze actie zorgt ervoor dat de widgetnaam wordt weergegeven telkens wanneer de widget wordt geladen bij het vernieuwen van de pagina. Omdat we willen dat de livevoorbeeld telkens wordt bijgewerkt wanneer de configuratie wordt gewijzigd, moeten we dezelfde code toevoegen in het reload deel van onze code. De definitieve retourinstructie is hello-world3.html als volgt:

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

Verpak de extensie opnieuw en werk deze opnieuw bij . Vernieuw het dashboard met deze widget.

Wijzigingen in de widgetnaam, in de configuratiemodus, werken de widgettitel nu bij.