Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
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 voortbouwend op het vorige. U begint met een eenvoudige widget en eindigt met een uitgebreide widget.
Aanbeveling
Bekijk onze nieuwste documentatie over uitbreidingsontwikkeling met behulp van de Azure DevOps Extension SDK.
Vereisten
Voorwaarde | Beschrijving |
---|---|
Programmeerkennis | JavaScript-, HTML- en CSS-kennis voor widgetontwikkeling |
Azure DevOps-organisatie | Een organisatie maken |
Teksteditor | We gebruiken Visual Studio Code voor zelfstudies |
Node.js | Nieuwste versie van Node.js |
Platformoverschrijdende CLI |
tfx-cli om extensies te verpakken Installeren met behulp van: npm i -g tfx-cli |
Projectmap | Basismap met de volgende structuur na het voltooien van de handleiding:|--- README.md |--- sdk |--- node_modules |--- scripts |--- VSS.SDK.min.js |--- img |--- logo.png |--- scripts |--- hello-world.html // html page for your widget |--- vss-extension.json // extension manifest |
Overzicht van zelfstudies
In deze zelfstudie leert u widgetontwikkeling via drie progressieve voorbeelden:
Onderdeel | Focus | Wat u leert |
---|---|---|
Deel 1: Hallo wereld | Basiswidget maken | Een widget maken waarmee tekst wordt weergegeven |
Deel 2: REST API-integratie | Azure DevOps-API-aanroepen | REST API-functionaliteit toevoegen om gegevens op te halen en weer te geven |
Deel 3: Widgetconfiguratie | Aanpassing van gebruikers | Configuratieopties voor uw widget implementeren |
Aanbeveling
Sla de zelfstudie over: Download de volledige voorbeeldextensie, ga naar de widgets
map en ga naar stap 6 om drie kant-en-klare voorbeeldwidgets te publiceren.
Bekijk voordat u begint de basiswidgetstijlen en structurele richtlijnen die we bieden.
Deel 1: Hallo wereld
Maak een basiswidget die 'Hallo wereld' weergeeft met behulp van JavaScript. Deze basis demonstreert de basisconcepten voor widgetontwikkeling.
Stap 1: de client-SDK installeren
Met de VSS SDK kan uw widget communiceren met Azure DevOps. Installeer deze met behulp van npm:
npm install vss-web-extension-sdk
Kopieer het VSS.SDK.min.js
bestand van vss-web-extension-sdk/lib
naar uw home/sdk/scripts
map.
Zie de GitHub-pagina client-SDK voor meer SDK-documentatie.
Stap 2: de HTML-structuur maken
Maak hello-world.html
in uw projectmap. Dit bestand biedt de indeling en verwijzingen naar de vereiste scripts van de widget.
<!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>
Widgets worden uitgevoerd in iframes, dus de meeste HTML-hoofdelementen behalve <script>
en <link>
worden genegeerd door het framework.
Stap 3: Widget JavaScript toevoegen
Als u de widgetfunctionaliteit wilt implementeren, voegt u dit script toe aan de <head>
sectie van uw HTML-bestand:
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/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>
Belangrijke JavaScript-onderdelen
Functie | Doel |
---|---|
VSS.init() |
Initialiseert de communicatie tussen widget en Azure DevOps |
VSS.require() |
Het laden van de vereiste SDK-bibliotheken en widgethelpers |
VSS.register() |
Registreert uw widget met een unieke id |
WidgetHelpers.IncludeWidgetStyles() |
Standaardstijl van Azure DevOps wordt toegepast |
VSS.notifyLoadSucceeded() |
Hiermee wordt het framework geïnformeerd dat het laden succesvol is voltooid. |
Belangrijk
De widgetnaam in VSS.register()
moet overeenkomen met de id
in uw extensiemanifest (stap 5).
Stap 4: Extensie-installatiekopieën toevoegen
Maak de vereiste installatiekopieën voor uw extensie:
-
Extensielogo: 98x98 pixel afbeelding met de naam
logo.png
in deimg
map -
Widgetcataloguspictogram: afbeelding van 98x98 pixel met de naam
CatalogIcon.png
in deimg
map -
Widgetvoorbeeld: afbeelding van 330x160 pixel met de naam
preview.png
in deimg
map
Deze afbeeldingen worden weergegeven in de Marketplace- en widgetcatalogus wanneer gebruikers door beschikbare extensies bladeren.
Stap 5: Het extensiemanifest maken
Maak vss-extension.json
in de hoofdmap van uw project. Dit bestand definieert de metagegevens en bijdragen van uw extensie:
{
"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
}
]
}
Belangrijk
Vervang door "publisher": "fabrikam"
de werkelijke naam van de uitgever. Leer hoe je een uitgever aanmaakt.
Essentiële manifesteigenschappen
Afdeling | Doel |
---|---|
Basisinformatie | Extensienaam, versie, beschrijving en uitgever |
pictogrammen | Paden naar de visuele assets van uw extensie |
Bijdragen | Widgetdefinities, waaronder id, type en eigenschappen |
bestanden | Alle bestanden die moeten worden opgenomen in het extensiepakket |
Zie referentiemateriaal voor uitbreidingsmanifesten voor volledige documentatie over manifesten.
Stap 6: Uw extensie inpakken en publiceren
Pak uw extensie in en publiceer deze naar Visual Studio Marketplace.
Het pakketprogramma installeren
npm i -g tfx-cli
Uw uitbreidingspakket maken
Voer vanuit uw projectmap het volgende uit:
tfx extension create --manifest-globs vss-extension.json
Met deze actie maakt u een .vsix
bestand met de verpakte extensie.
Een uitgever instellen
- Ga naar de Visual Studio Marketplace-publicatieportal.
- Meld u aan en maak een uitgever als u er nog geen hebt.
- Kies een unieke uitgever-id (gebruikt in uw manifestbestand).
- Werk uw
vss-extension.json
bij om uw uitgeversnaam te gebruiken in plaats van "fabrikam."
Uw extensie uploaden
- Selecteer in de publicatieportal de optie Nieuwe extensie uploaden.
- Kies uw
.vsix
bestand en upload het. - Deel de extensie met uw Azure DevOps-organisatie.
U kunt ook de opdrachtregel gebruiken:
tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization
Aanbeveling
Hiermee --rev-version
kunt u het versienummer automatisch verhogen bij het bijwerken van een bestaande extensie.
Stap 7: Uw widget installeren en testen
Als u wilt testen, voegt u uw widget toe aan een dashboard:
- Ga naar uw Azure DevOps-project:
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Ga naar Overzicht>Dashboards.
- Selecteer Een widget toevoegen.
- Zoek uw widget in de catalogus en selecteer Toevoegen.
De widget Hallo wereld wordt weergegeven op het dashboard, met de tekst die u hebt geconfigureerd.
Volgende stap: Ga verder met deel 2 om te leren hoe u Azure DevOps REST API's integreert in uw widget.
Deel 2: Hallo wereld met Azure DevOps REST API
Breid uw widget uit om te communiceren met Azure DevOps-gegevens met behulp van REST API's. In dit voorbeeld ziet u hoe u querygegevens ophaalt en dynamisch weergeeft in uw widget.
In dit deel gebruikt u de REST API voor het bijhouden van werkitems om informatie over een bestaande query op te halen en de querygegevens weer te geven onder de tekst 'Hallo wereld'.
Stap 1: het verbeterde HTML-bestand maken
Maak een nieuw widgetbestand dat voortbouwt op het vorige voorbeeld. Kopieer hello-world.html
en wijzig de naam ervan in hello-world2.html
. Uw projectstructuur bevat nu:
|--- README.md
|--- node_modules
|--- sdk/
|--- scripts/
|--- VSS.SDK.min.js
|--- img/
|--- logo.png
|--- scripts/
|--- hello-world.html // Part 1 widget
|--- hello-world2.html // Part 2 widget (new)
|--- vss-extension.json // Extension manifest
De HTML-structuur van de widget bijwerken
Breng deze wijzigingen aan in hello-world2.html
:
-
Voeg een container toe voor querygegevens: Voeg een nieuw
<div>
element toe om querygegevens weer te geven. -
Werk de widget-id bij: wijzig de widgetnaam van
HelloWorldWidget
inHelloWorldWidget2
voor unieke identificatie.
<!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"], 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: API-toegangsmachtigingen configureren
Voordat u REST API-aanroepen maakt, configureert u de vereiste machtigingen in uw extensiemanifest.
Het werkbereik toevoegen
Het vso.work
bereik verleent alleen-lezentoegang tot werkitems en query's. Voeg dit bereik toe aan uw vss-extension.json
:
{
"scopes": [
"vso.work"
]
}
Voorbeeld van het volledige manifest
Voor een volledig manifest met andere eigenschappen structureert u het als volgt:
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
Belangrijk
Bereikbeperkingen: Het toevoegen of wijzigen van scopes na publicatie wordt niet ondersteund. Als u uw extensie al hebt gepubliceerd, moet u deze eerst verwijderen uit Marketplace. Ga naar de Visual Studio Marketplace-publicatieportal, zoek uw extensie en selecteer Verwijderen.
Stap 3: REST API-integratie implementeren
Azure DevOps biedt JavaScript REST-clientbibliotheken via de SDK. Deze bibliotheken verpakken AJAX-aanroepen en wijzen API-antwoorden toe aan bruikbare objecten.
De widget JavaScript bijwerken
Vervang de VSS.require
aanroep in uw hello-world2.html
om de Work Item Tracking REST-client te omvatten:
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
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 WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Process query data (implemented in Step 4)
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();
});
Belangrijke implementatiedetails
Onderdeel | Doel |
---|---|
WorkItemTrackingRestClient.getClient() |
Hiermee haalt u een exemplaar op van de REST-client voor het bijhouden van werkitems |
getQuery() |
Haalt querygegevens op die zijn verpakt in een belofte |
WidgetStatusHelper.Failure() |
Biedt consistente foutafhandeling voor widgetfouten |
projectId |
Huidige projectcontext vereist voor API-aanroepen |
Aanbeveling
Aangepaste querypaden: Als u geen "Feedback"-query in "Gedeelde query's" hebt, vervang "Shared Queries/Feedback"
door het pad naar een query die in uw project bestaat.
Stap 4: API-antwoordgegevens weergeven
Geef de querygegevens in uw widget weer door het REST API-antwoord te verwerken.
Querygegevensweergave toevoegen
Vervang de // Process query data
opmerking door deze implementatie:
// 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);
De getQuery()
methode retourneert een Contracts.QueryHierarchyItem
object met eigenschappen voor querymetagegevens. In dit voorbeeld worden drie belangrijke stukjes informatie weergegeven onder de tekst 'Hallo wereld'.
Volledig werkvoorbeeld
Het uiteindelijke hello-world2.html
bestand moet er als volgt uitzien:
<!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, WorkItemTrackingRestClient) {
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 WorkItemTrackingRestClient.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: Het extensiemanifest bijwerken
Als u deze beschikbaar wilt maken in de widgetcatalogus, voegt u uw nieuwe widget toe aan het extensiemanifest.
De tweede widgetbijdrage toevoegen
Werk vss-extension.json
bij om uw widget dat is uitgerust met REST API op te nemen. Voeg deze bijdrage toe aan de contributions
matrix:
{
"contributions": [
// ...existing HelloWorldWidget contribution...,
{
"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"
]
}
Aanbeveling
Voorbeeldafbeelding: Maak een preview2.png
afbeelding (330x160 pixels) en plaats deze in de img
map om gebruikers te laten zien hoe uw widget eruitziet in de catalogus.
Stap 6: Verpakken, publiceren en delen
Uw extensie verpakken, publiceren en delen. Als u de extensie al hebt gepubliceerd, kunt u deze opnieuw verpakken en bijwerken in Marketplace.
Stap 7: Uw REST API-widget testen
Als u de INTEGRATIE van de REST API in actie wilt weergeven, voegt u de nieuwe widget toe aan uw dashboard:
- Ga naar uw Azure DevOps-project:
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Selecteer Overzicht>Dashboards.
- Selecteer Een widget toevoegen.
- Zoek 'Hello World Widget 2 (met API)' en selecteer Toevoegen.
In uw verbeterde widget worden zowel de tekst 'Hallo wereld' als de livequerygegevens van uw Azure DevOps-project weergegeven.
Volgende stappen: Ga verder met deel 3 om configuratieopties toe te voegen waarmee gebruikers kunnen aanpassen welke query moet worden weergegeven.
Deel 3: Hallo wereld configureren
Bouw voort op deel 2 door gebruikersconfiguratiemogelijkheden toe te voegen aan uw widget. In plaats van het querypad hard te coderen, maakt u een configuratie-interface waarmee gebruikers kunnen selecteren welke query moet worden weergegeven, met live preview-functionaliteit.
In dit deel ziet u hoe u configureerbare widgets maakt die gebruikers kunnen aanpassen aan hun specifieke behoeften en tegelijkertijd realtime feedback kunnen geven tijdens de configuratie.
Stap 1: Configuratiebestanden maken
Widgetconfiguraties delen veel overeenkomsten met widgets zelf: beide gebruiken dezelfde SDK-, HTML-structuur en JavaScript-patronen, maar dienen verschillende doeleinden binnen het extensieframework.
De projectstructuur instellen
Maak twee nieuwe bestanden om de widgetconfiguratie te ondersteunen:
- Kopieer
hello-world2.html
en wijzig de naam ervan inhello-world3.html
, uw configureerbare widget. - Maak een nieuw bestand met de naam
configuration.html
, waarmee de configuratie-interface wordt verwerkt.
Uw projectstructuur bevat nu:
|--- README.md
|--- sdk/
|--- node_modules
|--- scripts/
|--- VSS.SDK.min.js
|--- img/
|--- logo.png
|--- scripts/
|--- configuration.html // New: Configuration interface
|--- hello-world.html // Part 1: Basic widget
|--- hello-world2.html // Part 2: REST API widget
|--- hello-world3.html // Part 3: Configurable widget (new)
|--- vss-extension.json // Extension manifest
De configuratie-interface maken
Voeg deze HTML-structuur toe aan configuration.html
, waarmee een vervolgkeuzelijst wordt gemaakt voor het kiezen van query's:
<!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: Configuratie van JavaScript implementeren
De configuratie van JavaScript volgt hetzelfde initialisatiepatroon als widgets, maar implementeert het IWidgetConfiguration
contract in plaats van het basiscontract IWidget
.
Configuratielogica toevoegen
Voeg dit script in de <head>
sectie van configuration.html
:
<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>
Details van configuratiecontract
Voor het IWidgetConfiguration
contract zijn de volgende belangrijke functies vereist:
Functie | Doel | Wanneer aangeroepen |
---|---|---|
load() |
De gebruikersinterface van de configuratie initialiseren met bestaande instellingen | Wanneer het configuratiedialoogvenster wordt geopend |
onSave() |
Gebruikersinvoer serialiseren en instellingen valideren | Wanneer de gebruiker Opslaan selecteert |
Aanbeveling
Gegevensserialisatie: in dit voorbeeld wordt JSON gebruikt om instellingen te serialiseren. De widget heeft toegang tot deze instellingen via widgetSettings.customSettings.data
en moet ze dienovereenkomstig deserialiseren.
Stap 3: Live preview-functionaliteit inschakelen
Met livevoorbeeld kunnen gebruikers widgetwijzigingen onmiddellijk zien wanneer ze configuratie-instellingen wijzigen, zodat ze direct feedback kunnen geven voordat ze deze opslaan.
Wijzigingsmeldingen implementeren
Als u livevoorbeeld wilt inschakelen, voegt u deze gebeurtenis-handler toe binnen de load
functie:
$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);
});
Configuratiebestand voltooien
Uw laatste configuration.html
moet er als volgt uitzien:
<!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>
Belangrijk
Knop Opslaan inschakelen: Voor het framework is ten minste één melding voor configuratiewijzigingen vereist om de knop Opslaan in te schakelen. De gebeurtenis-handler voor wijzigingen zorgt ervoor dat deze actie plaatsvindt wanneer gebruikers een optie selecteren.
Stap 4: de widget configureerbaar maken
Transformeer uw widget van deel 2 om configuratiegegevens te gebruiken in plaats van vastgelegde waarden. Voor deze stap is het implementeren van het IConfigurableWidget
contract vereist.
Widgetregistratie bijwerken
Breng hello-world3.html
de volgende wijzigingen aan:
-
Widget-id bijwerken: van naar
HelloWorldWidget2
HelloWorldWidget3
. -
Voeg de functie voor opnieuw laden toe: Implementeer het
IConfigurableWidget
contract.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Configuratiegegevens verwerken
Werk de getQueryInfo
functie bij om configuratie-instellingen te gebruiken in plaats van in code vastgelegde querypaden:
var settings = JSON.parse(widgetSettings.customSettings.data);
if (!settings || !settings.queryPath) {
var $container = $('#query-info-container');
$container.empty();
$container.text("Please configure a query path to display data.");
return WidgetHelpers.WidgetStatusHelper.Success();
}
Verschillen in levenscyclus van widget
Functie | Doel | Gebruiksrichtlijnen |
---|---|---|
load() |
Eerste widgetweergave en eenmalige installatie | Zware bewerkingen, resource-initialisatie |
reload() |
Widget bijwerken met nieuwe configuratie | Lichtgewicht updates, gegevensvernieuwing |
Aanbeveling
Optimalisatie van prestaties: gebruik load()
deze functie voor dure bewerkingen die slechts één keer hoeven te worden uitgevoerd en reload()
voor snelle updates wanneer de configuratie verandert.
(Optioneel) Een lightbox toevoegen voor gedetailleerde informatie
Dashboardwidgets hebben beperkte ruimte, waardoor het lastig is om uitgebreide informatie weer te geven. Een lightbox biedt een elegante oplossing door gedetailleerde gegevens weer te geven in een modale overlay zonder weg te navigeren van het dashboard.
Waarom een lightbox gebruiken in widgets?
Voordeel | Beschrijving |
---|---|
Ruimte-efficiëntie | Widget compact houden terwijl u gedetailleerde weergaven biedt |
Gebruikerservaring | Dashboardcontext onderhouden terwijl meer informatie wordt weergegeven |
progressieve openbaarmaking | Samenvattingsgegevens weergeven in widget, details op aanvraag |
Responsief ontwerp | Aanpassen aan verschillende schermgrootten en widgetconfiguraties |
Klikbare elementen implementeren
Werk de rendering van uw querygegevens bij om klikbare elementen op te nemen die het lightbox activeren:
// Create a list with clickable query details
var $list = $('<ul class="query-summary">');
$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>"));
// Add a clickable element to open detailed view
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
// Append to the container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
De lightbox-functionaliteit maken
Voeg deze lightbox-implementatie toe aan uw widget JavaScript:
function showQueryDetails(query) {
// Create lightbox overlay
var $overlay = $('<div class="lightbox-overlay">');
var $lightbox = $('<div class="lightbox-content">');
// Add close button
var $closeBtn = $('<button class="lightbox-close">×</button>');
$closeBtn.on('click', function() {
$overlay.remove();
});
// Create detailed content
var $content = $('<div class="query-details">');
$content.append($('<h3>').text(query.name || 'Query Details'));
$content.append($('<p>').html('<strong>ID:</strong> ' + query.id));
$content.append($('<p>').html('<strong>Path:</strong> ' + query.path));
$content.append($('<p>').html('<strong>Created:</strong> ' + (query.createdDate ? new Date(query.createdDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Modified:</strong> ' + (query.lastModifiedDate ? new Date(query.lastModifiedDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Created By:</strong> ' + (query.createdBy ? query.createdBy.displayName : 'Unknown')));
$content.append($('<p>').html('<strong>Modified By:</strong> ' + (query.lastModifiedBy ? query.lastModifiedBy.displayName : 'Unknown')));
if (query.queryType) {
$content.append($('<p>').html('<strong>Type:</strong> ' + query.queryType));
}
// Assemble lightbox
$lightbox.append($closeBtn);
$lightbox.append($content);
$overlay.append($lightbox);
// Add to document and show
$('body').append($overlay);
// Close on overlay click
$overlay.on('click', function(e) {
if (e.target === $overlay[0]) {
$overlay.remove();
}
});
// Close on Escape key
$(document).on('keydown.lightbox', function(e) {
if (e.keyCode === 27) { // Escape key
$overlay.remove();
$(document).off('keydown.lightbox');
}
});
}
Lightbox-stijl toevoegen
Neem CSS-stijlen voor het lightbox op in de HTML-sectie <head>
van uw widget:
<style>
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
Verbeterde widget-implementatie
Uw volledige verbeterde widget met lightbox-functionaliteit:
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<style>
/* Lightbox styles from above */
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
WidgetHelpers.IncludeWidgetStyles();
function showQueryDetails(query) {
// Lightbox implementation from above
}
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
return WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Enhanced display with lightbox trigger
var $list = $('<ul class="query-summary">');
$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>")));
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
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();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
Overwegingen voor toegankelijkheid: Zorg ervoor dat uw lightbox toegankelijk is en de juiste labels voor schermlezers bevat. Test met de ingebouwde toegankelijkheidsfuncties van Azure DevOps.
Belangrijk
Prestaties: Lightboxes zouden snel moeten laden. Overweeg alleen gedetailleerde gegevens te laden wanneer de lightbox wordt geopend, in plaats van alles vooraf op te halen.
Stap 5: Het extensiemanifest configureren
Registreer zowel de configureerbare widget als de configuratie-interface in uw extensiemanifest.
Widget- en configuratiebijdragen toevoegen
Werk vss-extension.json
bij om twee nieuwe bijdragen op te nemen:
{
"contributions": [
// ...existing 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
}
]
}
Vereisten voor configuratiebijdrage
Eigenschappen | Doel | Vereiste waarde |
---|---|---|
type |
Identificeert bijdrage als widgetconfiguratie | ms.vss-dashboards-web.widget-configuration |
targets |
Waar de configuratie wordt weergegeven | ms.vss-dashboards-web.widget-configuration |
uri |
Pad naar HTML-configuratiebestand | Het pad naar het configuratiebestand |
Widgetdoelpatroon
Voor configureerbare widgets moet de targets
matrix een verwijzing naar de configuratie bevatten:
<publisher>.<extension-id>.<configuration-id>
Waarschuwing
Zichtbaarheid van de configuratieknop: als de widget niet juist is gericht op de configuratiebijdrage, wordt de knop Configureren niet weergegeven. Controleer of de uitgevers- en extensienamen exact overeenkomen met uw manifest.
Stap 6: Verpakken, publiceren en delen
Implementeer uw verbeterde extensie met configuratiemogelijkheden.
Als dit uw eerste publicatie is, volgt u stap 6: Verpakken, publiceren en delen. Voor bestaande extensies kunt u deze rechtstreeks in Marketplace opnieuw verpakken en bijwerken.
Stap 7: De configureerbare widget testen
Ervaar de volledige configuratiewerkstroom door uw widget toe te voegen en te configureren.
De widget toevoegen aan uw dashboard
- Ga naar
https://dev.azure.com/{Your_Organization}/{Your_Project}
. - Ga naar Overzicht>Dashboards.
- Selecteer Een widget toevoegen.
- Zoek 'Hallo wereldwidget 3 (met configuratie)' en selecteer Toevoegen.
Er wordt een configuratieprompt weergegeven omdat de widget setup vereist:
De widget configureren
Toegangsconfiguratie via een van beide methoden:
- Widgetmenu: Beweeg de muisaanwijzer over de widget, selecteer het beletselteken (⋯) en configureer
- Dashboardbewerkingsmodus: Selecteer Bewerken op het dashboard en vervolgens de knop Configureren in de widget
Het configuratievenster opent met een live-voorbeeld in het midden. Selecteer een query in de vervolgkeuzelijst om directe updates weer te geven en selecteer Opslaan om uw wijzigingen toe te passen.
Stap 8: Geavanceerde configuratieopties toevoegen
Breid uw widget uit met meer ingebouwde configuratiefuncties, zoals aangepaste namen en grootten.
Configuratie van naam en grootte inschakelen
Azure DevOps biedt twee configureerbare functies die standaard beschikbaar zijn:
Eigenschap | Eigenschap Manifest | Doel |
---|---|---|
Aangepaste namen | isNameConfigurable: true |
Gebruikers kunnen de standaardwidgetnaam overschrijven |
Meerdere grootten | Meerdere supportedSizes vermeldingen |
Gebruikers kunnen het formaat van widgets wijzigen |
Voorbeeld van uitgebreid manifest
{
"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"]
}
}
]
}
Geconfigureerde namen weergeven
Als u aangepaste widgetnamen wilt weergeven, werkt u de widget bij voor gebruik widgetSettings.name
:
return {
load: function (widgetSettings) {
// Display configured name instead of hard-coded text
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
// Update name during configuration changes
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
}
}
Nadat u de extensie hebt bijgewerkt, kunt u zowel de widgetnaam als de grootte configureren:
Uw extensie opnieuw verpakken en bijwerken om deze geavanceerde configuratieopties in te schakelen.
Gefeliciteerd! U hebt een volledige, configureerbare Azure DevOps-dashboardwidget gemaakt met mogelijkheden voor livevoorbeelden en opties voor het aanpassen van gebruikers.