Menambahkan widget dasbor
Layanan Azure DevOps | Azure DevOps Server 2022 - Azure DevOps Server 2019
Widget di dasbor diimplementasikan sebagai kontribusi dalam kerangka kerja ekstensi. Satu ekstensi dapat memiliki beberapa kontribusi. Pelajari cara membuat ekstensi dengan beberapa widget sebagai kontribusi.
Artikel ini dibagi menjadi tiga bagian, masing-masing bangunan pada sebelumnya - dimulai dengan widget sederhana dan diakhir dengan widget komprehensif.
Tip
Lihat dokumentasi terbaru kami tentang pengembangan ekstensi menggunakan Azure DevOps Extension SDK.
Persiapan dan penyiapan yang diperlukan untuk tutorial ini
Untuk membuat ekstensi untuk Azure DevOps atau TFS, ada beberapa perangkat lunak dan alat prasyarat yang Anda perlukan:
Pengetahuan: Beberapa pengetahuan tentang JavaScript, HTML, CSS diperlukan untuk pengembangan widget.
- Organisasi di Azure DevOps untuk menginstal dan menguji widget Anda, informasi selengkapnya dapat ditemukan di sini
- Editor teks. Untuk banyak tutorial, kami menggunakan
Visual Studio Code
, yang dapat diunduh di sini - Versi node terbaru, yang dapat diunduh di sini
- CLI lintas platform untuk Azure DevOps (tfx-cli) untuk mengemas ekstensi Anda.
- tfx-cli dapat diinstal menggunakan
npm
, komponen Node.js dengan menjalankannpm i -g tfx-cli
- tfx-cli dapat diinstal menggunakan
- Direktori rumah untuk proyek Anda. Direktori ini disebut sebagai
home
sepanjang tutorial.
Struktur file ekstensi:
|--- 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
Apa yang akan Anda temukan dalam tutorial
- Bagian pertama dari panduan ini menunjukkan kepada Anda cara membuat widget baru, yang mencetak pesan "Halo Dunia" sederhana.
- Bagian kedua dibangun pada bagian pertama dengan menambahkan panggilan ke REST API Azure DevOps.
- Bagian ketiga menjelaskan cara menambahkan konfigurasi ke widget Anda.
Catatan
Jika Anda terburu-buru dan ingin segera mendapatkan kode, Anda dapat mengunduh sampel di sini.
Setelah diunduh, buka widgets
folder, lalu ikuti Langkah 6 dan Langkah 7 secara langsung untuk menerbitkan ekstensi sampel yang memiliki tiga widget sampel dari berbagai kompleksitas.
Mulailah dengan beberapa gaya dasar untuk widget yang kami sediakan di luar kotak untuk Anda dan beberapa panduan tentang struktur widget.
Bagian 1: Halo Dunia
Bagian ini menyajikan widget yang mencetak "Halo Dunia" menggunakan JavaScript.
Langkah 1: Dapatkan SDK klien - VSS.SDK.min.js
Skrip SDK inti, VSS.SDK.min.js
, memungkinkan ekstensi web untuk berkomunikasi ke bingkai Azure DevOps host. Skrip melakukan operasi seperti menginisialisasi, memberi tahu ekstensi dimuat, atau mendapatkan konteks tentang halaman saat ini.
Dapatkan file SDK VSS.SDK.min.js
Klien dan tambahkan ke aplikasi web Anda. Tempatkan di home/sdk/scripts
folder .
Gunakan perintah 'npm install' untuk mengambil SDK:
npm install vss-web-extension-sdk
Untuk mempelajari lebih lanjut tentang SDK, kunjungi Halaman GitHub SDK Klien.
Langkah 2: Halaman HTML Anda - hello-world.html
Halaman HTML Anda adalah lem yang menyimpan tata letak Anda bersama-sama dan menyertakan referensi ke CSS dan JavaScript.
Anda dapat memberi nama file ini apa pun, pastikan untuk memperbarui semua referensi dengan hello-world
nama yang Anda gunakan.
Widget Anda berbasis HTML dan dihosting dalam iframe.
Tambahkan HTML di bawah ini di hello-world.html
. Kami menambahkan referensi wajib ke VSS.SDK.min.js
file dan menyertakan h2
elemen dalam , yang diperbarui dengan string Halo Dunia pada langkah mendatang.
<!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>
Meskipun kita menggunakan file HTML, sebagian besar elemen kepala HTML selain skrip dan tautan diabaikan oleh kerangka kerja.
Langkah 3: JavaScript Anda
Kami menggunakan JavaScript untuk merender konten di widget. Dalam artikel ini, kami membungkus semua kode JavaScript kami di dalam <script>
elemen dalam file HTML. Anda dapat memilih untuk memiliki kode ini dalam file JavaScript terpisah dan merujuknya dalam file HTML.
Kode merender konten. Kode JavaScript ini juga menginisialisasi VSS SDK, memetakan kode untuk widget Anda ke nama widget Anda, dan memberi tahu kerangka kerja ekstensi keberhasilan atau kegagalan widget.
Dalam kasus kami, di bawah ini adalah kode yang akan mencetak "Halo Dunia" di widget. Tambahkan elemen ini script
di head
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
menginisialisasi jabat tangan antara iframe yang menghosting widget dan bingkai host.
Kami meneruskan explicitNotifyLoaded: true
sehingga widget dapat secara eksplisit memberi tahu host ketika kami selesai memuat. Kontrol ini memungkinkan kami untuk memberi tahu penyelesaian beban setelah memastikan bahwa modul dependen dimuat.
Kami meneruskan usePlatformStyles: true
sehingga gaya inti Azure DevOps untuk elemen html (seperti isi, div, dan sebagainya) dapat digunakan oleh Widget. Jika widget lebih suka tidak menggunakan gaya ini, mereka dapat meneruskan usePlatformStyles: false
.
VSS.require
digunakan untuk memuat pustaka skrip VSS yang diperlukan. Panggilan ke metode ini secara otomatis memuat pustaka umum seperti JQuery dan JQueryUI.
Dalam kasus kami, kami bergantung pada pustaka WidgetHelpers, yang digunakan untuk mengkomunikasikan status widget ke kerangka kerja widget.
Jadi, kami meneruskan nama TFS/Dashboards/WidgetHelpers
modul yang sesuai dan panggilan balik ke VSS.require
.
Panggilan balik dipanggil setelah modul dimuat.
Panggilan balik memiliki sisa kode JavaScript yang diperlukan untuk widget. Di akhir panggilan balik, kami memanggil VSS.notifyLoadSucceeded
untuk memberi tahu penyelesaian beban.
WidgetHelpers.IncludeWidgetStyles
termasuk lembar gaya dengan beberapa css dasar untuk memulai. Pastikan untuk membungkus konten Anda di dalam elemen HTML dengan kelas widget
untuk menggunakan gaya ini.
VSS.register
digunakan untuk memetakan fungsi di JavaScript, yang secara unik mengidentifikasi widget di antara berbagai kontribusi dalam ekstensi Anda. Nama harus cocok dengan id
yang mengidentifikasi kontribusi Anda seperti yang dijelaskan di Langkah 5. Untuk widget, fungsi yang diteruskan harus VSS.register
mengembalikan objek yang memenuhi IWidget
kontrak, misalnya, objek yang dikembalikan harus memiliki properti beban yang nilainya adalah fungsi lain yang memiliki logika inti untuk merender widget.
Dalam kasus kami, ini untuk memperbarui teks h2
elemen ke "Halo Dunia".
Fungsi inilah yang dipanggil ketika kerangka kerja widget membuat instans widget Anda.
Kami menggunakan WidgetStatusHelper
dari WidgetHelpers untuk mengembalikan sebagai sukses WidgetStatus
.
Peringatan
Jika nama yang digunakan untuk mendaftarkan widget tidak cocok dengan ID untuk kontribusi dalam manifes, maka widget berfungsi secara tidak terduga.
vss-extension.json
harus selalu berada di akar folder (dalam panduan ini, HelloWorld
). Untuk semua file lainnya, Anda dapat menempatkannya dalam struktur apa pun yang Anda inginkan di dalam folder, cukup pastikan untuk memperbarui referensi dengan tepat dalam file HTML dan dalam vss-extension.json
manifes.
Langkah 4: Logo ekstensi Anda: logo.png
Logo Anda ditampilkan di Marketplace, dan di katalog widget setelah pengguna menginstal ekstensi Anda.
Anda memerlukan ikon katalog 98 px x 98 px. Pilih gambar, beri nama logo.png
, dan letakkan img
di folder .
Untuk mendukung TFS 2015 Update 3, Anda memerlukan gambar tambahan yaitu 330 px x 160 px. Gambar pratinjau ini ditampilkan dalam katalog ini. Pilih gambar, beri nama preview.png
, dan letakkan img
di folder seperti sebelumnya.
Anda dapat memberi nama gambar ini sesampainya selama manifes ekstensi di langkah berikutnya diperbarui dengan nama yang Anda gunakan.
Langkah 5: Manifes ekstensi Anda: vss-extension.json
- Setiap ekstensi harus memiliki file manifes ekstensi
- Membaca referensi manifes ekstensi
- Cari tahu lebih lanjut tentang titik kontribusi di Poin ekstensibilitas
Buat file json (vss-extension.json
, misalnya) di home
direktori dengan konten berikut:
{
"manifestVersion": 1,
"id": "vsts-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
}
]
}
Untuk informasi selengkapnya tentang atribut yang diperlukan, lihat Referensi manifes ekstensi
Catatan
Penerbit di sini perlu diubah ke nama penerbit Anda. Untuk membuat penerbit sekarang, kunjungi Paket/Terbitkan/Instal.
Ikon
Ikon stanza menentukan jalur ke ikon ekstensi Anda dalam manifes Anda.
Kontribusi
Setiap entri kontribusi mendefinisikan properti.
- ID untuk mengidentifikasi kontribusi Anda. ID ini harus unik dalam ekstensi. ID ini harus cocok dengan nama yang Anda gunakan di Langkah 3 untuk mendaftarkan widget Anda.
- Jenis kontribusi. Untuk semua widget, jenisnya harus
ms.vss-dashboards-web.widget
. - Array target tempat kontribusi berkontribusi. Untuk semua widget, targetnya harus
[ms.vss-dashboards-web.widget-catalog]
. - Properti adalah objek yang menyertakan properti untuk jenis kontribusi. Untuk widget, properti berikut wajib.
Properti | Deskripsi |
---|---|
nama | Nama widget yang akan ditampilkan di katalog widget. |
description | Deskripsi widget untuk ditampilkan di katalog widget. |
catalogIconUrl | Jalur relatif ikon katalog yang Anda tambahkan di Langkah 4 untuk ditampilkan di katalog widget. Gambar harus 98 px x 98 px. Jika Anda telah menggunakan struktur folder yang berbeda atau nama file yang berbeda, tentukan jalur relatif yang sesuai di sini. |
previewImageUrl | Jalur relatif gambar pratinjau yang Anda tambahkan di Langkah 4 untuk ditampilkan di katalog widget hanya untuk TFS 2015 Update 3. Gambar harus 330 px x 160 px. Jika Anda telah menggunakan struktur folder yang berbeda atau nama file yang berbeda, tentukan jalur relatif yang sesuai di sini. |
uri | Jalur relatif file HTML yang Anda tambahkan di Langkah 1. Jika Anda telah menggunakan struktur folder yang berbeda atau nama file yang berbeda, tentukan jalur relatif yang sesuai di sini. |
supportedSizes | Array ukuran yang didukung oleh widget Anda. Saat widget mendukung beberapa ukuran, ukuran pertama dalam array adalah ukuran default widget. widget size ditentukan untuk baris dan kolom yang ditempati oleh widget di kisi dasbor. Satu baris/kolom sesuai dengan 160 px. Dimensi apa pun di atas 1x1 mendapatkan tambahan 10 px yang mewakili selokan di antara widget. Misalnya, widget 160*3+10*2 3x2 lebar dan 160*2+10*1 tinggi. Ukuran maksimum yang didukung adalah 4x4 . |
cakupan yang didukung | Saat ini, kami hanya mendukung dasbor tim. Nilainya harus project_team . Di masa mendatang, ketika kami mendukung cakupan dasbor lain, akan ada lebih banyak opsi untuk dipilih dari sini. |
File
Stanza file menyatakan file yang ingin Anda sertakan dalam paket Anda - halaman HTML Anda, skrip Anda, skrip SDK, dan logo Anda.
Atur addressable
ke true
kecuali Anda menyertakan file lain yang tidak perlu dapat diatasi URL.
Catatan
Untuk informasi selengkapnya tentang file manifes ekstensi, seperti propertinya dan apa yang mereka lakukan, lihat referensi manifes ekstensi.
Langkah 6: Mengemas, menerbitkan, dan berbagi
Setelah Anda menulis ekstensi, langkah selanjutnya untuk memasukkannya ke Marketplace adalah mengemas semua file Anda bersama-sama. Semua ekstensi dimas sebagai file .vsix yang kompatibel dengan VSIX 2.0 - Microsoft menyediakan antarmuka baris perintah lintas platform (CLI) untuk mengemas ekstensi Anda.
Mendapatkan alat pengemasan
Anda dapat menginstal atau memperbarui CLI Lintas platform untuk Azure DevOps (tfx-cli) menggunakan npm
, komponen Node.js, dari baris perintah Anda.
npm i -g tfx-cli
Mengemas ekstensi Anda
Mengemas ekstensi Anda ke dalam file .vsix mudah setelah Anda memiliki tfx-cli. Buka direktori beranda ekstensi Anda dan jalankan perintah berikut.
tfx extension create --manifest-globs vss-extension.json
Catatan
Versi ekstensi/integrasi harus ditahapkan pada setiap pembaruan.
Saat memperbarui ekstensi yang ada, perbarui versi dalam manifes atau lewati --rev-version
sakelar baris perintah. Ini akan menaikkan nomor versi patch ekstensi Anda dan menyimpan versi baru ke manifes Anda.
Setelah Anda memiliki ekstensi paket dalam file .vsix, Anda siap untuk menerbitkan ekstensi Anda ke Marketplace.
Membuat penerbit untuk ekstensi
Semua ekstensi, termasuk ekstensi dari Microsoft, diidentifikasi sebagai disediakan oleh penerbit. Jika Anda belum menjadi anggota penerbit yang sudah ada, Anda akan membuatnya.
- Masuk ke Portal Penerbitan Marketplace Visual Studio
- Jika Anda belum menjadi anggota penerbit yang sudah ada, Anda akan diminta untuk membuat penerbit. Jika Anda tidak diminta untuk membuat penerbit, gulir ke bawah ke bagian bawah halaman dan pilih Terbitkan Ekstensi di bawah Situs Terkait.
- Tentukan pengidentifikasi untuk penerbit Anda, misalnya:
mycompany-myteam
- Pengidentifikasi digunakan sebagai nilai untuk
publisher
atribut dalam file manifes ekstensi Anda.
- Pengidentifikasi digunakan sebagai nilai untuk
- Tentukan nama tampilan untuk penerbit Anda, misalnya:
My Team
- Tentukan pengidentifikasi untuk penerbit Anda, misalnya:
- Tinjau Perjanjian Penerbit Marketplace dan pilih Buat
Sekarang penerbit Anda ditentukan. Dalam rilis mendatang, Anda dapat memberikan izin untuk melihat dan mengelola ekstensi penerbit Anda. Mudah dan lebih aman bagi tim dan organisasi untuk menerbitkan ekstensi di bawah penerbit umum, tetapi tanpa perlu berbagi serangkaian kredensial di sekumpulan pengguna.
vss-extension.json
Perbarui file manifes dalam sampel untuk mengganti ID fabrikam
penerbit dummy dengan ID penerbit Anda.
Menerbitkan dan berbagi ekstensi
Setelah membuat penerbit, Anda sekarang dapat mengunggah ekstensi Anda ke Marketplace.
- Temukan tombol Unggah ekstensi baru, navigasikan ke file .vsix paket Anda, dan pilih unggah.
Anda juga dapat mengunggah ekstensi melalui baris perintah dengan menggunakan tfx extension publish
perintah alih-alih tfx extension create
mengemas dan menerbitkan ekstensi Anda dalam satu langkah.
Anda dapat secara opsional menggunakan --share-with
untuk berbagi ekstensi Anda dengan satu atau beberapa akun setelah penerbitan.
Anda juga memerlukan token akses pribadi.
tfx extension publish --manifest-globs your-manifest.json --share-with yourOrganization
Langkah 7: Tambahkan widget dari katalog
Buka proyek Anda di Azure DevOps,
http://dev.azure.com/{yourOrganization}/{yourProject}
Pilih Gambaran Umum, lalu pilih Dasbor.
Pilih Tambahkan widget.
Sorot widget Anda, lalu pilih Tambahkan.
Widget muncul di dasbor Anda.
Bagian 2: Halo Dunia dengan REST API Azure DevOps
Widget dapat memanggil salah satu REST API di Azure DevOps untuk berinteraksi dengan sumber daya Azure DevOps. Dalam contoh ini, kami menggunakan REST API untuk WorkItemTracking untuk mengambil informasi tentang kueri yang ada dan menampilkan beberapa info kueri di widget tepat di bawah teks "Halo Dunia".
Langkah 1: HTML
Salin file hello-world.html
dari contoh sebelumnya, dan ganti nama salinan menjadi hello-world2.html
. Folder Anda sekarang terlihat seperti di bawah ini:
|--- 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
|--- hello-world2.html // renamed copy of hello-world.html
|--- vss-extension.json // extension's manifest
Tambahkan elemen 'div' baru tepat di bawah 'h2' untuk menyimpan informasi kueri. Perbarui nama widget dari 'HelloWorldWidget' ke 'HelloWorldWidget2' di baris tempat Anda memanggil 'VSS.register'. Ini memungkinkan kerangka kerja untuk mengidentifikasi widget secara unik dalam ekstensi.
<!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>
Langkah 2: Mengakses sumber daya Azure DevOps
Untuk mengaktifkan akses ke sumber daya Azure DevOps, cakupan perlu ditentukan dalam manifes ekstensi. Kami menambahkan cakupan ke vso.work
manifes kami.
Cakupan ini menunjukkan widget memerlukan akses baca-saja ke kueri dan item kerja. Lihat semua cakupan yang tersedia di sini.
Tambahkan di bawah ini di akhir manifes ekstensi Anda.
{
...,
"scopes":[
"vso.work"
]
}
Peringatan
Menambahkan atau mengubah cakupan setelah menerbitkan ekstensi saat ini tidak didukung. Jika Anda telah mengunggah ekstensi, hapus ekstensi dari Marketplace. Buka , klik kanan ekstensi Anda dan pilih "Hapus".
Langkah 3: Lakukan Panggilan REST API
Ada banyak pustaka sisi klien yang dapat diakses melalui SDK untuk melakukan panggilan REST API di Azure DevOps. Pustaka ini disebut klien REST dan merupakan pembungkus JavaScript di sekitar panggilan Ajax untuk semua titik akhir sisi server yang tersedia. Anda dapat menggunakan metode yang disediakan oleh klien ini alih-alih menulis panggilan Ajax sendiri. Metode ini memetakan respons API ke objek yang dapat digunakan oleh kode Anda.
Dalam langkah ini, kami memperbarui VSS.require
panggilan untuk memuat TFS/WorkItemTracking/RestClient
, yang menyediakan klien WORKItemTracking REST.
Kita dapat menggunakan klien REST ini untuk mendapatkan informasi tentang kueri yang disebut Feedback
di bawah folder Shared Queries
.
Di dalam fungsi yang kita teruskan ke VSS.register
, kita membuat variabel untuk menahan ID proyek saat ini. Kita perlu variabel ini untuk mengambil kueri.
Kami juga membuat metode getQueryInfo
baru untuk menggunakan klien REST. Metode ini yang kemudian dipanggil dari metode pemuatan.
Metode getClient
ini memberikan instans klien REST yang kita butuhkan.
Metode getQuery
mengembalikan kueri yang dibungkus dalam janji.
Tampilan yang diperbarui VSS.require
adalah sebagai berikut:
VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/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();
});
Perhatikan penggunaan metode Kegagalan dari WidgetStatusHelper
.
Ini memungkinkan Anda untuk menunjukkan kepada kerangka kerja widget bahwa kesalahan telah terjadi dan memanfaatkan pengalaman kesalahan standar yang disediakan untuk semua widget.
Jika Anda tidak memiliki
Feedback
kueri di bawahShared Queries
folder, gantiShared Queries\Feedback
dalam kode dengan jalur kueri yang ada di proyek Anda.
Langkah 4: Menampilkan Respons
Langkah terakhir adalah merender informasi kueri di dalam widget.
Fungsi mengembalikan getQuery
objek jenis Contracts.QueryHierarchyItem
di dalam janji.
Dalam contoh ini, kami menampilkan ID kueri, nama kueri, dan nama pembuat kueri di bawah teks "Halo Dunia".
// Do something with the query
Ganti komentar dengan yang di bawah ini:
// Create a list with query details
var $list = $('<ul>');
$list.append($('- ').text("Query Id: " + query.id));
$list.append($('- ').text("Query Name: " + query.name));
$list.append($('- ').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);
Final hello-world2.html
Anda adalah sebagai berikut:
<!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", "TFS/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($('- ').text("Query ID: " + query.id));
$list.append($('- ').text("Query Name: " + query.name));
$list.append($('- ').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: Pembaruan Manifes Ekstensi
Dalam langkah ini, kami memperbarui manifes ekstensi untuk menyertakan entri untuk widget kedua kami.
Tambahkan kontribusi baru ke array di contributions
properti dan tambahkan file hello-world2.html
baru ke array di properti file.
Anda memerlukan gambar pratinjau lain untuk widget kedua. Beri nama ini preview2.png
dan letakkan img
di folder .
{
...,
"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"
]
}
Langkah 6: Paket, Terbitkan, dan Bagikan
Paket, terbitkan, dan bagikan ekstensi Anda. Jika Anda telah menerbitkan ekstensi, Anda dapat mengemas ulang ekstensi dan langsung memperbaruinya ke Marketplace.
Langkah 7: Tambahkan Widget Dari Katalog
Sekarang, buka dasbor tim Anda di https:\//dev.azure.com/{yourOrganization}/{yourProject}
. Jika halaman ini sudah terbuka, maka refresh.
Arahkan mouse ke tombol Edit di kanan bawah, dan pilih tombol Tambahkan. Katalog widget terbuka di mana Anda menemukan widget yang Anda instal.
Pilih widget Anda dan pilih tombol 'Tambahkan' untuk menambahkannya ke dasbor Anda.
Bagian 3: Halo Dunia dengan Konfigurasi
Di Bagian 2 panduan ini, Anda melihat cara membuat widget yang menampilkan informasi kueri untuk kueri yang dikodekan secara permanen. Di bagian ini, kami menambahkan kemampuan untuk mengonfigurasi kueri yang akan digunakan alih-alih yang dikodekan secara permanen. Saat dalam mode konfigurasi, pengguna akan melihat pratinjau langsung widget berdasarkan perubahannya. Perubahan ini disimpan ke widget di dasbor saat pengguna memilih Simpan.
Langkah 1: HTML
Implementasi Widget dan Konfigurasi Widget sangat mirip. Keduanya diimplementasikan dalam kerangka kerja ekstensi sebagai kontribusi. Keduanya menggunakan file SDK yang sama, VSS.SDK.min.js
. Keduanya didasarkan pada HTML, JavaScript, dan CSS.
Salin file html-world2.html
dari contoh sebelumnya dan ganti nama salinan menjadi hello-world3.html
. Tambahkan file HTML lain yang disebut configuration.html
.
Folder Anda sekarang terlihat seperti contoh berikut:
|--- 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
Tambahkan HTML di bawah ini di 'configuration.html'. Kami pada dasarnya menambahkan referensi wajib ke 'VSS. File SDK.min.js' dan elemen 'pilih' untuk dropdown untuk memilih kueri dari daftar prasetel.
<!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: JavaScript - Konfigurasi
Gunakan JavaScript untuk merender konten dalam konfigurasi widget seperti yang kami lakukan untuk widget di Langkah 3 Bagian 1 dalam panduan ini.
Kode JavaScript ini merender konten, menginisialisasi VSS SDK, memetakan kode untuk konfigurasi widget Anda ke nama konfigurasi, dan meneruskan pengaturan konfigurasi ke kerangka kerja. Dalam kasus kami, di bawah ini adalah kode yang memuat konfigurasi widget.
Buka file configuration.html
dan elemen di bawah ini <script>
ke <head>
.
<script type="text/javascript">
VSS.init({
explicitNotifyLoaded: true,
usePlatformStyles: true
});
VSS.require("TFS/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
dan VSS.register
memainkan peran yang sama seperti yang mereka mainkan untuk widget seperti yang dijelaskan di Bagian 1.
Satu-satunya perbedaan adalah bahwa untuk konfigurasi widget, fungsi yang diteruskan ke VSS.register
harus mengembalikan objek yang memenuhi IWidgetConfiguration
kontrak.
Properti load
IWidgetConfiguration
kontrak harus memiliki fungsi sebagai nilainya.
Fungsi ini memiliki serangkaian langkah untuk merender konfigurasi widget.
Dalam kasus kami, ini untuk memperbarui nilai yang dipilih dari elemen dropdown dengan pengaturan yang ada jika ada.
Fungsi ini dipanggil ketika kerangka kerja membuat instans widget configuration
Properti onSave
IWidgetConfiguration
kontrak harus memiliki fungsi sebagai nilainya.
Fungsi ini dipanggil oleh kerangka kerja saat pengguna memilih Simpan di panel konfigurasi.
Jika input pengguna siap disimpan, maka serialisasikan ke string, bentuk custom settings
objek dan gunakan WidgetConfigurationSave.Valid()
untuk menyimpan input pengguna.
Dalam panduan ini, kami menggunakan JSON untuk membuat serial input pengguna ke dalam string. Anda dapat memilih cara lain untuk membuat serialisasi input pengguna ke string.
Ini dapat diakses oleh widget melalui properti WidgetSettings
kustom Pengaturan objek.
Widget harus mendeserialisasi ini, yang tercakup dalam Langkah 4.
Langkah 3: JavaScript - Aktifkan Pratinjau Langsung
Untuk mengaktifkan pembaruan pratinjau langsung saat pengguna memilih kueri dari dropdown, kami melampirkan penanganan aktivitas perubahan ke tombol . Handler ini memberi tahu kerangka kerja bahwa konfigurasi telah berubah.
Ini juga meneruskan yang customSettings
akan digunakan untuk memperbarui pratinjau. Untuk memberi tahu kerangka kerja, notify
metode pada perlu dipanggil widgetConfigurationContext
. Dibutuhkan dua parameter, nama peristiwa, yang dalam hal ini adalah WidgetHelpers.WidgetEvent.ConfigurationChange
, dan EventArgs
objek untuk peristiwa, dibuat dari customSettings
dengan bantuan metode pembantu WidgetEvent.Args
.
Tambahkan di bawah ini dalam fungsi yang ditetapkan ke load
properti .
$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);
});
Anda perlu memberi tahu kerangka kerja perubahan konfigurasi setidaknya sekali sehingga tombol "Simpan" dapat diaktifkan.
Pada akhirnya, Anda configuration.html
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("TFS/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>
Langkah 4: JavaScript - Terapkan muat ulang di widget
Kami telah menyiapkan konfigurasi widget untuk menyimpan jalur kueri yang dipilih oleh pengguna.
Kita sekarang harus memperbarui kode di widget untuk menggunakan konfigurasi tersimpan ini alih-alih dikodekan Shared Queries/Feedback
secara permanen dari contoh sebelumnya.
Buka file hello-world3.html
dan perbarui nama widget dari HelloWorldWidget2
ke HelloWorldWidget3
di baris tempat Anda memanggil VSS.register
.
Ini memungkinkan kerangka kerja untuk mengidentifikasi widget secara unik dalam ekstensi.
Fungsi yang dipetakan melalui HelloWorldWidget3
VSS.register
saat ini mengembalikan objek yang memenuhi IWidget
kontrak.
Karena widget kami sekarang membutuhkan konfigurasi, fungsi ini perlu diperbarui untuk mengembalikan objek yang memenuhi IConfigurableWidget
kontrak.
Untuk melakukan ini, perbarui pernyataan pengembalian untuk menyertakan properti yang disebut muat ulang seperti di bawah ini. Nilai untuk properti ini adalah fungsi yang memanggil getQueryInfo
metode sekali lagi.
Metode muat ulang ini dipanggil oleh kerangka kerja setiap kali input pengguna berubah untuk menampilkan pratinjau langsung. Ini juga dipanggil ketika konfigurasi disimpan.
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
return getQueryInfo(widgetSettings);
},
reload: function (widgetSettings) {
return getQueryInfo(widgetSettings);
}
}
Jalur kueri yang dikodekan secara permanen di 'getQueryInfo' harus diganti dengan jalur kueri yang dikonfigurasi, yang dapat diekstrak dari parameter 'widget Pengaturan' yang diteruskan ke metode . Tambahkan di bawah ini di awal metode 'getQueryInfo' dan ganti querypath yang dikodekan secara permanen dengan '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();
}
Pada titik ini, widget Anda siap untuk dirender dengan pengaturan yang dikonfigurasi.
load
Properti danreload
memiliki fungsi yang sama. Ini adalah kasus untuk widget yang paling sederhana. Untuk widget yang kompleks, akan ada operasi tertentu yang ingin Anda jalankan hanya sekali tidak peduli berapa kali konfigurasi berubah. Atau mungkin ada beberapa operasi berat yang tidak perlu berjalan lebih dari sekali. Operasi tersebut akan menjadi bagian dari fungsi yang sesuai denganload
properti dan bukanreload
properti .
Langkah 5: Pembaruan Manifes Ekstensi
vss-extension.json
Buka file untuk menyertakan dua entri baru ke array dalam contributions
properti . Satu untuk HelloWorldWidget3
widget dan yang lain untuk konfigurasinya.
Anda memerlukan gambar pratinjau lain untuk widget ketiga. Beri nama ini preview3.png
dan letakkan img
di folder .
Perbarui array dalam files
properti untuk menyertakan dua file HTML baru yang telah kami tambahkan dalam contoh ini.
{
...
"contributions": [
... ,
{
"id": "HelloWorldWidget3",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog",
"fabrikam.vsts-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
}
],
...
}
Perhatikan kontribusi untuk konfigurasi widget mengikuti model yang sedikit berbeda dari widget itu sendiri. Entri kontribusi untuk konfigurasi widget memiliki:
- ID untuk mengidentifikasi kontribusi Anda. Ini harus unik dalam ekstensi.
- Jenis kontribusi. Untuk semua konfigurasi widget, ini harus
ms.vss-dashboards-web.widget-configuration
- Array target tempat kontribusi berkontribusi. Untuk semua konfigurasi widget, ini memiliki satu entri:
ms.vss-dashboards-web.widget-configuration
. - Properti yang berisi sekumpulan properti yang menyertakan nama, deskripsi, dan URI file HTML yang digunakan untuk konfigurasi.
Untuk mendukung konfigurasi, kontribusi widget juga perlu diubah. Array target untuk widget perlu diperbarui untuk menyertakan ID untuk konfigurasi dalam formulir <publisher
>.>id for the extension
<.<id for the configuration contribution
> yang dalam hal ini adalah .fabrikam.vsts-extensions-myExtensions.HelloWorldWidget.Configuration
Peringatan
Jika entri kontribusi untuk widget yang dapat dikonfigurasi tidak menargetkan konfigurasi menggunakan penerbit dan nama ekstensi yang tepat seperti yang dijelaskan sebelumnya, tombol konfigurasi tidak muncul untuk widget.
Di akhir bagian ini, file manifes harus berisi tiga widget dan satu konfigurasi. Anda bisa mendapatkan manifes lengkap dari sampel di sini.
Langkah 6: Paket, Terbitkan, dan Bagikan
Jika Anda belum menerbitkan ekstensi, baca bagian ini untuk mengemas, menerbitkan, dan berbagi ekstensi Anda. Jika Anda telah menerbitkan ekstensi sebelum titik ini, Anda dapat mengemas ulang ekstensi dan langsung memperbaruinya ke Marketplace.
Langkah 7: Tambahkan Widget Dari Katalog
Sekarang, buka dasbor tim Anda di https://dev.azure.com/{yourOrganization}/{yourProject}. Jika halaman ini sudah terbuka, refresh. Arahkan mouse ke tombol Edit di kanan bawah, dan pilih tombol Tambahkan. Ini akan membuka katalog widget tempat Anda menemukan widget yang Anda instal. Pilih widget Anda dan pilih tombol 'Tambahkan' untuk menambahkannya ke dasbor Anda.
Anda akan melihat pesan yang meminta Anda untuk mengonfigurasi widget.
Ada dua cara untuk mengonfigurasi widget. Salah satunya adalah mengarahkan mouse ke widget, pilih elipsis yang muncul di sudut kanan atas lalu pilih Konfigurasikan. Yang lain adalah memilih tombol Edit di kanan bawah dasbor, lalu pilih tombol konfigurasi yang muncul di sudut kanan atas widget. Membuka pengalaman konfigurasi di sisi kanan, dan pratinjau widget Anda di tengah. Lanjutkan dan pilih kueri dari menu dropdown. Pratinjau langsung menunjukkan hasil yang diperbarui. Pilih "Simpan" dan widget Anda menampilkan hasil yang diperbarui.
Langkah 8: Mengonfigurasi Lainnya (opsional)
Anda dapat menambahkan elemen formulir HTML sebanyak yang configuration.html
Anda butuhkan dalam untuk konfigurasi tambahan.
Ada dua fitur yang dapat dikonfigurasi yang tersedia di luar kotak: nama widget dan ukuran widget.
Secara default, nama yang Anda berikan untuk widget Anda dalam manifes ekstensi disimpan sebagai nama widget untuk setiap instans widget Anda yang pernah ditambahkan ke dasbor.
Anda dapat mengizinkan pengguna untuk mengonfigurasi ini, sehingga mereka dapat menambahkan nama apa pun yang mereka inginkan ke instans widget Anda.
Untuk mengizinkan konfigurasi tersebut, tambahkan isNameConfigurable:true
di bagian properti untuk widget Anda dalam manifes ekstensi.
Jika Anda memberikan lebih dari satu entri untuk widget Anda dalam supportedSizes
array dalam manifes ekstensi, maka pengguna juga dapat mengonfigurasi ukuran widget.
Manifes ekstensi untuk sampel ketiga dalam panduan ini akan terlihat seperti di bawah ini jika kita mengaktifkan nama widget dan konfigurasi ukuran:
{
...
"contributions": [
... ,
{
"id": "HelloWorldWidget3",
"type": "ms.vss-dashboards-web.widget",
"targets": [
"ms.vss-dashboards-web.widget-catalog", "fabrikam.vsts-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"]
}
},
...
}
Dengan perubahan sebelumnya, kemas ulang dan perbarui ekstensi Anda. Refresh dasbor yang memiliki widget ini (Halo Dunia Widget 3 (dengan konfigurasi)). Buka mode konfigurasi untuk widget Anda, Anda sekarang dapat melihat opsi untuk mengubah nama dan ukuran widget.
Lanjutkan dan pilih ukuran yang berbeda dari menu drop-down. Anda melihat pratinjau langsung diubah ukurannya. Simpan perubahan dan widget di dasbor juga diubah ukurannya.
Peringatan
Jika Anda menghapus ukuran yang sudah didukung, maka widget gagal dimuat dengan benar. Kami sedang berupaya memperbaiki rilis mendatang.
Mengubah nama widget tidak mengakibatkan perubahan yang terlihat pada widget. Ini karena widget sampel kami tidak menampilkan nama widget di mana pun. Mari kita ubah kode sampel untuk menampilkan nama widget alih-alih teks "Halo Dunia" yang dikodekan secara permanen.
Untuk melakukan ini, ganti teks yang dikodekan secara permanen "Halo Dunia" dengan widgetSettings.name
di baris tempat kita mengatur teks h2
elemen.
Ini memastikan bahwa nama widget ditampilkan setiap kali widget dimuat pada refresh halaman.
Karena kita ingin pratinjau langsung diperbarui setiap kali konfigurasi berubah, kita harus menambahkan kode yang sama di reload
bagian kode kita juga.
Pernyataan pengembalian akhir dalam hello-world3.html
adalah sebagai berikut:
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);
}
}
Kemas ulang dan perbarui ekstensi Anda lagi. Refresh dasbor yang memiliki widget ini. Setiap perubahan pada nama widget, dalam mode konfigurasi, perbarui judul widget sekarang.
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk