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 eseguendo npm i -g tfx-cli
  • 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:

  1. Parte 1: mostra come creare un nuovo widget, che stampa un semplice messaggio "Hello World".
  2. Parte 2: si basa sulla prima parte aggiungendo una chiamata a un'API REST di Azure DevOps.
  3. 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.

Screenshot del dashboard Panoramica con un widget di esempio.

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 &lt;script&gt; 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 passato usePlatformStyles: 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 passare usePlatformStyles: 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 nome TFS/Dashboards/WidgetHelpers del modulo corrispondente e un callback a VSS.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 chiamato VSS.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 classe widget.
  • 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 a id che identifica il contributo come descritto nel passaggio 5. Per i widget, la funzione passata a VSS.register deve restituire un oggetto che soddisfa il IWidget 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'elemento h2 in "Hello World". Questa funzione viene chiamata quando il framework del widget crea un'istanza del widget. Viene usato da WidgetStatusHelper WidgetHelpers per restituire l'oggetto WidgetStatus 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 nel vss-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.pnge 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.

{
    "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.

  1. Accedere al portale di pubblicazione di Visual Studio Marketplace
  2. 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.
    • Specificare un nome visualizzato per il server di pubblicazione, ad esempio: My Team
  3. 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

  1. Accedere al progetto. http://dev.azure.com/{Your_Organization}/{Your_Project}

  2. Selezionare Panoramica>Dashboard.

  3. Seleziona Aggiungi un widget.

  4. 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".

Screenshot del dashboard Panoramica con un widget di esempio usando l'API REST per WorkItemTracking.

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

Screenshot dell'anteprima in tempo reale del dashboard panoramica del widget in base alle modifiche.

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.requiree VSS.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 a VSS.register deve restituire un oggetto che soddisfa il IWidgetConfiguration contratto.
  • La load proprietà del IWidgetConfiguration 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 del widget configuration
  • La onSave proprietà del IWidgetConfiguration 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'oggetto custom settings e usare WidgetConfigurationSave.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.ConfigurationChangee 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.

Screenshot del dashboard Panoramica con un widget di esempio del catalogo.

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.

Screenshot che mostra il widget in cui è possibile configurare il nome e le dimensioni.

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.