Aggiungere un widget del dashboard
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
I widget in un dashboard vengono implementati come contributi nel framework di estensione. Una singola estensione può avere più contributi. Informazioni su come creare un'estensione con più widget come contributi.
Questo articolo è suddiviso in tre parti, ognuna delle quali si basa sull'elemento precedente, iniziando con un widget semplice e terminando con un widget completo.
Suggerimento
Vedere la documentazione più recente sullo sviluppo di estensioni con Azure DevOps Extension SDK.
Prerequisiti
- Conoscenza: per lo sviluppo di widget è necessaria una certa conoscenza di JavaScript, HTML e CSS.
- Un'organizzazione in Azure DevOps.
- Editor di testo. Per molte delle esercitazioni si usa Visual Studio Code.
- Versione più recente del nodo.
- Interfaccia della riga di comando multipiattaforma per Azure DevOps (tfx-cli) per creare un pacchetto delle estensioni.
- Tfx-cli può essere installato usando
npm
, un componente di Node.js eseguendonpm i -g tfx-cli
- Tfx-cli può essere installato usando
- Home directory per il progetto. Questa directory viene definita in
home
tutta l'esercitazione.
Struttura dei file di estensione:
|--- 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
Contenuto dell'esercitazione:
- Parte 1: mostra come creare un nuovo widget, che stampa un semplice messaggio "Hello World".
- Parte 2: si basa sulla prima parte aggiungendo una chiamata a un'API REST di Azure DevOps.
- Parte 3: Spiega come aggiungere la configurazione al widget.
Nota
Se hai fretta e vuoi mettere subito le mani sul codice, puoi scaricare gli esempi.
Dopo il download, passare alla widgets
cartella, quindi seguire il passaggio 6 e il passaggio 7 direttamente per pubblicare l'estensione di esempio con i tre widget di esempio di diverse complessità.
Introduzione ad alcuni stili di base per i widget forniti in modo predefinito e alcune indicazioni sulla struttura dei widget.
Parte 1: Hello World
La parte 1 presenta un widget che stampa "Hello World" usando JavaScript.
Passaggio 1: Ottenere l'SDK client - VSS.SDK.min.js
Lo script dell'SDK principale, VSS.SDK.min.js
, consente alle estensioni Web di comunicare con il frame di Azure DevOps host. Lo script esegue operazioni come l'inizializzazione, la notifica dell'estensione viene caricata o il recupero del contesto sulla pagina corrente.
Ottenere il file dell'SDK VSS.SDK.min.js
client e aggiungerlo all'app Web. Posizionarlo nella home/sdk/scripts
cartella .
Per recuperare l'SDK, usare il comando 'npm install':
npm install vss-web-extension-sdk
Per altre informazioni, vedere la pagina GitHub dell'SDK client.
Passaggio 2: Configurare la pagina HTML - hello-world.html
La pagina HTML è l'associazione che contiene il layout e include riferimenti a CSS e JavaScript. È possibile assegnare un nome a questo file. Aggiornare tutti i riferimenti a hello-world
con il nome usato.
Il widget è basato su HTML ed è ospitato in un iframe.
Aggiungere il codice HTML seguente in hello-world.html
. Aggiungere il riferimento obbligatorio al VSS.SDK.min.js
file e includere un h2
elemento, che viene aggiornato con la stringa Hello World nel passaggio successivo.
<!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>
Anche se si usa un file HTML, la maggior parte degli elementi head HTML diversi dallo script e dal collegamento viene ignorata dal framework.
Passaggio 3: Aggiornare JavaScript
Per eseguire il rendering del contenuto nel widget viene usato JavaScript. In questo articolo viene eseguito il wrapping di tutto il codice JavaScript all'interno di un <script>
elemento nel file HTML. È possibile scegliere di avere questo codice in un file JavaScript separato e farvi riferimento nel file HTML.
Il codice esegue il rendering del contenuto. Questo codice JavaScript inizializza anche VSS SDK, esegue il mapping del codice per il widget al nome del widget e invia una notifica al framework di estensione delle operazioni riuscite o non riuscite del widget.
In questo caso, il codice seguente stampa "Hello World" nel widget. Aggiungere questo script
elemento nell'oggetto head
del codice HTML.
<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
inizializza l'handshake tra l'iframe che ospita il widget e il frame host.explicitNotifyLoaded: true
Passiamo in modo che il widget possa notificare in modo esplicito all'host al termine del caricamento. Questo controllo consente di notificare il completamento del carico dopo aver verificato che i moduli dipendenti vengano caricati. Viene passatousePlatformStyles: true
in modo che il widget possa usare gli stili di base di Azure DevOps per gli elementi HTML, ad esempio corpo, div e così via. Se il widget preferisce non usare questi stili, è possibile passareusePlatformStyles: false
.VSS.require
viene usato per caricare le librerie di script VSS necessarie. Una chiamata a questo metodo carica automaticamente librerie generali come JQuery e JQueryUI. In questo caso, si dipende dalla libreria WidgetHelpers, che viene usata per comunicare lo stato del widget al framework del widget. Quindi, passiamo il nomeTFS/Dashboards/WidgetHelpers
del modulo corrispondente e un callback aVSS.require
. Il callback viene chiamato dopo il caricamento del modulo. Il callback include il resto del codice JavaScript necessario per il widget. Al termine del callback, viene chiamatoVSS.notifyLoadSucceeded
per notificare il completamento del caricamento.WidgetHelpers.IncludeWidgetStyles
include un foglio di stile con alcuni css di base per iniziare. Per usare questi stili, eseguire il wrapping del contenuto all'interno di un elemento HTML con la classewidget
.VSS.register
viene usato per eseguire il mapping di una funzione in JavaScript, che identifica in modo univoco il widget tra i diversi contributi nell'estensione. Il nome deve corrispondere aid
che identifica il contributo come descritto nel passaggio 5. Per i widget, la funzione passata aVSS.register
deve restituire un oggetto che soddisfa ilIWidget
contratto, ad esempio, l'oggetto restituito deve avere una proprietà di caricamento il cui valore è un'altra funzione con la logica di base per eseguire il rendering del widget. In questo caso, è aggiornare il testo dell'elementoh2
in "Hello World". Questa funzione viene chiamata quando il framework del widget crea un'istanza del widget. Viene usato daWidgetStatusHelper
WidgetHelpers per restituire l'oggettoWidgetStatus
come esito positivo.
Avviso
Se il nome usato per registrare il widget non corrisponde all'ID per il contributo nel manifesto, il widget funziona in modo imprevisto.
vss-extension.json
deve sempre trovarsi nella radice della cartella (in questa guida,HelloWorld
). Per tutti gli altri file, è possibile inserirli in qualsiasi struttura desiderata all'interno della cartella, è sufficiente assicurarsi di aggiornare i riferimenti in modo appropriato nei file HTML e nelvss-extension.json
manifesto.
Passaggio 4: Aggiornare il logo dell'estensione: logo.png
Il logo viene visualizzato nel Marketplace e nel catalogo dei widget dopo che un utente installa l'estensione.
È necessaria un'icona del catalogo 98 px x 98 px. Scegliere un'immagine, denominarla logo.png
e inserirla nella img
cartella .
È possibile denominare queste immagini, purché il manifesto dell'estensione nel passaggio successivo venga aggiornato con i nomi usati.
Passaggio 5: Creare il manifesto dell'estensione: vss-extension.json
Ogni estensione deve avere un file manifesto di estensione.
- Leggere il riferimento al manifesto dell'estensione.
- Per altre informazioni sui punti di contributo, vedere Punti di estendibilità.
- Creare un file JSON (
vss-extension.json
ad esempio) nellahome
directory con il contenuto seguente:
{
"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
}
]
}
Per altre informazioni sugli attributi obbligatori, vedere le informazioni di riferimento sul manifesto dell'estensione.
Nota
Modificare il server di pubblicazione impostando il nome dell'editore. Per creare un server di pubblicazione, vedere Pacchetto/Pubblicazione/Installazione.
Icone
La stanza delle icone specifica il percorso dell'icona dell'estensione nel manifesto.
Contributi
Ogni voce di contributo definisce le proprietà.
- ID per identificare il contributo. Questo ID deve essere univoco all'interno di un'estensione. Questo ID deve corrispondere al nome usato nel passaggio 3 per registrare il widget.
- Tipo di contributo. Per tutti i widget, il tipo deve essere
ms.vss-dashboards-web.widget
. - Matrice di destinazioni a cui contribuisce il contributo. Per tutti i widget, la destinazione deve essere
[ms.vss-dashboards-web.widget-catalog]
. - Le proprietà sono oggetti che includono proprietà per il tipo di contributo. Per i widget, le proprietà seguenti sono obbligatorie.
Proprietà | Descrizione |
---|---|
name | Nome del widget da visualizzare nel catalogo dei widget. |
description | Descrizione del widget da visualizzare nel catalogo dei widget. |
catalogIconUrl | Percorso relativo dell'icona del catalogo aggiunta nel passaggio 4 da visualizzare nel catalogo dei widget. L'immagine deve essere 98 px x 98 px. Se è stata usata una struttura di cartelle diversa o un nome di file diverso, specificare il percorso relativo appropriato qui. |
previewImageUrl | Percorso relativo dell'immagine di anteprima aggiunta nel passaggio 4 da visualizzare nel catalogo dei widget. L'immagine deve essere 330 px x 160 px. Se è stata usata una struttura di cartelle diversa o un nome di file diverso, specificare il percorso relativo appropriato qui. |
uri | Percorso relativo del file HTML aggiunto nel passaggio 1. Se è stata usata una struttura di cartelle diversa o un nome di file diverso, specificare il percorso relativo appropriato qui. |
supportedSizes | Matrice di dimensioni supportate dal widget. Quando un widget supporta più dimensioni, la prima dimensione nella matrice è la dimensione predefinita del widget. Viene widget size specificato per le righe e le colonne occupate dal widget nella griglia del dashboard. Una riga/colonna corrisponde a 160 px. Qualsiasi dimensione maggiore di 1x1 ottiene un valore aggiuntivo di 10 px che rappresenta la barra tra i widget. Ad esempio, un widget 3x2 è 160*3+10*2 largo e 160*2+10*1 alto. La dimensione massima supportata è 4x4 . |
supportedScopes | Attualmente sono supportati solo i dashboard del team. Il valore deve essere project_team . Gli aggiornamenti futuri potrebbero includere altre opzioni per gli ambiti del dashboard. |
File
La stanza dei file indica i file da includere nel pacchetto, ovvero la pagina HTML, gli script, lo script SDK e il logo.
Impostare su addressable
a true
meno che non si includano altri file che non devono essere indirizzabili tramite URL.
Nota
Per altre informazioni sul file manifesto dell'estensione, ad esempio le relative proprietà e le relative operazioni, vedere il riferimento al manifesto dell'estensione.
Passaggio 6: Creare un pacchetto, pubblicare e condividere
Dopo aver creato l'estensione scritta, il passaggio successivo per inserirlo nel Marketplace consiste nel creare un pacchetto di tutti i file. Tutte le estensioni vengono in pacchetto come file VSIX 2.0 compatibili con estensione vsix: Microsoft fornisce un'interfaccia della riga di comando multipiattaforma per creare un pacchetto dell'estensione.
Ottenere lo strumento di creazione dei pacchetti
È possibile installare o aggiornare l'interfaccia della riga di comando multipiattaforma per Azure DevOps (tfx-cli) usando npm
, un componente di Node.js dalla riga di comando.
npm i -g tfx-cli
Creare il pacchetto dell'estensione
La creazione di pacchetti dell'estensione in un file con estensione vsix è semplice dopo aver creato tfx-cli. Passare alla home directory dell'estensione ed eseguire il comando seguente.
tfx extension create --manifest-globs vss-extension.json
Nota
Una versione di estensione/integrazione deve essere incrementata a ogni aggiornamento.
Quando si aggiorna un'estensione esistente, aggiornare la versione nel manifesto o passare l'opzione della --rev-version
riga di comando. In questo modo viene incrementato il numero di versione della patch dell'estensione e viene salvata la nuova versione nel manifesto.
Dopo aver creato il pacchetto dell'estensione in un file con estensione vsix, si è pronti per pubblicare l'estensione nel Marketplace.
Creare un server di pubblicazione per l'estensione
Tutte le estensioni, incluse le estensioni di Microsoft, vengono identificate come fornite da un editore. Se non si è già membri di un server di pubblicazione esistente, crearne uno.
- Accedere al portale di pubblicazione di Visual Studio Marketplace
- Se non si è già membri di un server di pubblicazione esistente, è necessario creare un server di pubblicazione. Se si dispone già di un server di pubblicazione, scorrere fino a e selezionare Pubblica estensioni in Siti correlati.
- Specificare un identificatore per il server di pubblicazione, ad esempio:
mycompany-myteam
- L'identificatore viene usato come valore per l'attributo
publisher
nel file manifesto delle estensioni.
- L'identificatore viene usato come valore per l'attributo
- Specificare un nome visualizzato per il server di pubblicazione, ad esempio:
My Team
- Specificare un identificatore per il server di pubblicazione, ad esempio:
- Esaminare il Contratto di pubblicazione del Marketplace e selezionare Crea.
Ora il server di pubblicazione è definito. In una versione futura è possibile concedere le autorizzazioni per visualizzare e gestire le estensioni dell'editore.
La pubblicazione di estensioni in un editore comune semplifica il processo per team e organizzazioni, offrendo un approccio più sicuro. Questo metodo elimina la necessità di distribuire un singolo set di credenziali tra più utenti, migliorando la sicurezza e
Aggiornare il vss-extension.json
file manifesto negli esempi per sostituire l'ID editore fittizio con l'ID fabrikam
editore.
Pubblicare e condividere l'estensione
Ora è possibile caricare l'estensione nel Marketplace.
Selezionare Carica nuova estensione, passare al file con estensione vsix in pacchetto e selezionare Carica.
È anche possibile caricare l'estensione tramite la riga di tfx extension create
comando usando il tfx extension publish
comando anziché per creare un pacchetto e pubblicare l'estensione in un unico passaggio.
Facoltativamente, è possibile usare --share-with
per condividere l'estensione con uno o più account dopo la pubblicazione.
È anche necessario un token di accesso personale.
tfx extension publish --manifest-globs your-manifest.json --share-with yourOrganization
Passaggio 7: Aggiungere il widget dal catalogo
Accedere al progetto.
http://dev.azure.com/{Your_Organization}/{Your_Project}
Selezionare Panoramica>Dashboard.
Seleziona Aggiungi un widget.
Evidenziare il widget e quindi selezionare Aggiungi.
Il widget viene visualizzato nel dashboard.
Parte 2: Hello World con l'API REST di Azure DevOps
I widget possono chiamare qualsiasi API REST in Azure DevOps per interagire con le risorse di Azure DevOps. Nell'esempio seguente viene usata l'API REST per WorkItemTracking per recuperare informazioni su una query esistente e visualizzare alcune informazioni sulla query nel widget sotto il testo "Hello World".
Passaggio 1: Aggiungere un file HTML
Copiare il file hello-world.html
dall'esempio precedente e rinominare la copia in hello-world2.html
. La cartella è ora simile all'esempio seguente:
|--- README.md |--- node_modules
|--- SDK
|--- script |--- VSS. SDK.min.js |--- img |--- logo.png |--- script
|--- hello-world.html pagina HTML da usare per il widget |--- hello-world2.html // copia rinominata di hello-world.html |--- vss-extension.json // manifesto dell'estensione
Per contenere le informazioni sulla query, aggiungere un nuovo div
elemento sotto .h2
Aggiornare il nome del widget da HelloWorldWidget
a HelloWorldWidget2
nella riga in cui si chiama VSS.register
.
Questa azione consente al framework di identificare in modo univoco il widget all'interno dell'estensione.
<!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>
Passaggio 2: Accedere alle risorse di Azure DevOps
Per abilitare l'accesso alle risorse di Azure DevOps, è necessario specificare gli ambiti nel manifesto dell'estensione. Aggiungere l'ambito vso.work
al manifesto.
Questo ambito indica che il widget richiede l'accesso in sola lettura alle query e agli elementi di lavoro. Vedere tutti gli ambiti disponibili qui.
Aggiungere il codice seguente alla fine del manifesto dell'estensione.
{
"scopes":[
"vso.work"
]
}
Per includere altre proprietà, è necessario elencarle in modo esplicito, ad esempio:
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
Avviso
L'aggiunta o la modifica di ambiti dopo la pubblicazione di un'estensione non è attualmente supportata. Se l'estensione è già stata caricata, rimuoverla dal Marketplace. Passare al portale di pubblicazione di Visual Studio Marketplace, selezionare con il pulsante destro del computer l'estensione e selezionare Rimuovi.
Passaggio 3: Effettuare la chiamata API REST
Sono disponibili molte librerie lato client a cui è possibile accedere tramite l'SDK per effettuare chiamate API REST in Azure DevOps. Queste librerie sono denominate client REST e sono wrapper JavaScript intorno alle chiamate Ajax per tutti gli endpoint lato server disponibili. È possibile usare metodi forniti da questi client invece di scrivere manualmente chiamate Ajax. Questi metodi eseguono il mapping delle risposte API agli oggetti che il codice può utilizzare.
In questo passaggio si aggiorna la VSS.require
chiamata per caricare AzureDevOps/WorkItemTracking/RestClient
, che fornisce il client REST WorkItemTracking.
È possibile usare questo client REST per ottenere informazioni su una query denominata Feedback
nella cartella Shared Queries
.
All'interno della funzione passata a VSS.register
viene creata una variabile per contenere l'ID progetto corrente. Questa variabile è necessaria per recuperare la query.
Viene anche creato un nuovo metodo getQueryInfo
per usare il client REST. Questo metodo viene quindi chiamato dal metodo load.
Il metodo getClient
fornisce un'istanza del client REST necessario.
Il metodo getQuery
restituisce la query sottoposta a wrapping in una promessa.
L'aggiornamento VSS.require
è simile al seguente:
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();
});
Si noti l'uso del metodo Failure da WidgetStatusHelper
.
Consente di indicare al framework del widget che si è verificato un errore e sfruttare l'esperienza di errore standard fornita a tutti i widget.
Se la Feedback
query Shared Queries
non è presente nella cartella , sostituire Shared Queries\Feedback
nel codice con il percorso di una query esistente nel progetto.
Passaggio 4: Visualizzare la risposta
L'ultimo passaggio consiste nel eseguire il rendering delle informazioni sulla query all'interno del widget.
La getQuery
funzione restituisce un oggetto di tipo Contracts.QueryHierarchyItem
all'interno di una promessa.
In questo esempio vengono visualizzati l'ID della query, il nome della query e il nome dell'autore della query nel testo "Hello World".
Sostituire il commento // Do something with the query
con il codice seguente:
// 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);
L'ultima hello-world2.html
è simile all'esempio seguente:
<!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>
Passaggio 5: Aggiornare il manifesto dell'estensione
In questo passaggio si aggiorna il manifesto dell'estensione per includere una voce per il secondo widget.
Aggiungere un nuovo contributo alla matrice nella contributions
proprietà e aggiungere il nuovo file hello-world2.html
alla matrice nella proprietà files.
È necessaria un'altra immagine di anteprima per il secondo widget. preview2.png
Denominarlo e inserirlo nella img
cartella .
{
...,
"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"
]
}
Passaggio 6: Creare un pacchetto, pubblicare e condividere
Creare un pacchetto, pubblicare e condividere l'estensione. Se l'estensione è già stata pubblicata, è possibile creare nuovamente il pacchetto dell'estensione e aggiornarla direttamente nel Marketplace.
Passaggio 7: Aggiungere il widget dal catalogo
Passare ora al dashboard del team all'indirizzo https:\//dev.azure.com/{Your_Organization}/{Your_Project}
. Se questa pagina è già aperta, aggiornarla.
Passare il puntatore del mouse su Modifica e selezionare Aggiungi. Viene aperto il catalogo dei widget in cui si trova il widget installato.
Per aggiungerlo al dashboard, scegliere il widget e selezionare Aggiungi.
Parte 3: Configurare Hello World
Nella parte 2 di questa guida è stato illustrato come creare un widget che mostra le informazioni sulle query per una query hardcoded. In questa parte viene aggiunta la possibilità di configurare la query da usare anziché quella hardcoded. Quando si usa la modalità di configurazione, l'utente può visualizzare un'anteprima in tempo reale del widget in base alle modifiche. Queste modifiche vengono salvate nel widget nel dashboard quando l'utente seleziona Salva.
Passaggio 1: Aggiungere un file HTML
Le implementazioni di widget e configurazioni dei widget sono molto simili. Entrambi vengono implementati nel framework di estensione come contributi. Entrambi usano lo stesso file SDK, VSS.SDK.min.js
. Entrambi sono basati su HTML, JavaScript e CSS.
Copiare il file html-world2.html
dall'esempio precedente e rinominare la copia in hello-world3.html
. Aggiungere un altro file HTML denominato configuration.html
.
La cartella è ora simile all'esempio seguente:
|--- 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
Aggiungere il codice HTML seguente in configuration.html
. Si aggiunge fondamentalmente il riferimento obbligatorio al VSS.SDK.min.js
file e un select
elemento per l'elenco a discesa per selezionare una query da un elenco preimpostato.
<!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>
Passaggio 2: Configurare JavaScript
Usare JavaScript per eseguire il rendering del contenuto nella configurazione del widget esattamente come è stato fatto per il widget nel passaggio 3 della parte 1 in questa guida.
Questo codice JavaScript esegue il rendering del contenuto, inizializza VSS SDK, esegue il mapping del codice per la configurazione del widget al nome della configurazione e passa le impostazioni di configurazione al framework. In questo caso, il codice seguente carica la configurazione del widget.
Aprire il file configuration.html
e l'elemento seguente <script>
in <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.require
eVSS.register
svolgono lo stesso ruolo svolto per il widget come descritto nella parte 1. L'unica differenza è che per le configurazioni dei widget, la funzione passata aVSS.register
deve restituire un oggetto che soddisfa ilIWidgetConfiguration
contratto.- La
load
proprietà delIWidgetConfiguration
contratto deve avere una funzione come valore. Questa funzione include il set di passaggi per eseguire il rendering della configurazione del widget. In questo caso, è aggiornare il valore selezionato dell'elemento a discesa con le impostazioni esistenti, se presenti. Questa funzione viene chiamata quando il framework crea un'istanza delwidget configuration
- La
onSave
proprietà delIWidgetConfiguration
contratto deve avere una funzione come valore. Questa funzione viene chiamata dal framework quando l'utente seleziona Salva nel riquadro di configurazione. Se l'input dell'utente è pronto per il salvataggio, serializzarlo in una stringa, formare l'oggettocustom settings
e usareWidgetConfigurationSave.Valid()
per salvare l'input dell'utente.
In questa guida viene usato JSON per serializzare l'input dell'utente in una stringa. È possibile scegliere qualsiasi altro modo per serializzare l'input dell'utente nella stringa.
È accessibile al widget tramite la proprietà customSettings dell'oggetto WidgetSettings
.
Il widget deve deserializzare, illustrato nel passaggio 4.
Passaggio 3: JavaScript - Abilitare l'anteprima in tempo reale
Per abilitare l'aggiornamento dell'anteprima in tempo reale quando l'utente seleziona una query dall'elenco a discesa, allega un gestore eventi di modifica al pulsante. Questo gestore notifica al framework che la configurazione è stata modificata.
Passa anche l'oggetto customSettings
da usare per l'aggiornamento dell'anteprima. Per notificare al framework, è necessario chiamare il notify
metodo sull'oggetto widgetConfigurationContext
. Accetta due parametri, il nome dell'evento, che in questo caso è WidgetHelpers.WidgetEvent.ConfigurationChange
e un EventArgs
oggetto per l'evento, creato da customSettings
con l'aiuto del metodo helper WidgetEvent.Args
.
Aggiungere il codice seguente nella funzione assegnata alla load
proprietà .
$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);
});
Revisione: assicurarsi che il framework riceve una notifica della modifica della configurazione almeno una volta per abilitare il pulsante Salva .
Alla fine, l'esempio configuration.html
è simile al seguente:
<!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>
Passaggio 4: Implementare il ricaricamento nel widget - JavaScript
È stata configurata la configurazione del widget per archiviare il percorso di query selezionato dall'utente.
È ora necessario aggiornare il codice nel widget per usare questa configurazione archiviata anziché il hardcoded Shared Queries/Feedback
dell'esempio precedente.
Aprire il file hello-world3.html
e aggiornare il nome del widget da HelloWorldWidget2
a HelloWorldWidget3
nella riga in cui si chiama VSS.register
.
Questa azione consente al framework di identificare in modo univoco il widget all'interno dell'estensione.
La funzione mappata a HelloWorldWidget3
tramite VSS.register
restituisce attualmente un oggetto che soddisfa il IWidget
contratto.
Poiché il widget richiede ora la configurazione, questa funzione deve essere aggiornata per restituire un oggetto che soddisfa il IConfigurableWidget
contratto.
A tale scopo, aggiornare l'istruzione return per includere una proprietà denominata ricaricamento in base al codice seguente. Il valore di questa proprietà è una funzione che chiama il getQueryInfo
metodo una volta.
Questo metodo di ricaricamento viene chiamato dal framework ogni volta che l'input dell'utente cambia per visualizzare l'anteprima in tempo reale. Questo metodo di ricaricamento viene chiamato anche quando viene salvata la configurazione.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Il percorso di query hardcoded in getQueryInfo
deve essere sostituito con il percorso di query configurato, che può essere estratto dal parametro widgetSettings
passato al metodo .
Aggiungere il codice seguente all'inizio del getQueryInfo
metodo e sostituire il percorso di query hardcoded con 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();
}
A questo punto, il widget è pronto per il rendering con le impostazioni configurate.
Sia le load
proprietà che le reload
proprietà hanno una funzione simile. Questo è il caso per i widget più semplici.
Per i widget complessi, ci sarebbero alcune operazioni che si desidera eseguire una sola volta, indipendentemente dal numero di modifiche alla configurazione.
In alternativa, potrebbero esserci alcune operazioni pesanti che non devono essere eseguite più di una volta. Tali operazioni fanno parte della funzione corrispondente alla load
proprietà e non alla reload
proprietà .
Passaggio 5: Aggiornare il manifesto dell'estensione
Aprire il vss-extension.json
file per includere due nuove voci nella matrice nella contributions
proprietà . Uno per il HelloWorldWidget3
widget e l'altro per la configurazione.
È necessaria un'altra immagine di anteprima per il terzo widget. preview3.png
Denominarlo e inserirlo nella img
cartella .
Aggiornare la matrice nella files
proprietà per includere i due nuovi file HTML aggiunti in questo esempio.
{
...
"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
}
],
...
}
Il contributo per la configurazione del widget segue un modello leggermente diverso rispetto al widget stesso. Una voce di contributo per la configurazione del widget include:
- ID per identificare il contributo. L'ID deve essere univoco all'interno di un'estensione.
- Tipo di contributo. Per tutte le configurazioni dei widget, deve essere
ms.vss-dashboards-web.widget-configuration
- Matrice di destinazioni a cui contribuisce il contributo. Per tutte le configurazioni dei widget, include una singola voce:
ms.vss-dashboards-web.widget-configuration
. - Proprietà che contengono un set di proprietà che include nome, descrizione e URI del file HTML utilizzato per la configurazione.
Per supportare la configurazione, è necessario modificare anche il contributo del widget. La matrice di destinazioni per il widget deve essere aggiornata per includere l'ID per la configurazione nel formato <publisher
>.<>id for the extension
.<id for the configuration contribution
> In questo caso è .fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration
Avviso
Se la voce di contributo per il widget configurabile non è destinata alla configurazione usando il nome corretto dell'editore e dell'estensione come descritto in precedenza, il pulsante configura non viene visualizzato per il widget.
Alla fine di questa parte, il file manifesto deve contenere tre widget e una configurazione. È possibile ottenere il manifesto completo dall'esempio qui.
Passaggio 6: Creare un pacchetto, pubblicare e condividere
Se l'estensione non è pubblicata, vedere questa sezione. Se l'estensione è già stata pubblicata, è possibile creare nuovamente il pacchetto dell'estensione e aggiornarla direttamente nel Marketplace.
Passaggio 7: Aggiungere il widget dal catalogo
Passare ora al dashboard del team all'indirizzo https://dev.azure.com/{Your_Organization}/{Your_Project}. Se questa pagina è già aperta, aggiornarla. Passare il puntatore del mouse su Modifica e selezionare Aggiungi. Questa azione dovrebbe aprire il catalogo dei widget in cui si trova il widget installato. Per aggiungere il widget al dashboard, scegliere il widget e selezionare Aggiungi.
Un messaggio simile al seguente chiede di configurare il widget.
Esistono due modi per configurare i widget. Uno consiste nel passare il puntatore del mouse sul widget, selezionare i puntini di sospensione visualizzati nell'angolo in alto a destra e quindi selezionare Configura. L'altro consiste nel selezionare il pulsante Modifica in basso a destra del dashboard e quindi selezionare il pulsante Configura visualizzato nell'angolo in alto a destra del widget. Apre l'esperienza di configurazione sul lato destro e un'anteprima del widget al centro. Procedere e scegliere una query dall'elenco a discesa. L'anteprima live mostra i risultati aggiornati. Selezionare Salva e il widget visualizza i risultati aggiornati.
Passaggio 8: Configurare altro (facoltativo)
È possibile aggiungere tutti gli elementi del modulo HTML necessari in configuration.html
per altre configurazioni.
Sono disponibili due funzionalità configurabili: nome del widget e dimensioni del widget.
Per impostazione predefinita, il nome specificato per il widget nel manifesto dell'estensione viene archiviato come nome del widget per ogni istanza del widget che viene aggiunto a un dashboard.
È possibile consentire agli utenti di configurare, in modo che possano aggiungere qualsiasi nome all'istanza del widget.
Per consentire tale configurazione, aggiungere isNameConfigurable:true
nella sezione delle proprietà per il widget nel manifesto dell'estensione.
Se si specificano più voci per il supportedSizes
widget nella matrice nel manifesto dell'estensione, gli utenti possono configurare anche le dimensioni del widget.
Il manifesto dell'estensione per il terzo esempio in questa guida sarà simile all'esempio seguente se si abilita il nome del widget e la configurazione delle dimensioni:
{
...
"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"]
}
},
...
]
}
Con la modifica precedente, ripacchetto e aggiornamento dell'estensione. Aggiornare il dashboard con questo widget (Hello World Widget 3 (con configurazione).Refresh the dashboard that has this widget (Hello World Widget 3 (with config)). Aprire la modalità di configurazione per il widget, ora dovrebbe essere possibile visualizzare l'opzione per modificare il nome e le dimensioni del widget.
Scegliere una dimensione diversa dall'elenco a discesa. Viene visualizzata l'anteprima live ridimensionata. Salvare la modifica e anche il widget nel dashboard viene ridimensionato.
La modifica del nome del widget non comporta alcuna modifica visibile nel widget perché i widget di esempio non visualizzano il nome del widget da nessuna parte. Modificare il codice di esempio per visualizzare il nome del widget anziché il testo hardcoded "Hello World".
A tale scopo, sostituire il testo hardcoded "Hello World" con widgetSettings.name
nella riga in cui si imposta il testo dell'elemento h2
.
Questa azione garantisce che il nome del widget venga visualizzato ogni volta che il widget viene caricato all'aggiornamento della pagina.
Poiché si vuole che l'anteprima in tempo reale venga aggiornata ogni volta che la configurazione cambia, è necessario aggiungere anche lo reload
stesso codice nella parte del codice.
L'istruzione return finale in hello-world3.html
è la seguente:
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);
}
}
Ripacchetto e aggiornare di nuovo l'estensione. Aggiornare il dashboard con questo widget.
Tutte le modifiche apportate al nome del widget, nella modalità di configurazione, aggiornano ora il titolo del widget.