Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Layanan Azure DevOps | Azure DevOps Server | Azure DevOps Server 2022
Widget diimplementasikan sebagai kontribusi dalam kerangka kerja ekstensi. Satu ekstensi dapat mencakup beberapa kontribusi widget. Artikel ini memperlihatkan cara membuat ekstensi yang menyediakan satu atau beberapa widget.
Petunjuk / Saran
Periksa dokumentasi terbaru kami tentang menggunakan Azure DevOps Extension SDK untuk pengembangan ekstensi.
Petunjuk / Saran
Jika Anda memulai ekstensi Azure DevOps baru, coba koleksi sampel yang dikelola ini terlebih dahulu—mereka bekerja dengan build produk saat ini dan mencakup skenario modern (misalnya, menambahkan tab pada halaman permintaan pull).
- Sampel ekstensi Azure DevOps (GitHub)—sampel starter ringkas yang menunjukkan pola ekstensi umum: https://github.com/microsoft/azure-devops-extension-sample
- Sampel ekstensi Azure DevOps (panduan pengumpulan dan kontribusi warisan)—instal untuk memeriksa target UI, atau lihat sumbernya: https://marketplace.visualstudio.com/items/ms-samples.samples-contributions-guide dan https://github.com/Microsoft/vso-extension-samples/tree/master/contributions-guide
- Sampel Microsoft Learn (jelajahi sampel Azure DevOps)—sampel yang telah diseleksi, terbaru di seluruh dokumentasi Microsoft: /samples/browse/?terms=azure%20devops%20extension
Jika sampel tidak berfungsi di organisasi Anda, instal ke organisasi pribadi atau pengujian dan bandingkan ID target manifes ekstensi dan versi API dengan dokumen saat ini. Untuk referensi dan API, lihat:
- azure-devops-extension-api
- azure-devops-extension-sdk
- API ekstensi yang diinstal
Prasyarat
| Persyaratan | Deskripsi |
|---|---|
| Pengetahuan pemrograman | Pengetahuan JavaScript, HTML, dan CSS untuk pengembangan widget |
| Organisasi Azure DevOps | Membuat organisasi |
| Editor teks | Kami menggunakan Visual Studio Code untuk tutorial |
| Node.js | Versi terbaru Node.js |
| CLI lintas platform |
tfx-cli untuk memaketkan ekstensi Instal menggunakan: npm i -g tfx-cli |
| Direktori proyek | Direktori beranda dengan struktur ini setelah menyelesaikan tutorial:|--- README.md|--- sdk |--- node_modules |--- scripts |--- VSS.SDK.min.js|--- img |--- logo.png|--- scripts|--- hello-world.html // html page for your widget|--- vss-extension.json // extension manifest |
Ikhtisar tutorial
Tutorial ini mengajarkan pengembangan widget melalui tiga contoh progresif:
| Bagian | Fokus | Yang Anda pelajari |
|---|---|---|
| Bagian 1: Halo Dunia | Pembuatan widget dasar | Membuat widget yang menampilkan teks |
| Bagian 2: Integrasi REST API | Panggilan API Azure DevOps | Menambahkan fungsionalitas REST API untuk mengambil dan menampilkan data |
| Bagian 3: Konfigurasi widget | Kustomisasi pengguna | Menerapkan opsi konfigurasi untuk widget Anda |
Petunjuk / Saran
Jika Anda lebih suka melompat langsung ke contoh kerja, sampel yang disertakan (lihat catatan sebelumnya) menunjukkan sekumpulan widget yang dapat Anda kemas dan terbitkan.
Sebelum Memulai, tinjau gaya widget dasar dan panduan struktural yang kami berikan.
Bagian 1: Halo Dunia
Buat widget dasar yang menampilkan "Halo Dunia" menggunakan JavaScript. Fondasi ini menunjukkan konsep pengembangan widget inti.
Langkah 1: Instal SDK klien
VSS SDK memungkinkan widget Anda berkomunikasi dengan Azure DevOps. Instal menggunakan npm:
npm install vss-web-extension-sdk
VSS.SDK.min.js Salin file dari vss-web-extension-sdk/lib ke folder Andahome/sdk/scripts.
Untuk dokumentasi SDK lainnya, lihat halaman GitHub SDK Klien.
Langkah 2: Buat struktur HTML
Buat hello-world.html di direktori proyek Anda. File ini menyediakan tata letak dan referensi widget ke skrip yang diperlukan.
<!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>
Widget berjalan di iframe, sehingga sebagian besar elemen kepala HTML kecuali <script> dan <link> diabaikan oleh kerangka kerja.
Langkah 3: Tambahkan widget JavaScript
Untuk menerapkan fungsionalitas widget, tambahkan skrip ini ke bagian <head> file HTML Anda:
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers"], function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget", function () {
return {
load: function (widgetSettings) {
var $title = $('h2.title');
$title.text('Hello World');
return WidgetHelpers.WidgetStatusHelper.Success();
}
};
});
VSS.notifyLoadSucceeded();
});
</script>
Komponen Kunci JavaScript
| Fungsi | Tujuan |
|---|---|
VSS.init() |
Menginisialisasi komunikasi antara widget dan Azure DevOps |
VSS.require() |
Memuat pustaka SDK dan pembantu widget yang diperlukan |
VSS.register() |
Mendaftarkan widget Anda dengan pengidentifikasi unik |
WidgetHelpers.IncludeWidgetStyles() |
Menerapkan pengaturan gaya default Azure DevOps |
VSS.notifyLoadSucceeded() |
Memberi tahu kerangka kerja bahwa pemuatan berhasil diselesaikan |
Penting
Nama widget di VSS.register() harus cocok dengan id di manifes ekstensi Anda (Langkah 5).
Langkah 4: Tambahkan gambar ekstensi
Buat gambar yang diperlukan untuk ekstensi Anda:
-
Logo ekstensi: Gambar 98x98 piksel bernama
logo.pngdalamimgfolder -
Ikon katalog widget: Gambar piksel 98x98 bernama
CatalogIcon.pngdiimgfolder -
Pratinjau widget: Gambar piksel 330x160 bernama
preview.pngdiimgfolder
Gambar-gambar ini ditampilkan di Marketplace dan katalog widget saat pengguna menelusuri ekstensi yang tersedia.
Langkah 5: Membuat manifes ekstensi
Buat vss-extension.json di direktori akar proyek Anda. File ini menentukan metadata dan kontribusi ekstensi Anda:
{
"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
}
]
}
Penting
Ganti "publisher": "fabrikam" dengan nama penerbit Anda yang sebenarnya. Pelajari cara membuat penerbit.
Properti asas manifes penting
| Bagian | Tujuan |
|---|---|
| Info dasar | Nama ekstensi, versi, deskripsi, dan penerbit |
| Ikon | Lokasi elemen visual perluasan Anda |
| Kontribusi | Definisi widget termasuk ID, jenis, dan properti |
| File | Semua file yang akan disertakan dalam paket ekstensi |
Untuk dokumentasi manifes lengkap, lihat Referensi manifes ekstensi.
Langkah 6: Mengemas dan menerbitkan ekstensi Anda
Kemas ekstensi Anda dan terbitkan ke Visual Studio Marketplace.
Menginstal alat pengemasan
npm i -g tfx-cli
Membuat paket ekstensi Anda
Dari direktori proyek Anda, jalankan:
tfx extension create --manifest-globs vss-extension.json
Tindakan ini membuat berkas .vsix yang berisi paket ekstensi Anda.
Menyiapkan penerbit
- Buka Portal Penerbitan Marketplace Visual Studio.
- Silakan masuk dan buat akun penerbit jika Anda belum memilikinya.
- Pilih pengidentifikasi penerbit unik (digunakan dalam file manifes Anda).
- Perbarui
vss-extension.jsonuntuk menggunakan nama penerbit Anda, bukan "fabrikam."
Unggah ekstensi Anda
- Di Portal Penerbitan, pilih Unggah ekstensi baru.
- Pilih file Anda
.vsixdan unggah. - Bagikan ekstensi dengan organisasi Azure DevOps Anda.
Atau, gunakan baris perintah:
tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization
Petunjuk / Saran
Gunakan --rev-version untuk secara otomatis menaikkan nomor versi saat memperbarui ekstensi yang ada.
Langkah 7: Menginstal dan menguji widget Anda
Untuk menguji, tambahkan widget Anda ke dasbor:
- Buka proyek Azure DevOps Anda:
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Buka Gambaran Umum>Dasbor.
- Pilih Tambahkan widget.
- Temukan widget Anda di katalog dan pilih Tambahkan.
Widget "Halo Dunia" Anda muncul di dasbor, menampilkan teks yang Anda konfigurasikan.
Langkah selanjutnya: Lanjutkan ke Bagian 2 untuk mempelajari cara mengintegrasikan REST API Azure DevOps ke widget Anda.
Bagian 2: Halo Dunia dengan REST API Azure DevOps
Perluas widget Anda untuk berinteraksi dengan data Azure DevOps menggunakan REST API. Contoh ini menunjukkan cara mengambil informasi kueri dan menampilkannya secara dinamis di widget Anda.
Di bagian ini, gunakan REST API Pelacakan Item Kerja untuk mengambil informasi tentang kueri yang ada dan menampilkan detail kueri di bawah teks "Halo Dunia".
Langkah 1: Buat file HTML yang disempurnakan
Buat file widget baru yang dibangun pada contoh sebelumnya. Salin hello-world.html dan ganti namanya menjadi hello-world2.html. Struktur proyek Anda sekarang mencakup:
|--- README.md
|--- node_modules
|--- sdk/
|--- scripts/
|--- VSS.SDK.min.js
|--- img/
|--- logo.png
|--- scripts/
|--- hello-world.html // Part 1 widget
|--- hello-world2.html // Part 2 widget (new)
|--- vss-extension.json // Extension manifest
Memperbarui struktur HTML widget
Buat perubahan ini pada hello-world2.html:
-
Menambahkan kontainer untuk data kueri: Sertakan elemen baru
<div>untuk menampilkan informasi kueri. -
Perbarui pengidentifikasi widget: Ubah nama widget dari
HelloWorldWidgetkeHelloWorldWidget2untuk identifikasi unik.
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers"], function (WidgetHelpers) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
return {
load: function (widgetSettings) {
var $title = $('h2.title');
$title.text('Hello World');
return WidgetHelpers.WidgetStatusHelper.Success();
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
Langkah 2: Mengonfigurasi izin akses API
Sebelum melakukan panggilan REST API, konfigurasikan izin yang diperlukan dalam manifes ekstensi Anda.
Menambahkan cakupan kerja
Cakupan vso.work memberikan akses baca-saja ke item dan kueri kerja. Tambahkan cakupan ini ke vss-extension.json:
{
"scopes": [
"vso.work"
]
}
Contoh manifes lengkap
Untuk manifes lengkap dengan properti lain, susun seperti ini:
{
"name": "example-widget",
"publisher": "example-publisher",
"version": "1.0.0",
"scopes": [
"vso.work"
]
}
Penting
Batasan cakupan: Menambahkan atau mengubah cakupan setelah penerbitan tidak didukung. Jika Anda sudah menerbitkan ekstensi, Anda harus menghapusnya dari Marketplace terlebih dahulu. Buka Portal Penerbitan Marketplace Visual Studio, temukan ekstensi Anda, dan pilih Hapus.
Langkah 3: Menerapkan integrasi REST API
Azure DevOps menyediakan pustaka klien JavaScript REST melalui SDK. Pustaka ini membungkus panggilan AJAX dan memetakan respons API ke objek yang dapat digunakan.
Memperbarui widget JavaScript
Ganti panggilan VSS.require dalam hello-world2.html Anda untuk menambahkan klien REST Pelacakan Item Kerja:
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to Azure DevOps Services
return WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Process query data (implemented in Step 4)
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
Detail implementasi utama
| Komponen | Tujuan |
|---|---|
WorkItemTrackingRestClient.getClient() |
Mendapatkan instans REST client untuk Pelacakan Item Kerja |
getQuery() |
Mengambil informasi kueri yang dibungkus dalam janji |
WidgetStatusHelper.Failure() |
Menyediakan penanganan kesalahan yang konsisten untuk kegagalan widget |
projectId |
Konteks proyek saat ini diperlukan untuk panggilan API |
Petunjuk / Saran
Jalur kueri kustom: Jika Anda tidak memiliki kueri "Umpan Balik" di "Kueri Bersama", ganti "Shared Queries/Feedback" dengan jalur ke kueri apa pun yang ada di proyek Anda.
Langkah 4: Menampilkan data respons API
Render informasi kueri di widget Anda dengan memproses respons REST API.
Menambahkan pemrosesan data kueri
// Process query data Ganti komentar dengan implementasi ini:
// 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);
Metode getQuery() mengembalikan Contracts.QueryHierarchyItem objek dengan properti untuk metadata kueri. Contoh ini menampilkan tiga bagian utama informasi di bawah teks "Halo Dunia".
Contoh kerja lengkap
File akhir hello-world2.html Anda akan terlihat seperti ini:
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
WidgetHelpers.IncludeWidgetStyles();
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to Azure DevOps Services
return WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName : "<unknown>")));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
// Use the widget helper and return failure as Widget Status
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
Langkah 5: Memperbarui manifes ekstensi
Untuk membuatnya tersedia di katalog widget, tambahkan widget baru Anda ke manifes ekstensi.
Menambahkan kontribusi widget kedua
Perbarui vss-extension.json untuk menyertakan widget yang diaktifkan REST API Anda. Tambahkan kontribusi ini ke contributions array:
{
"contributions": [
// ...existing HelloWorldWidget contribution...,
{
"id": "HelloWorldWidget2",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog"
],
"properties": {
"name": "Hello World Widget 2 (with API)",
"description": "My second widget",
"previewImageUrl": "img/preview2.png",
"uri": "hello-world2.html",
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
}
],
"files": [
{
"path": "hello-world.html",
"addressable": true
},
{
"path": "hello-world2.html",
"addressable": true
},
{
"path": "sdk/scripts",
"addressable": true
},
{
"path": "img",
"addressable": true
}
],
"scopes": [
"vso.work"
]
}
Petunjuk / Saran
Gambar pratinjau: Buat preview2.png gambar (330x160 piksel) dan letakkan di img folder untuk menampilkan tampilan widget Anda kepada pengguna di katalog.
Langkah 6: Mengemas, menerbitkan, dan berbagi
Paket, terbitkan, dan bagikan ekstensi Anda. Jika Anda sudah menerbitkan ekstensi, Anda dapat mengemas ulang dan memperbaruinya langsung di Marketplace.
Langkah 7: Uji widget REST API Anda
Untuk melihat tindakan integrasi REST API, tambahkan widget baru ke dasbor Anda:
- Buka proyek Azure DevOps Anda:
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Pilih Gambaran Umum>Dasbor.
- Pilih Tambahkan widget.
- Temukan "Hello World Widget 2 (dengan API)" dan pilih Tambahkan.
Widget Anda yang disempurnakan menampilkan teks "Halo Dunia" dan informasi kueri langsung dari proyek Azure DevOps Anda.
Langkah berikutnya: Lanjutkan ke Bagian 3 untuk menambahkan opsi konfigurasi yang memungkinkan pengguna menyesuaikan kueri mana yang akan ditampilkan.
Bagian 3: Mengonfigurasi Halo Dunia
Kembangkan dari Bagian 2 dengan menambahkan fitur konfigurasi pengguna ke widget Anda. Alih-alih mengkodekan jalur kueri secara permanen, buat antarmuka konfigurasi yang memungkinkan pengguna memilih kueri mana yang akan ditampilkan, dengan fungsionalitas pratinjau langsung.
Bagian ini menunjukkan cara membuat widget yang dapat dikonfigurasi yang dapat disesuaikan pengguna dengan kebutuhan spesifik mereka sambil memberikan umpan balik real-time selama konfigurasi.
Langkah 1: Membuat file konfigurasi
Konfigurasi widget berbagi banyak kesamaan dengan widget itu sendiri—keduanya menggunakan pola SDK, struktur HTML, dan JavaScript yang sama, tetapi melayani tujuan yang berbeda dalam kerangka kerja ekstensi.
Menyiapkan struktur proyek
Untuk mendukung konfigurasi widget, buat dua file baru:
- Salin
hello-world2.htmldan ganti namanya menjadihello-world3.html, widget yang dapat dikonfigurasi. - Buat file baru bernama
configuration.html, yang menangani antarmuka konfigurasi.
Struktur proyek Anda sekarang mencakup:
|--- README.md
|--- sdk/
|--- node_modules
|--- scripts/
|--- VSS.SDK.min.js
|--- img/
|--- logo.png
|--- scripts/
|--- configuration.html // New: Configuration interface
|--- hello-world.html // Part 1: Basic widget
|--- hello-world2.html // Part 2: REST API widget
|--- hello-world3.html // Part 3: Configurable widget (new)
|--- vss-extension.json // Extension manifest
Membuat antarmuka konfigurasi
Tambahkan struktur HTML ini ke configuration.html, yang membuat pemilih dropdown untuk memilih kueri:
<!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>
Langkah 2: Menerapkan konfigurasi JavaScript
Konfigurasi JavaScript mengikuti pola inisialisasi yang sama dengan widget, tetapi mengimplementasikan IWidgetConfiguration kontrak alih-alih kontrak dasar IWidget.
Menambahkan logika konfigurasi
Sisipkan skrip ini ke <head> bagian dari configuration.html:
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers"], function (WidgetHelpers) {
VSS.register("HelloWorldWidget.Configuration", function () {
var $queryDropdown = $("#query-path-dropdown");
return {
load: function (widgetSettings, widgetConfigurationContext) {
var settings = JSON.parse(widgetSettings.customSettings.data);
if (settings && settings.queryPath) {
$queryDropdown.val(settings.queryPath);
}
return WidgetHelpers.WidgetStatusHelper.Success();
},
onSave: function() {
var customSettings = {
data: JSON.stringify({
queryPath: $queryDropdown.val()
})
};
return WidgetHelpers.WidgetConfigurationSave.Valid(customSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
Detail kontrak konfigurasi
Kontrak IWidgetConfiguration memerlukan fungsi utama ini:
| Fungsi | Tujuan | Ketika dipanggil |
|---|---|---|
load() |
Inisialisasi antarmuka konfigurasi dengan pengaturan yang sudah ada | Saat dialog konfigurasi terbuka |
onSave() |
Menserialisasikan input pengguna dan memvalidasi pengaturan | Saat pengguna memilih Simpan |
Petunjuk / Saran
Serialisasi data: Contoh ini menggunakan JSON untuk menserialisasikan pengaturan. Widget mengakses pengaturan ini melalui widgetSettings.customSettings.data dan harus mendeserialisasinya dengan sesuai.
Langkah 3: Mengaktifkan fungsionalitas pratinjau langsung
Pratinjau langsung memungkinkan pengguna untuk melihat perubahan widget segera saat mereka memodifikasi pengaturan konfigurasi, memberikan umpan balik instan sebelum menyimpan.
Menerapkan pemberitahuan perubahan
Untuk mengaktifkan pratinjau langsung, tambahkan penanganan aktivitas ini dalam load fungsi:
$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);
});
Selesaikan file konfigurasi
Akhir configuration.html Anda akan terlihat seperti ini:
<!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>
Penting
Tombol Aktifkan Simpan: Kerangka kerja memerlukan setidaknya satu pemberitahuan perubahan konfigurasi untuk mengaktifkan tombol Simpan . Penanganan aktivitas perubahan memastikan tindakan ini terjadi saat pengguna memilih opsi.
Langkah 4: Membuat widget dapat dikonfigurasi
Ubah widget Anda dari Bagian 2 untuk menggunakan data konfigurasi alih-alih nilai yang dikodekan secara permanen. Langkah ini memerlukan penerapan kontrak IConfigurableWidget.
Memperbarui pendaftaran widget
Di hello-world3.html, buat perubahan ini:
-
Perbarui ID widget: Ubah dari
HelloWorldWidget2keHelloWorldWidget3. -
Tambahkan fungsi muat ulang: Terapkan
IConfigurableWidgetkontrak.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Menangani data konfigurasi
getQueryInfo Perbarui fungsi untuk menggunakan pengaturan konfigurasi alih-alih jalur kueri yang dikodekan secara permanen:
var settings = JSON.parse(widgetSettings.customSettings.data);
if (!settings || !settings.queryPath) {
var $container = $('#query-info-container');
$container.empty();
$container.text("Please configure a query path to display data.");
return WidgetHelpers.WidgetStatusHelper.Success();
}
Perbedaan siklus hidup widget
| Fungsi | Tujuan | Panduan penggunaan |
|---|---|---|
load() |
Penyajian widget awal dan penyiapan satu kali | Operasi berat, inisialisasi sumber daya |
reload() |
Memperbarui widget dengan konfigurasi baru | Pembaruan ringan, penyegaran data |
Petunjuk / Saran
Pengoptimalan performa: Gunakan load() untuk operasi mahal yang hanya perlu berjalan sekali, dan reload() untuk pembaruan cepat saat konfigurasi berubah.
(Opsional) Menambahkan lightbox untuk informasi terperinci
Widget dasbor memiliki ruang terbatas, sehingga menantang untuk menampilkan informasi yang komprehensif. Lightbox memberikan solusi yang elegan dengan menampilkan data terperinci dalam overlay modal tanpa berpindah dari dasbor.
Mengapa menggunakan lightbox di widget?
| Keuntungan | Deskripsi |
|---|---|
| Efisiensi ruang | Jaga widget tetap ringkas saat menawarkan tampilan terperinci |
| Pengalaman pengguna | Pertahankan konteks dasbor sambil menampilkan informasi lebih lanjut |
| Pengungkapan progresif | Tampilkan data ringkasan di widget, detail sesuai permintaan |
| desain responsif | Beradaptasi dengan ukuran layar dan konfigurasi widget yang berbeda |
Menerapkan elemen yang dapat diklik
Perbarui penyajian data kueri Anda untuk menyertakan elemen yang dapat diklik yang memicu lightbox:
// Create a list with clickable query details
var $list = $('<ul class="query-summary">');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName : "<unknown>"));
// Add a clickable element to open detailed view
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
// Append to the container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
Mengembangkan fungsi lightbox
Tambahkan implementasi lightbox ini ke javaScript widget Anda:
function showQueryDetails(query) {
// Create lightbox overlay
var $overlay = $('<div class="lightbox-overlay">');
var $lightbox = $('<div class="lightbox-content">');
// Add close button
var $closeBtn = $('<button class="lightbox-close">×</button>');
$closeBtn.on('click', function() {
$overlay.remove();
});
// Create detailed content
var $content = $('<div class="query-details">');
$content.append($('<h3>').text(query.name || 'Query Details'));
$content.append($('<p>').html('<strong>ID:</strong> ' + query.id));
$content.append($('<p>').html('<strong>Path:</strong> ' + query.path));
$content.append($('<p>').html('<strong>Created:</strong> ' + (query.createdDate ? new Date(query.createdDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Modified:</strong> ' + (query.lastModifiedDate ? new Date(query.lastModifiedDate).toLocaleDateString() : 'Unknown')));
$content.append($('<p>').html('<strong>Created By:</strong> ' + (query.createdBy ? query.createdBy.displayName : 'Unknown')));
$content.append($('<p>').html('<strong>Modified By:</strong> ' + (query.lastModifiedBy ? query.lastModifiedBy.displayName : 'Unknown')));
if (query.queryType) {
$content.append($('<p>').html('<strong>Type:</strong> ' + query.queryType));
}
// Assemble lightbox
$lightbox.append($closeBtn);
$lightbox.append($content);
$overlay.append($lightbox);
// Add to document and show
$('body').append($overlay);
// Close on overlay click
$overlay.on('click', function(e) {
if (e.target === $overlay[0]) {
$overlay.remove();
}
});
// Close on Escape key
$(document).on('keydown.lightbox', function(e) {
if (e.keyCode === 27) { // Escape key
$overlay.remove();
$(document).off('keydown.lightbox');
}
});
}
Menambahkan gaya lightbox
Sertakan gaya CSS untuk lightbox di bagian HTML <head> widget Anda:
<style>
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
Implementasi widget yang ditingkatkan
Widget lengkap Anda yang disempurnakan dengan fungsionalitas lightbox:
<!DOCTYPE html>
<html>
<head>
<script src="sdk/scripts/VSS.SDK.min.js"></script>
<style>
/* Lightbox styles from above */
.query-summary {
list-style: none;
padding: 0;
margin: 10px 0;
}
.query-summary li {
padding: 2px 0;
font-size: 12px;
}
.details-link {
background: #0078d4;
color: white;
border: none;
padding: 4px 8px;
font-size: 11px;
cursor: pointer;
border-radius: 2px;
margin-top: 8px;
}
.details-link:hover {
background: #106ebe;
}
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-content {
background: white;
border-radius: 4px;
padding: 20px;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
line-height: 1;
}
.lightbox-close:hover {
color: #000;
}
.query-details h3 {
margin-top: 0;
color: #323130;
}
.query-details p {
margin: 8px 0;
font-size: 14px;
line-height: 1.4;
}
</style>
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require(["AzureDevOps/Dashboards/WidgetHelpers", "AzureDevOps/WorkItemTracking/RestClient"],
function (WidgetHelpers, WorkItemTrackingRestClient) {
WidgetHelpers.IncludeWidgetStyles();
function showQueryDetails(query) {
// Lightbox implementation from above
}
VSS.register("HelloWorldWidget2", function () {
var projectId = VSS.getWebContext().project.id;
var getQueryInfo = function (widgetSettings) {
return WorkItemTrackingRestClient.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Enhanced display with lightbox trigger
var $list = $('<ul class="query-summary">');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName : "<unknown>")));
var $detailsLink = $('<button class="details-link">View Details</button>');
$detailsLink.on('click', function() {
showQueryDetails(query);
});
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
$container.append($detailsLink);
return WidgetHelpers.WidgetStatusHelper.Success();
}, function (error) {
return WidgetHelpers.WidgetStatusHelper.Failure(error.message);
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
}
}
});
VSS.notifyLoadSucceeded();
});
</script>
</head>
<body>
<div class="widget">
<h2 class="title"></h2>
<div id="query-info-container"></div>
</div>
</body>
</html>
Pertimbangan aksesibilitas: Pastikan lightbox Anda dapat diakses keyboard dan menyertakan label yang tepat untuk pembaca layar. Uji dengan fitur aksesibilitas bawaan Azure DevOps.
Penting
Performa: Lightbox harus dimuat dengan cepat. Pertimbangkan untuk memuat data terperinci secara tertunda hanya saat lightbox terbuka, daripada mengambil semuanya di awal.
Langkah 5: Mengonfigurasi manifes ekstensi
Daftarkan widget yang dapat dikonfigurasi dan antarmuka konfigurasinya di manifes ekstensi Anda.
Menambahkan kontribusi widget dan konfigurasi
Perbarui vss-extension.json untuk menyertakan dua kontribusi baru:
{
"contributions": [
{
"id": "HelloWorldWidget3",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog",
"fabrikam.azuredevops-extensions-myExtensions.HelloWorldWidget.Configuration"
],
"properties": {
"name": "Hello World Widget 3 (with config)",
"description": "My third widget",
"previewImageUrl": "img/preview3.png",
"uri": "hello-world3.html",
"supportedSizes": [
{
"rowSpan": 1,
"columnSpan": 2
},
{
"rowSpan": 2,
"columnSpan": 2
}
],
"supportedScopes": ["project_team"]
}
},
{
"id": "HelloWorldWidget.Configuration",
"type": "ms.vss-dashboards-web.widget-configuration",
"targets": [ "ms.vss-dashboards-web.widget-configuration" ],
"properties": {
"name": "HelloWorldWidget Configuration",
"description": "Configures HelloWorldWidget",
"uri": "configuration.html"
}
}
],
"files": [
{
"path": "hello-world.html", "addressable": true
},
{
"path": "hello-world2.html", "addressable": true
},
{
"path": "hello-world3.html", "addressable": true
},
{
"path": "configuration.html", "addressable": true
},
{
"path": "sdk/scripts", "addressable": true
},
{
"path": "img", "addressable": true
}
]
}
Persyaratan kontribusi konfigurasi
| Properti | Tujuan | Nilai yang diperlukan |
|---|---|---|
type |
Mengidentifikasi kontribusi sebagai pengaturan widget | ms.vss-dashboards-web.widget-configuration |
targets |
Tempat konfigurasi muncul | ms.vss-dashboards-web.widget-configuration |
uri |
Jalur ke file HTML konfigurasi | Jalur file konfigurasi Anda |
Pola penargetan widget
Untuk widget yang dapat dikonfigurasi targets , array harus menyertakan referensi ke konfigurasi:
<publisher>.<extension-id>.<configuration-id>
Peringatan
Visibilitas tombol konfigurasi: Jika widget tidak menargetkan kontribusi konfigurasinya dengan benar, tombol Konfigurasikan tidak muncul. Verifikasi bahwa nama penerbit dan ekstensi sama persis dengan manifes Anda.
Langkah 6: Mengemas, menerbitkan, dan berbagi
Sebarkan ekstensi yang disempurnakan dengan kemampuan konfigurasi.
Jika ini adalah publikasi pertama Anda, ikuti Langkah 6: Paket, terbitkan, dan bagikan. Untuk ekstensi yang ada, kemas ulang dan perbarui langsung di Marketplace.
Langkah 7: Uji widget yang dapat dikonfigurasi
Rasakan alur kerja konfigurasi lengkap dengan menambahkan dan mengonfigurasi widget Anda.
Menambahkan widget ke dasbor Anda
- Pergi ke
https://dev.azure.com/{Your_Organization}/{Your_Project}. - Buka Gambaran Umum>Dasbor.
- Pilih Tambahkan widget.
- Temukan "Hello World Widget 3 (dengan konfigurasi)" dan pilih Tambahkan.
Perintah konfigurasi ditampilkan karena widget memerlukan penyiapan:
Mengonfigurasi widget
Konfigurasi akses melalui salah satu metode:
- Menu Widget: Arahkan mouse ke atas widget, pilih elipsis (⋯), lalu Konfigurasikan
- Mode edit dasbor: Pilih Edit di dasbor, lalu tombol konfigurasikan pada widget
Panel konfigurasi terbuka dengan pratinjau langsung di tengah. Pilih kueri dari menu dropdown untuk melihat pembaruan langsung, lalu pilih Simpan untuk menerapkan perubahan Anda.
Langkah 8: Tambahkan opsi konfigurasi tingkat lanjut
Perluas widget Anda dengan lebih banyak fitur konfigurasi bawaan seperti nama dan ukuran kustom.
Mengaktifkan konfigurasi nama dan ukuran
Azure DevOps menyediakan dua fitur yang dapat dikonfigurasi secara langsung:
| Fitur | Properti manifes | Tujuan |
|---|---|---|
| Nama Khusus | isNameConfigurable: true |
Pengguna dapat mengambil alih nama widget default |
| Beberapa ukuran | Beberapa supportedSizes entri |
Pengguna dapat mengubah ukuran widget |
Contoh manifes yang telah ditingkatkan
{
"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"]
}
}
]
}
Menampilkan nama yang dikonfigurasi
Untuk menampilkan nama widget kustom, perbarui widget Anda untuk menggunakan widgetSettings.name:
return {
load: function (widgetSettings) {
// Display configured name instead of hard-coded text
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
// Update name during configuration changes
var $title = $('h2.title');
$title.text(widgetSettings.name);
return getQueryInfo(widgetSettings);
}
}
Setelah memperbarui ekstensi, Anda dapat mengonfigurasi nama dan ukuran widget:
Kemas ulang dan perbarui ekstensi Anda untuk mengaktifkan opsi konfigurasi tingkat lanjut ini.
Selamat! Anda membuat widget dasbor Azure DevOps yang lengkap dan dapat dikonfigurasi dengan kemampuan pratinjau langsung dan opsi penyesuaian pengguna.