Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022 | Azure DevOps Server 2020
Widgets werden als Beiträge im Erweiterungsrahmen implementiert. Eine einzelne Erweiterung kann mehrere Widgetbeiträge enthalten. In diesem Artikel wird gezeigt, wie Sie eine Erweiterung erstellen, die mindestens ein Widget bereitstellt.
Tipp
Sehen Sie sich unsere neuste Dokumentation zur Erweiterungsentwicklung mithilfe des Azure DevOps Erweiterungs-SDK an.
Tipp
Wenn Sie eine neue Azure DevOps-Erweiterung starten, probieren Sie diese verwalteten Beispielsammlungen zuerst aus – sie arbeiten mit aktuellen Produktbuilds und behandeln moderne Szenarien (z. B. Hinzufügen von Registerkarten auf Pullanforderungsseiten).
- Azure DevOps-Erweiterungsbeispiel (GitHub) – ein kompaktes Startbeispiel, das allgemeine Erweiterungsmuster veranschaulicht: https://github.com/microsoft/azure-devops-extension-sample
- Azure DevOps-Erweiterungsbeispiele (Legacysammlungs- und Beitragsleitfaden) – Installieren, um UI-Ziele zu prüfen oder die Quelle anzuzeigen: https://marketplace.visualstudio.com/items/ms-samples.samples-contributions-guide und https://github.com/Microsoft/vso-extension-samples/tree/master/contributions-guide
- Microsoft Learn-Beispiele (Azure DevOps-Beispiele durchsuchen) – kuratierte, aktuelle Beispiele in Dokumenten von Microsoft: /samples/browse/?terms=azure%20devops%20extension
Wenn ein Beispiel in Ihrer Organisation nicht funktioniert, installieren Sie es in einer persönlichen oder Testorganisation und vergleichen Sie die Ziel-IDs und API-Versionen des Erweiterungsmanifests mit den aktuellen Dokumenten. Weitere Informationen und APIs finden Sie unter:
Voraussetzungen
| Anforderung | Beschreibung |
|---|---|
| Programmierkenntnisse | JavaScript-, HTML- und CSS-Kenntnisse für die Widgetentwicklung |
| Azure DevOps Organisation | Eine Organisation erstellen |
| Texteditor | Wir verwenden Visual Studio Code für Lernprogramme |
| Node.js | Neueste Version von Node.js |
| Plattformübergreifende CLI |
tfx-cli zum Verpacken von Erweiterungen Installieren mit: npm i -g tfx-cli |
| Projektverzeichnis | Startverzeichnis mit dieser Struktur nach Abschluss des Tutorials:|--- 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 |
Übersicht über das Tutorial
In diesem Lernprogramm lernen Sie die Widgetentwicklung anhand von drei progressiven Beispielen kennen:
| Teil | Fokus | Lerninhalt |
|---|---|---|
| Teil 1: Hello World | Einfache Widgeterstellung | Erstellen eines Widgets, das Text anzeigt |
| Teil 2: REST-API-Integration | Azure DevOps-API-Aufrufe | Hinzufügen von REST-API-Funktionen zum Abrufen und Anzeigen von Daten |
| Teil 3: Widgetkonfiguration | Benutzeranpassung | Implementieren von Konfigurationsoptionen für Ihr Widget |
Tipp
Wenn Sie lieber direkt zu funktionierenden Beispielen springen möchten, zeigen die enthaltenen Beispiele (siehe vorherige Notiz) eine Reihe von Widgets an, die Sie packen und veröffentlichen können.
Bevor Sie beginnen, überprüfen Sie die grundlegenden Widgetstile und die strukturelle Anleitungen, die wir bereitstellen.
Teil 1: Hallo Welt
Erstellen Sie ein einfaches Widget, das "Hello World" mit JavaScript anzeigt. Diese Stiftung veranschaulicht die Kernkonzepte für die Widgetentwicklung.
Schritt 1: Installieren des Client-SDK
Mit dem VSS SDK kann Ihr Widget mit Azure DevOps kommunizieren. Installieren Sie es mithilfe von npm:
npm install vss-web-extension-sdk
Kopieren Sie die VSS.SDK.min.js Datei von vss-web-extension-sdk/lib in Ihren home/sdk/scripts Ordner.
Weitere SDK-Dokumentation finden Sie auf der GitHub-Seite des Client SDK.
Schritt 2: Erstellen der HTML-Struktur
Erstellen Sie hello-world.html in Ihrem Projektverzeichnis. Diese Datei stellt das Layout des Widgets und Verweise auf erforderliche Skripts bereit.
<!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 werden in iframes ausgeführt, sodass die meisten HTML-Kopfelemente außer <script> und <link> vom Framework ignoriert werden.
Schritt 3: Hinzufügen von Widget-JavaScript
Um die Widgetfunktion zu implementieren, fügen Sie dieses Skript dem <head> Abschnitt Ihrer HTML-Datei hinzu:
<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>
Wichtige JavaScript-Komponenten
| Funktion | Zweck |
|---|---|
VSS.init() |
Initialisiert die Kommunikation zwischen Widget und Azure DevOps |
VSS.require() |
Lädt erforderliche SDK-Bibliotheken und Widget-Hilfsprogramme |
VSS.register() |
Registriert Ihr Widget mit einem eindeutigen Bezeichner. |
WidgetHelpers.IncludeWidgetStyles() |
Wendet die standardmäßige Azure DevOps-Formatierung an. |
VSS.notifyLoadSucceeded() |
Benachrichtigt das Framework, dass das Laden erfolgreich abgeschlossen wurde. |
Von Bedeutung
Der Name des Widgets in VSS.register() muss mit dem id in Ihrem Erweiterungsmanifest übereinstimmen (Schritt 5).
Schritt 4: Hinzufügen von Erweiterungsbildern
Erstellen Sie die erforderlichen Bilder für Ihre Erweiterung:
-
Erweiterungslogo: Bild mit 98x98 Pixeln
logo.pngimimgOrdner -
Widget-Katalogsymbol: Bild mit 98x98 Pixeln
CatalogIcon.pngin demimgOrdner -
Widgetvorschau: Bild mit 330 x 160 Pixeln
preview.pngimimgOrdner
Diese Bilder werden im Marketplace- und Widget-Katalog angezeigt, wenn Benutzer verfügbare Erweiterungen durchsuchen.
Schritt 5: Erstellen des Erweiterungsmanifests
Erstellen Sie vss-extension.json im Stammverzeichnis Ihres Projekts. Diese Datei definiert die Metadaten und Beiträge Ihrer Erweiterung:
{
"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
}
]
}
Von Bedeutung
Ersetzen Sie "publisher": "fabrikam" durch Ihren tatsächlichen Herausgebernamen. Erfahren Sie, wie Sie einen Herausgeber erstellen.
Wesentliche Manifesteigenschaften
| `Section` | Zweck |
|---|---|
| Grundlegende Informationen | Erweiterungsname, Version, Beschreibung und Herausgeber |
| Symbole | Pfade zu den visuellen Ressourcen Ihrer Erweiterung |
| Beiträge | Widgetdefinitionen einschließlich ID, Typ und Eigenschaften |
| Dateien | Alle Dateien, die in das Erweiterungspaket eingeschlossen werden sollen |
Vollständige Manifestdokumentation finden Sie in der Erweiterungsmanifestreferenz.
Schritt 6: Packen und Veröffentlichen Ihrer Erweiterung
Packen Sie Ihre Erweiterung, und veröffentlichen Sie sie im Visual Studio Marketplace.
Verpackungstool installieren
npm i -g tfx-cli
Erstellen Sie Ihr Erweiterungspaket
Führen Sie den folgenden Befehl in Ihrem Projektverzeichnis aus:
tfx extension create --manifest-globs vss-extension.json
Mit dieser Aktion wird eine .vsix Datei erstellt, die die paketierte Erweiterung enthält.
Einrichten eines Herausgebers
- Wechseln Sie zum Visual Studio Marketplace-Veröffentlichungsportal.
- Melden Sie sich an, und erstellen Sie einen Herausgeber, wenn Sie keinen haben.
- Wählen Sie einen eindeutigen Herausgeberbezeichner (verwendet in Ihrer Manifestdatei) aus.
- Aktualisieren Sie Ihr
vss-extension.json, um Ihren Herausgebernamen anstelle von "fabrikam" zu verwenden.
Hochladen Ihrer Erweiterung
- Wählen Sie im Veröffentlichungsportal " Neue Erweiterung hochladen" aus.
- Wählen Sie Ihre
.vsixDatei aus, und laden Sie sie hoch. - Teilen Sie die Erweiterung mit Ihrer Azure DevOps-Organisation.
Alternativ können Sie die Befehlszeile verwenden:
tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization
Tipp
Verwenden Sie --rev-version, um die Versionsnummer automatisch zu erhöhen, wenn Sie eine vorhandene Erweiterung aktualisieren.
Schritt 7: Installieren und Testen Ihres Widgets
Zum Testen fügen Sie Ihr Widget zu einem Dashboard hinzu:
- Wechseln Sie zu Ihrem Azure DevOps-Projekt:
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Wechseln Sie zu Übersicht>Dashboards.
- Wählen Sie "Widget hinzufügen" aus.
- Suchen Sie Ihr Widget im Katalog, und wählen Sie "Hinzufügen" aus.
Ihr Widget "Hello World" wird auf dem Dashboard angezeigt und zeigt den von Ihnen konfigurierten Text an.
Nächster Schritt: Fahren Sie mit Teil 2 fort, um zu erfahren, wie Sie Azure DevOps-REST-APIs in Ihr Widget integrieren.
Teil 2: Hallo Welt mit der Azure DevOps-REST-API
Erweitern Sie Ihr Widget, um mit Azure DevOps-Daten mithilfe von REST-APIs zu interagieren. In diesem Beispiel wird veranschaulicht, wie Abfrageinformationen abgerufen und dynamisch in Ihrem Widget angezeigt werden.
Verwenden Sie in diesem Teil die REST-API für die Arbeitsaufgabenverfolgung , um Informationen zu einer vorhandenen Abfrage abzurufen und die Abfragedetails unter dem Text "Hello World" anzuzeigen.
Schritt 1: Erstellen der erweiterten HTML-Datei
Erstellen Sie eine neue Widgetdatei, die auf dem vorherigen Beispiel basiert. Kopieren Sie hello-world.html und benennen Sie es in hello-world2.html um. Ihre Projektstruktur umfasst jetzt Folgendes:
|--- 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
Aktualisieren der HTML-Struktur des Widgets
Nehmen Sie diese Änderungen an hello-world2.html vor:
-
Fügen Sie einen Container für Abfragedaten hinzu: Fügen Sie ein neues
<div>Element zum Anzeigen von Abfrageinformationen hinzu. -
Aktualisieren Sie den Widgetbezeichner: Ändern Sie den Widgetnamen von
HelloWorldWidgetzuHelloWorldWidget2"Zur eindeutigen Identifikation".
<!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>
Schritt 2: Konfigurieren von API-Zugriffsberechtigungen
Konfigurieren Sie vor dem Ausführen von REST-API-Aufrufen die erforderlichen Berechtigungen in Ihrem Erweiterungsmanifest.
Hinzufügen des Arbeitsumfangs
Die vso.work scope grants read-only access to work items and queries. Fügen Sie diesen Reservierungsumfang Ihrem vss-extension.json zu:
{
"scopes": [
"vso.work"
]
}
Vollständiges Manifestbeispiel
Strukturieren Sie es wie folgt für ein vollständiges Manifest mit anderen Eigenschaften:
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
Von Bedeutung
Bereichseinschränkungen: Das Hinzufügen oder Ändern von Bereichen nach der Veröffentlichung wird nicht unterstützt. Wenn Sie Ihre Erweiterung bereits veröffentlicht haben, müssen Sie sie zuerst aus dem Marketplace entfernen. Wechseln Sie zum Visual Studio Marketplace-Veröffentlichungsportal, suchen Sie Ihre Erweiterung, und wählen Sie "Entfernen" aus.
Schritt 3: Implementieren der REST-API-Integration
Azure DevOps stellt JavaScript-REST-Clientbibliotheken über das SDK bereit. Diese Bibliotheken umschließen AJAX-Aufrufe und ordnen API-Antworten verwendbaren Objekten zu.
Aktualisieren des Widgets JavaScript
Ersetzen Sie den VSS.require Aufruf in Ihrem hello-world2.html, um den REST-Client für die Aufgabenverfolgung einzuschließen.
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();
});
Wichtige Implementierungsdetails
| Komponente | Zweck |
|---|---|
WorkItemTrackingRestClient.getClient() |
Ruft eine Instanz des REST-Clients für die Arbeitsaufgabenverfolgung ab |
getQuery() |
Ruft Abfrageinformationen ab, die in eine Zusage eingeschlossen sind |
WidgetStatusHelper.Failure() |
Stellt eine konsistente Fehlerbehandlung für Widgetfehler bereit. |
projectId |
Aktueller Projektkontext erforderlich für API-Aufrufe |
Tipp
Benutzerdefinierte Abfragepfade: Wenn Sie keine "Feedback"-Abfrage in "Freigegebene Abfragen" haben, ersetzen Sie "Shared Queries/Feedback" den Pfad zu einer Abfrage, die in Ihrem Projekt vorhanden ist.
Schritt 4: Anzeigen von API-Antwortdaten
Rendern Sie die Abfrageinformationen in Ihrem Widget, indem Sie die REST-API-Antwort verarbeiten.
Hinzufügen des Renderns von Abfragedaten
Ersetzen Sie den // Process query data Kommentar durch diese Implementierung:
// 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);
Die getQuery() Methode gibt ein Contracts.QueryHierarchyItem Objekt mit Eigenschaften für Abfragemetadaten zurück. In diesem Beispiel werden drei wichtige Informationen unterhalb des Texts "Hello World" angezeigt.
Vollständiges Arbeitsbeispiel
Die endgültige hello-world2.html Datei sollte wie folgt aussehen:
<!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>
Schritt 5: Aktualisieren des Erweiterungsmanifests
Um es im Widgetkatalog verfügbar zu machen, fügen Sie Ihr neues Widget dem Erweiterungsmanifest hinzu.
Hinzufügen des zweiten Widgetbeitrags
Aktualisieren Sie vss-extension.json , um Ihr REST-API-fähiges Widget einzuschließen. Fügen Sie diesen Beitrag in das Array contributions hinzu.
{
"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"
]
}
Tipp
Vorschaubild: Erstellen Sie ein preview2.png Bild (330 x 160 Pixel), und platzieren Sie es im img Ordner, um Benutzern anzuzeigen, wie Ihr Widget im Katalog aussieht.
Schritt 6: Packen, Veröffentlichen und Freigeben
Verpacken, veröffentlichen und teilen Sie Ihre Erweiterung. Wenn Sie die Erweiterung bereits veröffentlicht haben, können Sie sie direkt im Marketplace umpacken und aktualisieren.
Schritt 7: Testen Des REST-API-Widgets
Um die REST-API-Integration in Aktion anzuzeigen, fügen Sie das neue Widget zu Ihrem Dashboard hinzu:
- Wechseln Sie zu Ihrem Azure DevOps-Projekt:
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Wählen Sie Übersicht>Dashboards aus.
- Wählen Sie "Widget hinzufügen" aus.
- Suchen Sie "Hello World Widget 2 (mit API)", und wählen Sie "Hinzufügen" aus.
Ihr erweitertes Widget zeigt sowohl den Text "Hello World" als auch Liveabfrageinformationen aus Ihrem Azure DevOps-Projekt an.
Nächste Schritte: Fahren Sie mit Teil 3 fort, um Konfigurationsoptionen hinzuzufügen, mit denen Benutzer anpassen können, welche Abfrage angezeigt werden soll.
Teil 3: Konfigurieren von Hallo Welt
Bauen Sie auf Teil 2 auf, indem Sie Ihrem Widget Benutzerkonfigurationsfunktionen hinzufügen. Anstatt den Abfragepfad hartcodieren zu müssen, erstellen Sie eine Konfigurationsschnittstelle, über die Benutzer auswählen können, welche Abfrage angezeigt werden soll, mit Livevorschaufunktionen.
In diesem Teil wird veranschaulicht, wie konfigurierbare Widgets erstellt werden, die Benutzer an ihre spezifischen Anforderungen anpassen können, während während der Konfiguration Echtzeitfeedback bereitgestellt wird.
Schritt 1: Erstellen von Konfigurationsdateien
Widgetkonfigurationen teilen viele Ähnlichkeiten mit Widgets selbst – beide verwenden dieselben SDK-, HTML-Struktur- und JavaScript-Muster, dienen jedoch unterschiedlichen Zwecken innerhalb des Erweiterungsframeworks.
Einrichten der Projektstruktur
Erstellen Sie zwei neue Dateien, um die Widgetkonfiguration zu unterstützen:
- Kopieren Sie
hello-world2.htmlund benennen Sie es inhello-world3.htmlum, Ihr konfigurierbares Widget. - Erstellen Sie eine neue Datei namens
configuration.html, die die Konfigurationsschnittstelle behandelt.
Ihre Projektstruktur umfasst jetzt Folgendes:
|--- 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
Erstellen der Konfigurationsschnittstelle
Fügen Sie diese HTML-Struktur zu configuration.html hinzu, die eine Dropdownauswahl für die Auswahl von Abfragen erstellt.
<!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>
Schritt 2: Implementieren von Konfigurations-JavaScript
Configuration JavaScript folgt dem gleichen Initialisierungsmuster wie Widgets, implementiert jedoch den IWidgetConfiguration Vertrag anstelle des Basisvertrags IWidget .
Hinzufügen von Konfigurationslogik
Fügen Sie dieses Skript in den <head> Abschnitt von 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>
Konfigurationsvertragsdetails
Für den IWidgetConfiguration Vertrag sind die folgenden wichtigen Funktionen erforderlich:
| Funktion | Zweck | Bei Aufruf |
|---|---|---|
load() |
Initialisieren der Konfigurations-UI mit vorhandenen Einstellungen | Wenn das Konfigurationsdialogfeld geöffnet wird |
onSave() |
Serialisieren von Benutzereingaben und Überprüfen von Einstellungen | Wenn der Benutzer "Speichern" auswählt |
Tipp
Daten serialisieren: In diesem Beispiel wird JSON zum Serialisieren von Einstellungen verwendet. Das Widget greift auf diese Einstellungen über widgetSettings.customSettings.data zu und muss sie korrekt deserialisieren.
Schritt 3: Aktivieren der Livevorschaufunktion
Mit der Livevorschau können Benutzer Widgetänderungen sofort sehen, während sie Konfigurationseinstellungen ändern und sofortiges Feedback vor dem Speichern bereitstellen.
Implementieren von Änderungsbenachrichtigungen
Um die Livevorschau zu aktivieren, fügen Sie diesen Ereignishandler innerhalb der load Funktion hinzu:
$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);
});
Vollständige Konfigurationsdatei
Ihr Endgültiges configuration.html sollte wie folgt aussehen:
<!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>
Von Bedeutung
Schaltfläche "Speichern" aktivieren: Das Framework erfordert mindestens eine Konfigurationsänderungsbenachrichtigung, um die Schaltfläche " Speichern " zu aktivieren. Der Änderungsereignishandler stellt sicher, dass diese Aktion auftritt, wenn Benutzer eine Option auswählen.
Schritt 4: Konfigurieren des Widgets
Transformieren Sie Ihr Widget aus Teil 2, um Konfigurationsdaten anstelle hartcodierter Werte zu verwenden. Dieser Schritt erfordert die Implementierung des IConfigurableWidget Vertrags.
Aktualisieren der Widgetregistrierung
Nehmen Sie in hello-world3.html folgende Änderungen vor:
-
Widget-ID aktualisieren: Von
HelloWorldWidget2zuHelloWorldWidget3. -
Neuladefunktion hinzufügen: Implementieren Sie den
IConfigurableWidgetVertrag.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Behandeln von Konfigurationsdaten
Aktualisieren Sie die getQueryInfo Funktion so, dass Konfigurationseinstellungen anstelle hartcodierter Abfragepfade verwendet werden:
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();
}
Unterschiede beim Widgetlebenszyklus
| Funktion | Zweck | Nutzungsrichtlinien |
|---|---|---|
load() |
Anfängliches Widgetrendering und einmaliges Setup | Schwere Vorgänge, Ressourceninitialisierung |
reload() |
Widget mit neuer Konfiguration aktualisieren | Einfache Updates, Datenaktualisierung |
Tipp
Leistungsoptimierung: Wird load() für teure Vorgänge verwendet, die nur einmal ausgeführt werden müssen, und reload() für schnelle Updates, wenn sich die Konfiguration ändert.
(Optional) Zum Hinzufügen einer Lightbox-Komponente für detaillierte Informationen
Dashboard-Widgets haben begrenzten Platz und machen es schwierig, umfassende Informationen anzuzeigen. Eine Lightbox bietet eine elegante Lösung, indem detaillierte Daten in einer modalen Überlagerung angezeigt werden, ohne vom Dashboard weg zu navigieren.
Warum eine Lightbox in Widgets verwenden?
| Nutzen | Beschreibung |
|---|---|
| Platzeffizienz | Halten Sie widget kompakt, während Sie detaillierte Ansichten anbieten |
| Benutzererfahrung | Den Dashboard-Kontext beibehalten, während mehr Informationen angezeigt werden. |
| Schrittweise Offenlegung | Zusammenfassungsdaten im Widget anzeigen, Details bei Bedarf |
| Dynamisches Design | Anpassen an verschiedene Bildschirmgrößen und Widgetkonfigurationen |
Implementieren von klickbaren Elementen
Aktualisieren Sie das Rendern der Abfragedaten so, dass sie klickbare Elemente enthält, die das Lightbox-Element auslösen:
// 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);
Erstellen der Lightbox-Funktionalität
Fügen Sie diese Lightbox-Implementierung zu Ihrem Widget JavaScript hinzu:
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');
}
});
}
Hinzufügen von Lightbox-Stil
Fügen Sie CSS-Formatvorlagen für das Lightbox-Element in Ihren HTML-Abschnitt <head> des Widgets ein:
<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>
Erweiterte Widgetimplementierung
Ihr vollständiges erweitertes Widget mit Lightbox-Funktionalität:
<!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>
Überlegungen zur Barrierefreiheit: Stellen Sie sicher, dass Ihre Lightbox tastaturbedienbar ist und geeignete Bezeichnungen für Bildschirmleser enthält. Testen Sie die integrierten Barrierefreiheitsfeatures von Azure DevOps.
Von Bedeutung
Leistung: Lightboxes sollten schnell geladen werden. Erwägen Sie, detaillierte Daten erst beim Öffnen der Lightbox nachzuladen, anstatt alles im Voraus abzurufen.
Schritt 5: Konfigurieren des Erweiterungsmanifests
Registrieren Sie sowohl das konfigurierbare Widget als auch die Konfigurationsschnittstelle in Ihrem Erweiterungsmanifest.
Hinzufügen von Widget- und Konfigurationsbeiträgen
Aktualisierung von vss-extension.json, um zwei neue Beiträge einzuschließen:
{
"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
},
{
"rowSpan": 2,
"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
}
]
}
Konfigurationsbeitragsanforderungen
| Eigenschaft | Zweck | Erforderlicher Wert |
|---|---|---|
type |
Identifiziert den Beitrag als Widgetkonfiguration | ms.vss-dashboards-web.widget-configuration |
targets |
Wo die Konfiguration angezeigt wird | ms.vss-dashboards-web.widget-configuration |
uri |
Pfad zur HTML-Konfigurationsdatei | Pfad der Konfigurationsdatei |
Widgetadressierungsmuster
Für konfigurierbare Widgets muss das targets Array einen Verweis auf die Konfiguration enthalten:
<publisher>.<extension-id>.<configuration-id>
Warnung
Sichtbarkeit der Konfigurationsschaltfläche: Wenn das Widget nicht ordnungsgemäß auf seinen Konfigurationsbeitrag ausgerichtet ist, wird die Schaltfläche " Konfigurieren " nicht angezeigt. Überprüfen Sie, ob die Herausgeber- und Erweiterungsnamen ihrem Manifest exakt entsprechen.
Schritt 6: Packen, Veröffentlichen und Freigeben
Stellen Sie Ihre erweiterte Erweiterung mit Konfigurationsfunktionen bereit.
Wenn es sich um Ihre erste Publikation handelt, folgen Sie Schritt 6: Packen, Veröffentlichen und Freigeben. Für vorhandene Erweiterungen können Sie diese direkt im Marketplace umverpacken und aktualisieren.
Schritt 7: Testen des konfigurierbaren Widgets
Erleben Sie den vollständigen Konfigurationsworkflow, indem Sie Ihr Widget hinzufügen und konfigurieren.
Fügen Sie das Widget zum Ihrem Dashboard hinzu.
- Gehe zu
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Wechseln Sie zu Übersicht>Dashboards.
- Wählen Sie "Widget hinzufügen" aus.
- Suchen Sie "Hello World Widget 3 (mit Konfiguration)", und wählen Sie "Hinzufügen" aus.
Eine Konfigurationsaufforderung wird angezeigt, da für das Widget Setup erforderlich ist:
Konfigurieren des Widgets
Access-Konfiguration über eine der beiden Methoden:
- Widget-Menü: Bewegen Sie den Mauszeiger über das Widget, wählen Sie die Ellipse (⋯) und dann Konfigurieren
- Dashboard-Bearbeitungsmodus: Wählen Sie " Bearbeiten " im Dashboard und dann die Schaltfläche "Konfigurieren" im Widget aus.
Der Konfigurationsbereich wird mit einer Livevorschau im Zentrum geöffnet. Wählen Sie eine Abfrage aus der Dropdownliste aus, um sofortige Updates anzuzeigen, und wählen Sie dann "Speichern" aus, um Ihre Änderungen anzuwenden.
Schritt 8: Hinzufügen erweiterter Konfigurationsoptionen
Erweitern Sie Ihr Widget mit mehr integrierten Konfigurationsfeatures wie benutzerdefinierten Namen und Größen.
Aktivieren der Namens- und Größenkonfiguration
Azure DevOps bietet zwei konfigurierbare Features sofort einsatzbereit:
| Merkmal | Manifesteigenschaft | Zweck |
|---|---|---|
| Benutzerdefinierte Namen | isNameConfigurable: true |
Benutzer können den Standard-Widgetnamen außer Kraft setzen. |
| Mehrere Größen | Mehrere supportedSizes Einträge |
Benutzer können die Größe von Widgets ändern. |
Beispiel für ein erweitertes 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"]
}
}
]
}
Konfigurierte Namen anzeigen
Um benutzerdefinierte Widgetnamen anzuzeigen, aktualisieren Sie Ihr Widget, um widgetSettings.name zu verwenden.
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);
}
}
Nachdem Sie Die Erweiterung aktualisiert haben, können Sie sowohl den Widgetnamen als auch die Größe konfigurieren:
Neuverpacken und aktualisieren Sie Ihre Erweiterung, um diese erweiterten Konfigurationsoptionen zu aktivieren.
Glückwunsch! Sie haben ein vollständiges, konfigurierbares Azure DevOps-Dashboard-Widget mit Livevorschaufunktionen und Benutzeranpassungsoptionen erstellt.