Admin JavaScript Kütüphanesi
Panel tek bir istek yardımcısı, modal kütüphanesi, bildirim yığını ve tablo denetleyicisi taşır. Eklediğiniz her ekran bunları kullanır.
Genel Bakış
Her admin sayfası aynı dört betiği yükler ve içlerindeki her şey globaldir: düz fonksiyonlar, bir de tablolar için tek bir sınıf.
Sunucuyla sözleşme tek bir JSON zarfıdır; her operation'ın döndürdüğü zarf. Yardımcı onu kendisi okur, yani çağrı yerinde genellikle başarı ya da hata dalı gerekmez.
Ön Koşullar
Bunların tamamını admin altbilgisi üretir: inc/head.php ve inc/footer.php dahil edilmiş, bir operatör giriş yapmış olmalı. Altbilgi yoksa ne kütüphaneyi ne de sabitleri alırsınız.
Her çağrının karşı tarafında bir operation durur: operation alanıyla dağıtılan ve controller'ın hata yönetimiyle sarmalanan bir metot.
Bootstrap 5 yüklüdür ve doğrudan kullanılır (modal, açılır menü, toast, ipucu). jQuery eski kod için sayfadadır ama burada kullanılmaz.
Yapı
Her Parça Nerede Yaşar
$page_scripts ile $modals içine koyduğunu gösterir. Yükleme sırası bu dosyadır.
Yükleme Sırası ve Tuzağı
Altbilgi şu sırayı izler: sabitler bloğu, tablo denetleyicisi, gelişmiş seçim kütüphanesi, default.js, Bootstrap paketi, sizin sayfa betiğiniz, ardından alert.js ve modal.js.
Yani sayfa betiği, uyarı ve modal kütüphaneleri var olmadan önce çalışır. Fonksiyonları rahatça tanımlayın, işi işleyicide yapın.
Kendi Betiğinizi Eklemek
Yerleşimin okuduğu dört şablon değişkeni vardır; ikisini head, ikisini altbilgi okur. Sayfanın aşağısındaki bir sekme dosyası altbilgi çiftine yine de ekleme yapabilir.
['datepicker', 'apexcharts', 'pdf-export']. Stilleri head, betikleri altbilgi yükler. Listede olmayan sayfada da yoktur.
$clink = $links["controller"] ?? '';
$plugins = ['datepicker'];
// JavaScript'in textContent'e ya da bir girdinin değerine atayacağı metin, HTML
// varlığı olarak değil JSON olarak ulaşmalıdır. Makalenin sonundaki tuzağa bakın.
$L = Utility::jencode([
'records' => Language::gc("admin/widgets/records"),
'confirm' => Language::g("needs/confirm-action-ok"),
]);
$page_scripts = <<<HTML
<script>
const clink = '{$clink}';
const L = {$L};
</script>
HTML;
include $template_dir . "inc" . DS . "head.php";
Referans
WcpRequest
Her operation'ın geçtiği tek çağrı. Promise zincirini döndürür, yani bekleyebilirsiniz; ama işe yarar kancalar geri çağrılardır.
function WcpRequest(url, preferences) // bir Promise döndürür
WcpRequest(clink, {
method: 'POST',
data: { operation: 'update_widget', id: 5, name: 'Example' },
button: btn,
afterDone: (response) => WCPTable.get('widgetList')?.reload(),
});
Gövdeyi yardımcı kurar: iç içe bir nesne name[key], bir dizi name[0] olur, bir File değeri olduğu gibi eklenir.
Her istek ayrıca X-Requested-With: XMLHttpRequest başlığını taşır. Dağıtıcı onu zorunlu tutar, yoksa 403 döner. Düz bir ?operation= bağlantısı ya da elle yazılmış fetch operation çalıştıramaz.
Seçenek Nesnesi Anahtarları
'GET'. Başka her değer kurulan form verisini gövde olarak gönderir; GET aynı kümeyi adresteki sorgu dizesine çevirir.
FormData değil: aşağıdaki tuzağa bakın. İç içe nesneler, diziler ve File değerleri karşılanır.
options.headers değeriniz korunur; X-Requested-With yalnız siz koymadıysanız eklenir. Dolu bir options.body, data'yı tümüyle yok saydırır.
data-loader özniteliğinden, sonra buttonLoader'dan, sonra çıplak dönen daireden alınır. Doğrudan bir ikon çocuğu ve etiketi olan bir buton, yükleyici metin taşımıyorsa etiketini korur: yalnız ikon değişir. Aksi halde tüm içerik değişir ve özgün işaretleme afterDone ile finally arasında geri konur. Satır menüsündeki bir buton ayrıca o menüyü açık tutar; menü yanıt geldikten iki saniye sonra kapanır.
saving_loader ve kardeşleri).
'json' (varsayılan), 'text' ya da 'blob'. Sunucudan gelen JSON içerik tipi bunu yener, yani bir operation'ın yanıtı her zaman ayrıştırılır.
false yönlendirilmiş yanıtı olağan bir yanıt gibi okur.
'_self' (varsayılan) ya da '_blank'. '_blank' ile yanıttaki redirect gezinmek yerine yeni sekme açar ve ardından yalnız done çalışır.
alert_toast ikisini birden açar. Sunucu kendi successToast değerini döndürerek başarı tarafını ezebilir.
Response ile çalışır. Başlık ve durum kodu için, veri için değil.
Error nesnesini alır ve otomatik hata uyarısının yerine geçer. Hem ağ hatası hem de durumu successful olmayan bir yanıt buraya düşer.
fail'iniz.
Yanıt Neler Taşıyabilir
Otomatik işlemenin okuduğu anahtarlar bunlardır. Başka her şey olduğu gibi afterDone'a geçer; bir çağrı yerinin veri geri alma yolu budur.
| Anahtar | Değer | Yardımcı onunla ne yapar |
|---|---|---|
status | "successful" ya da başka bir şey | Anahtar konum. "successful" dışındaki her değer message'ı fırlatılan bir hataya çevirir; böylece reddedilen operation ile ölü ağ aynı hata yolunu izler. |
message | metin | Başarıda perdeli bildirim (ya da successToast ile toast) olarak gösterilir. Başarısızlıkta hata olarak. İşaretleme gibi işlenir, yani bir insanın yazdığı her şeyi kaçırın. |
redirect | adres, "reload" ya da "script" | Gezinir, sayfayı yeniler ya da script'i çalıştırır. "script" yalnız yanında bir message varken geçerlidir; tek başına bir adres sayılır ve sayfa "script" adlı bir sayfaya gider. |
redirect_delay | milisaniye | Yönlendirme başlamadan önce bildirimin ekranda kalma süresi. Mesajla birlikte varsayılan 5000, mesajsız 1. |
script | JavaScript kaynağı | Mesaj da yönlendirme de yokken çalıştırılır. Son çare; veri döndürüp afterDone içinde davranmayı tercih edin. |
successToast | boolean | Operation'ın, çağrı yerini ezerek perdeli bildirim yerine küçük toast'ı seçmesini sağlar. |
Modal'lar
Diyalog işaretlemesini asla elle yazmayın. Dört açıcı her durumu karşılar, hepsi aynı kabuğu kurar. Başlıktaki kapatma butonu sayesinde alt bölüm kendi Kapat ya da İptal butonunu taşımaz.
// Genel diyalog. İlk argüman bir DOM id'si ya da eleman; bilinmeyen id yenisini yaratır.
function open_modal(modal_id, options)
function close_modal(modal_id) // id ya da eleman
function destroy_modal(modal_id) // örneği yok eder ve elemanı kaldırır
// Onay. Kurulur, gösterilir ve gizlendiğinde belgeden kaldırılır.
function confirmModal(options = {}) function confirmModalClose()
function confirmDeleteModal(options = {}) function confirmDeleteModalClose()
function actionModal(options = {}) function actionModalClose()
// ^ tutamak döndüren tek fonksiyon: { modal, modalElement, confirmBtn }
// Diğer ikisi hiçbir şey döndürmez; kapatıcılarını kullanın.
// Bir ton için başlık sınıfı. İçinde "danger" geçen her değer kırmızı başlık,
// geri kalanı birincil başlık verir. Bir başlığı çalışma anında yeniden yazarsanız kullanın.
function wcpModalHeaderClass(tone)
Daha Basit Üç Diyalog
open_modal: başlık, içerik işaretlemesi ve alt bölüm işaretlemesi. Boş bir alt bölüm hiç görünmez; yalnızca bilgi veren bir diyaloğun istediği de budur.
'p-4'), 'modal-lg' gibi bir genişlik sınıfı, ek alt bölüm sınıfları ve '600px' gibi açık bir azami genişlik.
bgClass artık yalnız başlık tonunu seçer, hiçbir şey boyamaz; textClass hâlâ kabul edilir ve hiç gösterilmez. backdrop 'true', 'false' ya da 'static' alır; kapanınca iş kaybolmayacaksa dokunmayın. onClose diyalog gizlendikten sonra bir kez çalışır.
open_modal, seçeneklerinizi birleştirdikten sonra elemandan data-bs-modal-title (ya da data-izimodal-title), data-bs-modal-bg-class, data-bs-modal-text-class ve data-bs-modal-width okur. İşaretlemede unutulmuş bir öznitelik, geçirdiğiniz değeri sessizce yener.
open_modal ise id'sini ilk argüman olarak alır.
confirmModal. Fonksiyon tıklamaya bağlanır. Dize ise tıklamada değil, diyalog kurulurken hemen eval'e verilir. confirmButtonOnClick bunun yerine butonun satır içi onclick'ine yazılır. İkisini de vermezseniz buton yalnızca diyaloğu kapatır; kapatmanın kendiliğinden olduğu tek durum budur.
confirmModal. Üç metin çevrilmiş confirmActionTitle, confirmActionMsg ve confirmActionOk sabitlerine düşer, yani sade bir onay için hiç metin yazmanız gerekmez. İki sınıf ise 'bg-danger' ve 'btn-danger' varsayılanını alır. Sonuncusu onay butonuna eklenen ham bir öznitelik dizesidir.
confirmDeleteModal: silinen şeyin adı, altında sonucu anlatan bir satır ve ortalanmış bloğun altına eklenen ek işaretleme.
confirmDeleteModal: büyük ikon ve tonu, onay butonunun etiketi (varsayılan confirmActionOk), sınıfı ve ikonu, bir de diyalog genişliği (varsayılan '420px'). ? buttonClass aynı zamanda başlık tonudur: kabuğun kırmızı başlıkla birincil başlık arasında seçim yaparken okuduğu değer odur.
confirmDeleteModal: geri çağrı onay butonunu alır, yani doğrudan WcpRequest'e verilebilir. buttonLoader o butona data-loader olarak yazılır. onConfirm vermek, diyaloğu sizin kapatacağınız anlamına gelir.
actionModal
Birden çok kayda dokunan ya da geri alınamayan her iş için diyalog budur, tekil silme dahil.
action ('delete', 'active', 'suspended', 'cancelled') diyalogda saklanır ama hiç okunmaz. O, çağrı yerinin kendi anahtarıdır; ton yine headerClass ile gelmek zorundadır. headerTextClass ve cancelText de kabul edilir ve hiç gösterilmez.
showIcon: false geçip iki satırı da boş bıraktığınızda tümüyle düşer.
serviceInfoRows bir { label, value, valueClass } listesidir. Verildiğinde yerleşik çiftin yerine geçer. Boş bırakırsanız panel o iki satırı dört etiket ve ad seçeneğinden kurar.
selectedCountText'tir; selectedCount kabul edilir ama hiç gösterilmez, yani yalnız sayıyı geçmek paneli boş bırakır.
apiSwitchChecked aksini söylemedikçe işaretsiz başlar. Arkasında modül olmayan kayıtlarda kapatın, yoksa operatöre hiçbir şey yapmayan bir seçenek sunulur.
confirmActionRequirePassword sabitini geçin; içindeki alan diyaloğun okuduğu id'yi taşır: #confirmPassword. O alan boş kaldığı sürece onay butonu kapalı durur. Onu taşımayan bir işaretleme ise sessizce ne bu korumayı alır ne de geri çağrıya bir değer taşır.
actionModal Geri Çağrı Bağlamı
onConfirm iki ayrı argüman olarak buton ve modal değil, tek bir nesne alır.
onConfirm: (ctx) => {
ctx.button; // onay butonu elemanı (WcpRequest'e `button` olarak geçin)
ctx.modal; // Bootstrap Modal örneği
ctx.modalElement; // diyaloğun kök elemanı
ctx.applyApi; // boolean: servis sağlayıcı anahtarının durumu, gizliyse false
ctx.password; // string: parola alanına yazılan, gizliyse ''
ctx.data; // `data` olarak geçirilen nesnenin ta kendisi
ctx.setLoading(true); // kapat + dönen daire + processingText
ctx.setLoading(true, 'Deleting'); // ...kendi etiketinizle
ctx.setLoading(false); // ikonu + confirmText'i geri koy
ctx.close(); // diyaloğu gizle; kimse sizin yerinize kapatmaz
}
ctx.setLoading ile button: ctx.button'dan birini kullanın, ikisini birden değil: ikisi de butonun içeriğinin sahibidir ve sonra yazan kazanır.
Tablolar
Her listeyi, adıyla erişebileceğiniz bir denetleyici sürer. Bileşenin, preset'lerinin ve filtrelerinin kendi makalesi vardır.
const table = WCPTable.get('widgetList'); // liste sayfada değilse null
const same = document.querySelector('.wcp-table[data-name="widgetList"]').wcp;
table.reload(url) // yeniden çek; isteğe bağlı adres veri adresinin yerine geçer
table.activate() // ertelenmiş listenin ilk yüklemesi; normal listede hiçbir şey yapmaz
table.resetFilters() // bağlı her kontrolü temizle, filtreleri adresten düşür, listeyi yenile
table.hasActiveCustomFilters() // boolean
table.addRow(rowHtml, prepend = false) // işaretleme ya da bir <tr> elemanı
table.exportUrl('csv') // o anki filtre, arama ve sıralamayı taşıyan indirme adresi
// removeRow üç biçimlidir. Üçü de sayaçları günceller ve tur atmadan anında
// yeniden boyar. Sunucuda sayfalanan bir liste ardından bir kez tamamlama çeker.
table.removeRow('id', '5') // sütun anahtarı + hücrenin tam metni
table.removeRow((tr) => tr.dataset.uid === u) // satır elemanı üzerinde bir yüklem
table.removeRow('example.com') // metni eşleşen herhangi bir hücre
// İşe yarar durum
table.name · table.ajax · table.isPartial · table.currentPage
table.perPage · table.searchQuery · table.totalEntries · table.totalFilter
İki olay üretilir, her biri iki hedefte: örneğin kendisi ve belgedeki ikizi.
| Olay | Nerede dinlenir | detail ne taşır |
|---|---|---|
ajax-load-after | örneğin kendisi | table, name, isPartial, totalEntries, filteredData |
wcp-table-ajax-load-after | document | aynı nesne |
table-update-after | örneğin kendisi | table, name, currentPage, filteredData, animated |
wcp-table-update-after | document | aynı nesne |
İkiz sayesinde dinleyici liste var olmadan bağlanabilir. Sarmalayıcı elemana bağlanan dinleyici sessizce hiç tetiklenmez; WCPTable.get(name)'e ya da belgeye bağlanıp detail.name ile süzün.
Uyarılar ve Toast'lar
// Perdeli bildirim: aynı anda bir tane, sonraki çağrılar arkasında sıraya girer.
// İkisi de aynı seçenek nesnesini ve aynı varsayılanları alır.
alert_success(message, options = {}) // options: { timer: 3000, onClose: null }
alert_error(message, options = {}) // hata için daha uzun bir varsayılan YOKTUR
alert_error(message, { timer: 0 }) // 0 = zamanlayıcı yok, kapatılana kadar durur
// Küçük köşe toast'ı, hazır hâliyle.
alert_success_toast(message) // 4 saniye
alert_error_toast(message) // 6 saniye
// Toast'ın tamamı.
createStackedToast({
message, // işaretlemedir, bir insanın yazdığı her şeyi kaçırın
type: 'info', // success | error | info | warning; başka değer info'ya düşer
icon: 'bi bi-info-circle', // yalnız başlıklı biçimde kullanılır
autoHide: 10000, // milisaniye; 0 kapatılana kadar tutar
animation: true,
title: '', // boş = tek satırlık kompakt biçim
subtitle: '', // buraya konan zaman damgası göreli zaman ipucu da alır
})
Kompakt biçim üzerine tıklanınca kapanır; başlıklı biçim yalnız kendi butonundan kapanır. Yığın sağ alt köşededir ve dört toast tutar; beşincisi gelince en eskisi kapanır. Konum seçeneği yoktur.
Çevrilmiş bir karşılığınız yoksa title ya da subtitle geçmeyin. Varsayılanlar boştur, böylece çevrilmemiş bir başlık görünmez.
Global Sabitler
Altbilgi bunları herhangi bir betik çalışmadan önce yazar, yani her yerde kullanılabilir. Bunlar pencere özelliği değil const bağlarıdır; yükleyiciler adla çözülsün diye window üzerine de yansıtılır.
passwordHtml olarak geçin.
enabling_, disabling_, sending_, checking_, approving_, activating_, cancelling_, suspending_, downloading_, applying_, redirecting_ ve formSubmit_loader.
{label} yerini kendinizin doldurduğu bir kalıp: label_loader.replace('{label}', importingText).
Yardımcı Fonksiyonlar
innerHTML'e ya da bir özniteliğe ulaşmadan önce escHtml'den geçirin.
symbol varsayılan true). Yazılmış bir tutarı ayraçları ne olursa olsun sayıya geri okuyun ve yüklü kurlarla kodlar arasında çevirin.
null, varsa ve boşsa '') ve bir değer konmuş ya da kaldırılmış yeni bir adres alın. Hiçbiri gezinmez.
data-required butona konur ve girdilerin id'lerini virgülle ayrılmış olarak taşır. Onu girdiye koymak butonu sonsuza dek kapalı bırakır.
Örnek
Bir satır silme üç parça ister: soran JavaScript, cevaplayan operation ve yenilenmeden kaybolan satır.
function deleteWidget(id, name) {
confirmDeleteModal({
message: escHtml(name),
description: L.deleteWarning,
buttonLoader: deleting_loader,
// Geri çağrıya onay butonu verilir; isteğin istediği de tam budur: onu kapatır,
// üzerinde yükleyiciyi gösterir ve sonunda yeniden açar.
onConfirm: (btn) => WcpRequest(clink, {
method: 'POST',
data: { operation: 'delete_widget', id: id },
button: btn,
// Başarı bildirimi küçük: bir silme koca bir perdeyi hak etmez.
successToast: true,
// afterDone otomatik işlemeden SONRA koşar, yani mesaj gösterilmiş ve hata
// zaten bildirilmiştir. Buradaki hiçbir şey başarısızlıkta koşmaz.
afterDone: () => {
confirmDeleteModalClose();
WCPTable.get('widgetList')?.removeRow('id', String(id));
},
}),
});
}
public function delete_widget(Operation $operation): bool
{
$operation->demo();
$id = (int) Filter::init("POST/id", "rnumbers");
if (!$id) throw new Exception(Language::gc("admin/widgets/error-id-required"));
// Fırlatılan istisna kendiliğinden {"status":"error","message":"..."} olur; yardımcının
// hata bildirimine çevirdiği şekil de budur. Ayrı bir hata dalı gerekmez.
if (!$this->model->delete($id))
throw new Exception(Language::gc("admin/widgets/error-delete-failed"));
// Bilerek yönlendirme yok: satırı sayfa kendisi kaldırıyor, yenilemek bir tura mal olur
// ve operatörün listedeki yerini kaybettirir.
return $operation->output([
'status' => "successful",
'message' => Language::gc("admin/widgets/deleted"),
]);
}
Aynı liste, toplu hâli. Butonun sahibi diyalog olduğu için isteğe buton verilmez.
function bulkWidgets(action, ids) {
actionModal({
action: action,
title: L.bulkTitle,
headerClass: action === 'delete' ? 'bg-danger' : 'bg-success',
// Basılan şey selectedCountText'tir. Tek başına selectedCount hiçbir şey basmaz.
selectedCount: ids.length,
selectedCountText: ids.length + ' ' + L.records,
info1Title: L.permanentTitle, info1Desc: L.permanentDesc,
info2Title: L.providerTitle, info2Desc: L.providerDesc,
// Servis sağlayıcı anahtarını yalnız en az bir satırın arkasında modül varken sun.
showApiSwitch: ids.some(hasModule),
// Birden çok kayıt söz konusu olduğunda parolayı bir kez daha iste.
showPassword: ids.length > 1,
passwordHtml: confirmActionRequirePassword,
confirmText: L.confirm,
data: { ids: ids },
onConfirm: (ctx) => {
ctx.setLoading(true);
WcpRequest(clink, {
method: 'POST',
data: {
operation: 'bulk_actions',
action: action,
id: ctx.data.ids, // dizi id[0], id[1], ... olarak ulaşır
apply_on_module: ctx.applyApi ? 1 : 0,
password: ctx.password,
},
afterDone: () => {
ctx.setLoading(false);
ctx.close(); // kimse sizin yerinize kapatmaz
WCPTable.get('widgetList')?.reload();
},
afterFail: () => ctx.setLoading(false),
});
},
});
}
Tuzaklar
Yardımcı nesnenin kendi özelliklerini gezer; bir FormData örneğinin böyle bir özelliği yoktur. İstek hiçbir şey taşımadan gider, operation adını bile. Sunucu olağan işaretlemeyle cevap verir, konsol temizdir. Düz nesne geçin ya da hazır gövdeyi options.body içine koyun.
done başarı bildirimini, yönlendirmeyi ve hata uyarısını kapatır; üçünü de çağrı yerinde yeniden yazarsınız. Ek iş afterDone'a aittir.
Diyaloglar otomatik kapatmayı yalnız geri çağrı verilmemişken bağlar. Unutursanız kayıt silinir ama diyalog hata vermeden ekranda asılı kalır. Kapatmayı afterDone içinde çağırın, tıklama anında değil.
Özgün işaretleme ikisinin arasında geri konur, yani afterDone içinde yazılan etiket sessizce silinir. Renk değişir, yazı değişmez. Öznitelikler etkilenmez, yalnız içerik geri yüklenir.
Bir çeviriyi HTML varlıklarına kodlamak innerHTML için doğru, geri kalan her yer için yanlıştır. textContent ve bir girdinin value'su çözmez, operatör ekranda Düzenle okur. Böyle dizeleri JavaScript'e JSON olarak verin.
onclick="fn(' + JSON.stringify(v) + ')", JSON'un çift tırnaklarını çift tırnaklı bir özniteliğin içine sokar. Tarayıcı özniteliği orada bitirir ve işleyici sessizce hiç bağlanmaz. Ya kaçırın (escHtml(JSON.stringify(v))) ya da özniteliği elemanın üzerine yazın.
Altbilgi o ikisini sizinkinden sonra yazar. Fonksiyon tanımlamak sorun değildir; open_modal ya da alert_success'i ayrıştırma anında çağırmak hata fırlatır. İşi bir işleyicinin içine koyun.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.