Ini adalah bagian kedua, terakhir, dari tutorial di mana kami menulis klien TodoMVC menggunakan kerangka kerja reaktif
ds minimalis.
Ringkasan bagian
pertama : kami menerima daftar tugas dari server dalam format JSON, membuat daftar HTML darinya, menambahkan kemampuan untuk mengedit nama dan tanda penyelesaian untuk setiap kasus, dan menerapkan pemberitahuan server tentang pengeditan ini.
Itu masih harus direalisasikan: menghapus kasus sewenang-wenang, menambahkan kasus baru, pemasangan / reset massal dan penyaringan kasus berdasarkan penyelesaian dan fungsi menghapus semua kasus selesai. Ini yang akan kita lakukan. Versi final klien, yang akan kita bahas dalam artikel ini, dapat dilihat di
sini .
Opsi yang kami tentukan terakhir kali dapat disegarkan di
sini .
Ini kodenya:
'#todoapp'.d("" ,'#header'.d("" ,'H1'.d("") ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("") ) ,'#main'.d("" ,'#toggle-all type=checkbox'.d("") ,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed $editing= $patch=; a!" ,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=($completed=#.checked)") ,'LABEL.view' .d("? $editing:!; ! .title") .e("dblclick","$editing=`yes") ,'INPUT.edit' .d("? $editing; !! .title@value") .ui("$patch=(.title=#.value)") .e("blur","$editing=") ,'BUTTON.destroy'.d("") ) .a("!? $completed $editing") .u("? $patch; (@method`PATCH .url:dehttp headers $patch@):query $patch=") ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} }) .FUNC({ convert:{ dehttp: url=>url.replace(/^https?\:/,'') } }) .RENDER()
Sekarang hanya ada lima puluh baris di sini, tetapi pada akhir artikel akan ada dua kali lebih banyak - sebanyak 100. Akan ada banyak permintaan HTTP ke server, jadi silakan buka alat pengembang (di Chrome, seperti yang Anda ingat, Ctrl + Shift + I) - akan ada Pertama-tama, tab Jaringan menarik, dan kedua, Konsol. Juga, jangan lupa untuk melihat kode untuk setiap versi halaman kami - di Chrome itu adalah Ctrl + U.
Di sini saya harus melakukan sedikit penyimpangan. Jika Anda tidak membaca bagian
pertama tutorial, saya tetap merekomendasikan untuk memulainya. Jika Anda membacanya, tetapi tidak mengerti apa-apa, lebih baik membacanya lagi. Seperti yang ditunjukkan oleh komentar pada dua artikel saya sebelumnya, sintaks dan prinsip dap tidak selalu langsung dipahami oleh pembaca yang tidak siap. Artikel lain tidak disarankan untuk dibaca oleh orang yang merasa tidak nyaman dengan tampilan sintaksis tidak seperti si.

Bagian kedua tutorial ini akan sedikit lebih rumit dan menarik daripada yang pertama.
[TODO: minta token untuk menemukan gambar otak anak sekolah yang meledak di internet] .
Dengan izin Anda, saya akan terus memberi nomor bab dengan bagian 1. Di sana kami menghitung sampai 7. Jadi,
8. Membuat daftar to-do dari variabel status
Untuk menghapus
BUTTON.destroy
dari daftar ada tombol
BUTTON.destroy
. Penghapusan terdiri dari pengiriman permintaan DELETE ke server dan sebenarnya menghapus elemen
UL#todo-list > LI
dengan semua konten. Dengan mengirimkan permintaan DELETE, semuanya jelas:
,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query")
Tetapi dengan penghapusan elemen dari layar, opsi dimungkinkan. Seseorang bisa dengan mudah memperkenalkan variabel keadaan lain, katakanlah
$deleted
, dan sembunyikan elemen dengan CSS, termasuk kelas CSS yang
deleted
,'LI'.d("$completed=.completed $editing= $patch= $deleted=; a!"
Dan itu akan berhasil. Tapi itu akan curang. Selain itu, lebih jauh ke bawah kita akan memiliki filter dan penghitung kasus aktif dan selesai (apa yang ada di
#footer
). Oleh karena itu, lebih baik untuk segera menghapus objek dari daftar yang harus dilakukan dengan jujur, “secara fisik”. Artinya, kita perlu kemampuan untuk memodifikasi array itu sendiri, yang awalnya kita terima dari server - yang berarti bahwa array ini juga harus menjadi variabel keadaan. Sebut saja dia
$todos
.
Cakupan variabel
$todos
adalah untuk memilih leluhur umum dari semua elemen yang akan mengakses variabel ini. Dan itu akan diakses oleh
INPUT#new-todo
dari
#header
, dan counter dari
#footer
, dan sebenarnya
UL#todo-list
. Nenek moyang yang sama dari mereka semua adalah elemen root dari template,
#todoapp
. Oleh karena itu, dalam aturan-d kita akan mendefinisikan variabel
$todos
. Di tempat yang sama, kami segera mengunggah data dari server ke sana. Dan untuk membangun daftar
UL#todo-list
, kita sekarang juga akan berasal dari itu:
'#todoapp'.d("$todos=todos:query"
Itu penting. Jika selama pengujian tiba-tiba daftar agenda tidak dimuat - sangat mungkin seseorang menghapus semuanya (ini adalah server publik, dan apa pun bisa terjadi di sana).
Dalam hal ini, harap buka
contoh fitur lengkap dan buat beberapa kasus untuk percobaan.
Kami melihat . Di sini
$todos
dideklarasikan dalam aturan-d elemen
#todoapp
dan segera
diinisialisasi dengan data yang diperlukan. Segalanya tampak berfungsi, tetapi satu fitur yang tidak menyenangkan telah muncul. Jika server merespons permintaan untuk waktu yang lama (Chrome memungkinkan Anda untuk mensimulasikan situasi ini: pada tab Jaringan pada alat pengembang, Anda dapat memilih berbagai mode untuk mensimulasikan jaringan lambat), maka versi aplikasi kami yang baru hingga permintaan selesai terlihat agak sedih - tidak ada yang lain selain beberapa CSS artefak. Gambar seperti itu pasti tidak akan menambah antusiasme bagi pengguna. Meskipun versi sebelumnya tidak menderita karena hal ini - sampai data diterima pada halaman, hanya daftar itu sendiri yang hilang, tetapi elemen lain segera muncul, tanpa menunggu data.
Ini masalahnya. Seperti yang Anda ingat, konverter
:query
tidak sinkron. Sinkronisasi ini dinyatakan dalam fakta bahwa sampai permintaan selesai, hanya eksekusi aturan saat ini yang diblokir, yaitu generasi elemen, yang, pada kenyataannya, membutuhkan data yang diminta (yang logis). Generasi elemen lain tidak diblokir. Oleh karena itu, ketika
UL#todo-list
mengakses server, hanya saja server itu diblokir, tetapi bukan
#header
dan bukan
#footer
, yang langsung ditarik. Sekarang seluruh
#todoapp
sedang menunggu permintaan untuk diselesaikan.
9. Pemuatan data tertunda
Untuk memperbaiki situasi dan menghindari pemblokiran elemen-elemen yang tidak terlibat, kami akan menunda pemuatan awal data sampai semuanya sudah diambil. Untuk melakukan ini, kami tidak akan segera memuat data ke variabel
$todos
, tetapi pertama-tama kami hanya menginisialisasi dengan "tidak ada"
'#todoapp'.d("$todos="
Jadi dia tidak akan memblokir apa pun dan seluruh templat akan berfungsi - meskipun untuk saat ini dengan "daftar tugas" yang kosong. Tetapi sekarang, dengan layar awal yang membosankan, Anda dapat dengan aman
memodifikasi $todos
dengan mengunggah daftar tugas untuk itu. Untuk melakukan ini, tambahkan keturunan ini ke
#todoapp
,'loader' .u("$todos=todos:query")
Elemen ini memiliki aturan-u yang terlihat persis sama dengan aturan pemblokiran yang kami tolak, tetapi ada satu perbedaan mendasar.
Biarkan saya mengingatkan Anda bahwa aturan-d (dari
bawah ) adalah aturan pembuatan elemen yang dieksekusi ketika templat dibuat dari atas ke
bawah , dari induk ke turunan; dan aturan-u (dari
atas ) adalah aturan reaksi yang dijalankan sebagai respons terhadap peristiwa yang muncul dari bawah ke
atas , dari anak ke orang tua.
Jadi, jika sesuatu (termasuk "tidak ada") ditugaskan
ke variabel
dalam aturan-d , ini berarti
deklarasi dan inisialisasi dalam lingkup elemen ini dan turunannya (lingkup bersarang diimplementasikan dalam dap, seperti pada JS ) Penugasan
dalam aturan-up berarti
modifikasi variabel yang dideklarasikan sebelumnya dalam cakupan. Deklarasi dan inisialisasi variabel dalam aturan-d memungkinkan orang tua untuk meneruskan kepada keturunan menuruni hierarki informasi yang diperlukan untuk konstruksi, dan modifikasi memungkinkan orang tua untuk memberikan pembaruan pada informasi ini dan dengan demikian memulai restrukturisasi yang tepat dari semua elemen yang bergantung padanya.
Elemen
loader
, yang merupakan turunan
#todoapp
, dalam aturan-u-nya
memodifikasi variabel
$todos
, memuat data dari server ke dalamnya, yang menyebabkan regenerasi otomatis semua elemen konsumen dari variabel ini (dan hanya mereka, yang penting!). Konsumen suatu variabel adalah elemen yang aturan-dnya mengandung variabel ini sebagai nilai, yaitu Mereka yang
membaca variabel ini (dengan mempertimbangkan ruang lingkup) saat membangun.
Kami sekarang memiliki satu konsumen variabel
$todos
-
UL#todo-list
sangat, yang kemudian akan dibangun kembali setelah memuat data.
,'UL#todo-list'.d("*@ $todos"
Jadi,
sekarang kita memiliki daftar yang harus dilakukan adalah variabel status di
#todoapp
, sambil tidak memblokir rendering awal templat.
10. Menghapus dan menambahkan to-do
Sekarang kita dapat memodifikasi
$todos
segala cara. Mari kita mulai dengan menghapus item. Kami sudah memiliki tombol-lintas
BUTTON.destroy
, yang sejauh ini hanya mengirim permintaan penghapusan server
,'BUTTON.destroy'.ui("(@method`DELETE .url:dehttp):query")
Penting untuk memastikan bahwa objek yang sesuai juga dihapus dari variabel
$todos
- dan karena ini akan menjadi modifikasi,
UL#todo-list
, sebagai konsumen variabel ini, akan secara otomatis membangun kembali, tetapi tanpa elemen yang dihapus.
Dengan sendirinya, dap tidak menyediakan sarana khusus untuk memanipulasi data. Manipulasi dapat ditulis dengan sempurna dalam fungsi di JS, dan aturan dap hanya mengirimkan data kepada mereka dan mengambil hasilnya. Kami menulis fungsi JS untuk menghapus objek dari array tanpa mengetahui jumlahnya. Sebagai contoh, ini:
const remove = (arr,tgt)=> arr.filter( obj => obj!=tgt );
Anda mungkin dapat menulis sesuatu yang lebih efektif, tetapi ini bukan tentang itu sekarang. Kecil kemungkinan bahwa aplikasi kita harus bekerja dengan daftar yang harus dilakukan dari jutaan item. Yang penting adalah bahwa fungsi mengembalikan objek array baru, dan bukan hanya menghilangkan elemen dari apa adanya.
Untuk membuat fungsi ini dapat diakses dari aturan dap, Anda perlu menambahkannya ke bagian
.FUNC
, tetapi sebelum itu memutuskan bagaimana kami ingin menyebutnya. Opsi paling sederhana dalam kasus ini adalah, mungkin, untuk memanggilnya dari konverter, yang menerima objek
{ todos, tgt }
dan mengembalikan array yang difilter
.FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''),
tetapi tidak ada yang menghentikan Anda dari mendefinisikan fungsi ini tepat di dalam
.FUNC
(Saya sudah mengatakan bahwa
.FUNC
sebenarnya adalah metode JS biasa, dan argumennya adalah objek JS biasa?)
.FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ) } })
Sekarang kita dapat mengakses konverter ini dari aturan dap
,'BUTTON.destroy' .ui("$todos=($todos $@tgt):remove (@method`DELETE .url:dehttp):query")
Di sini, pertama-tama kita membentuk objek yang dalam notasi JS cocok dengan
{ todos, tgt:$ }
, meneruskannya ke
:remove
konverter yang dijelaskan dalam
.FUNC
, dan kembalikan hasil yang difilter ke
$todos
, dengan demikian mengubahnya. Di sini
$
adalah
konteks data elemen, objek bisnis dari array
$todos
tempat templat dibuat. Setelah simbol
@
, alias argumen ditunjukkan. Jika
@
ada, maka nama argumen sendiri digunakan. Ini mirip dengan inovasi ES6 baru -
singkatan properti .
Demikian pula, kami menambahkan kasus baru ke daftar menggunakan elemen
INPUT#new-todo
dan permintaan POST
,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$=(#.value@title) (@method`POST todos@url headers $):query $todos=($todos $@tgt):insert #.value=") ... .FUNC({ convert:{ dehttp: url => url.replace(/^https?\:/,''), remove: x => x.todos.filter( todo => todo!=x.tgt ),
Aturan reaksi elemen
INPUT#new-todo
ke acara UI standar (untuk elemen
INPUT
, peristiwa
change
dianggap sebagai standar dap) termasuk: membaca input pengguna dari properti
value
elemen ini, membentuk konteks
$
lokal dengan nilai ini sebagai bidang
.title
, mengirimkan
$
context ke server menggunakan metode POST, memodifikasi array
$todos
dengan menambahkan konteks
$
sebagai elemen baru dan akhirnya membersihkan properti
value
elemen
INPUT
.
Di sini, pembaca muda mungkin bertanya: mengapa menggunakan
concat()
saat menambahkan elemen ke array, jika ini dapat dilakukan dengan menggunakan
push()
biasa
push()
? Pembaca yang berpengalaman akan segera memahami apa masalahnya, dan menulis jawabannya di komentar.
Kami melihat apa yang
terjadi. Kasing ditambahkan dan dihapus secara normal, permintaan yang sesuai dikirim ke server dengan benar (Anda membiarkan tab Jaringan terbuka selama ini, kan?). Tetapi bagaimana jika kita ingin mengubah nama atau status dari kasus yang baru ditambahkan? Masalahnya adalah untuk memberi tahu server tentang perubahan ini, kita perlu
.url
, yang menetapkan server untuk bisnis ini. Ketika kami menciptakan bisnis,
.url
tidak tahu masing-masing
.url
, kami tidak dapat membentuk permintaan PATCH yang benar untuk perubahan.
Faktanya, semua informasi yang diperlukan tentang kasing terdapat dalam respons server terhadap permintaan POST, dan akan lebih tepat untuk membuat objek bisnis baru tidak hanya dari input pengguna, tetapi dari respons server, dan menambahkan objek ini ke
$todos
- dengan semua informasi yang disediakan informasi server, termasuk bidang
.url
,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (@method`POST todos@url headers (#.value@title)):query@tgt ):insert #.value=")
Kami melihat - oke, sekarang semuanya berjalan dengan benar. Pemberitahuan ke server tentang pengeditan kasus yang baru dibuat berjalan dengan benar.
Orang bisa berhenti pada ini, tapi ... Tapi jika Anda melihat dekat, Anda masih bisa melihat sedikit keterlambatan antara memasukkan nama kasing baru dan saat itu muncul dalam daftar. Penundaan ini jelas terlihat jika Anda mengaktifkan jaringan lambat yang disimulasikan. Seperti yang sudah Anda duga, masalahnya adalah permintaan ke server: pertama, kami meminta data untuk kasus baru dari server, dan hanya setelah menerimanya kami memodifikasi
$todos
. Langkah selanjutnya, kami akan mencoba memperbaiki situasi ini, tetapi pertama-tama saya akan mengalihkan perhatian Anda ke poin lain yang menarik. Jika kita kembali sedikit ke
versi sebelumnya , kita perhatikan: meskipun permintaan juga ada, case baru ditambahkan ke daftar secara instan, tanpa menunggu permintaan untuk mengakhiri
Ini adalah fitur lain untuk mengerjakan konverter asinkron di dap: jika hasil konverter asinkron tidak digunakan (yaitu, ia tidak ditugaskan untuk apa pun), maka Anda tidak bisa menunggu penyelesaiannya - dan pelaksanaan aturan tidak diblokir. Ini sering berguna: Anda mungkin memperhatikan bahwa ketika menghapus case dari daftar, mereka menghilang dari layar secara instan, tanpa menunggu hasil dari permintaan DELETE. Ini terutama terlihat jika Anda dengan cepat menghapus beberapa kasus berturut-turut dan melacak permintaan di panel Jaringan.
Tetapi, karena kita menggunakan hasil dari permintaan POST - menugaskannya ke
$
context - kita harus menunggu sampai selesai. Oleh karena itu, Anda perlu menemukan cara lain untuk memodifikasi
$todos
sebelum menjalankan permintaan POST. Solusi: masih, pertama buat objek bisnis baru dan segera tambahkan ke
$todos
, biarkan daftar gambar, dan hanya kemudian, setelah rendering, jika bisnis tidak memiliki
.url
(yaitu, bisnis baru saja dibuat), jalankan permintaan POST, dan memaksakan hasilnya pada konteks data dari kasus ini.
Jadi, pertama-tama kita hanya menambahkan sebuah blank yang hanya berisi
.title
ke dalam
.title
,'INPUT#new-todo placeholder="What needs to be done?" autofocus' .ui("$todos=($todos (#.value@title)@tgt):insert #.value=")
UL#todo-list > LI
Aturan
UL#todo-list > LI
Elemen
UL#todo-list > LI
Sudah Berisi
a!
memulai aturan-a setelah elemen pertama kali ditarik. Di sana kita dapat menambahkan peluncuran permintaan POST tanpa adanya
.url
. Untuk menyuntikkan bidang tambahan ke dalam konteks, dap memiliki
&
operator
.a("!? $completed $editing; ? .url:!; & (@method`POST todos@url headers $):query")
Kami melihat . Hal lain! Bahkan dengan jaringan yang lambat, daftar yang harus dilakukan diperbarui secara instan, dan pemberitahuan server dan pemuatan data yang hilang terjadi di latar belakang, setelah menggambar daftar yang diperbarui.
11. Kawan semuanya!
Di elemen
#header
ada tombol untuk instalasi massal / reset tanda penyelesaian untuk semua kasus dalam daftar. Untuk penugasan massal nilai ke bidang elemen array, kami hanya menulis pengonversi lain,:
:assign
, dan menerapkannya ke
$todos
dengan mengeklik
INPUT#toggle-all
,'INPUT#toggle-all type=checkbox' .ui("$todos=($todos (#.checked@completed)@src):assign") ... assign: x => x.todos && x.todos.map(todo => Object.assign(todo,x.src))
Dalam hal ini, kami hanya tertarik pada bidang
.completed
, tetapi mudah untuk melihat bahwa dengan konverter seperti itu, Anda dapat secara besar-besaran mengubah nilai bidang apa pun dari elemen array.
Oke, dalam array
$todos
diaktifkan, sekarang kita perlu memberi tahu server tentang perubahan yang dilakukan. Dalam contoh asli, ini dilakukan dengan mengirimkan permintaan PATCH untuk setiap kasus - bukan strategi yang sangat efektif, tetapi tidak lagi tergantung pada kami. Oke, untuk setiap kasus kami mengirim permintaan PATCH
.ui("*@ $todos=($todos (#.checked@completed)@src):assign; (@method`PATCH .url:dehttp headers (.completed)):query")
Kami melihat : Sebuah klik pada common daw meluruskan semua daw individual, dan server diberitahu oleh permintaan PATCH yang sesuai. Norma
12. Menyaring kasus berdasarkan penyelesaian
Selain daftar hal yang harus dilakukan, aplikasi juga harus dapat memfilter kasus dengan tanda penyelesaian dan menunjukkan penghitung tugas yang telah selesai dan belum selesai. Tentu saja, untuk pemfilteran kita akan sepele untuk menggunakan metode
filter()
sama
filter()
yang disediakan oleh JS itu sendiri.
Tapi pertama-tama Anda harus memastikan bahwa bidang
.completed
dari setiap kasus selalu benar, dan ketika Anda mengklik masing-masing kasus diperbarui dengan variabel
$completed
. Sebelumnya, ini tidak penting bagi kami, tetapi sekarang akan menjadi.
,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$patch=(.completed=$completed=#.checked) $recount=()")
Poin penting di sini adalah bahwa konteks data setiap kasus adalah objek kasus itu sendiri, yang terletak pada array
$todos
. Bukan satu salinan, atau konstruksi terkait, tetapi objek itu sendiri. Dan semua panggilan ke bidang
.title
,
.completed
,
.completed
url
- baca dan tulis - berlaku langsung ke objek ini. Oleh karena itu, agar pemfilteran array
$todos
berfungsi dengan benar, kita perlu penyelesaian case agar tidak hanya tercermin di layar, tetapi juga di bidang
.completed
objek
.completed
.
Untuk memperlihatkan hanya case dengan tanda kelengkapan yang diperlukan
.completed
dalam daftar, kami hanya akan memfilter
$todos
sesuai dengan filter yang dipilih. Filter yang dipilih adalah, Anda dapat menebaknya, variabel keadaan lain dari aplikasi kami, dan kami akan menyebutnya:
$filter
. Untuk memfilter
$todos
sesuai dengan
$filter
dipilih
$filter
mari kita pergi sepanjang thumbnail dan hanya menambahkan konverter lain, dari form
{list, filter} => daftar yang difilter , dan kita akan mengambil nama dan fungsi penyaringan dari "array asosiatif" (yaitu, JS biasa objek)
todoFilters
const todoFilters={ "All": null, "Active": todo => !todo.completed, "Completed": todo => !!todo.completed }; '#todoapp'.d("$todos= $filter="
Kami periksa . Filter berfungsi dengan baik. Ada nuansa di mana nama-nama filter ditampilkan bersama, karena di sini kami melangkah sedikit dari struktur DOM yang asli dan keluar dari CSS. Tapi kita akan kembali ke sini nanti.
13. Penghitung kasus yang selesai dan aktif.
Untuk menampilkan penghitung kasing yang aktif dan case, cukup filter
$todos
dengan filter yang sesuai dan perlihatkan panjang array yang dihasilkan
,'#footer'.d("$active=($todos @filter`Active):filter $completed=($todos @filter`Completed):filter" ,'#todo-count'.d("! (active $active.length)format")
Dalam
formulir ini, penghitung menunjukkan nilai yang benar pada saat boot, tetapi tidak menanggapi perubahan selanjutnya dalam penyelesaian urusan (saat mengklik pada daw). Faktanya adalah bahwa klik pada gagak, mengubah keadaan setiap kasus individu, tidak mengubah keadaan
$todos
- modifikasi elemen array bukanlah modifikasi dari array itu sendiri. Oleh karena itu, kami memerlukan sinyal tambahan tentang perlunya mendaftar ulang kasus. Sinyal semacam itu bisa menjadi variabel status tambahan, yang dimodifikasi setiap kali penghitungan ulang diperlukan. Sebut saja
$recount
.
Kami akan mendeklarasikan leluhur bersama dalam d-rule, kami akan memperbaruinya ketika kami mengklik daw, dan kami #footer
akan membuat elemen menjadi konsumen - untuk ini, cukup sebutkan variabel ini dalam d-rule-nya '#todoapp'.d("$todos= $filter= $recount="
Sekarang semuanya berfungsi sebagaimana mestinya, penghitung diperbarui dengan benar.14. Penghapusan semua kasus yang diselesaikan.
Penghapusan banyak kasus di TodoMVC diimplementasikan sebagai non-halal sebagai modifikasi batch - oleh beberapa permintaan. Baiklah, mari kita menghela nafas, mengangkat bahu, dan mengeksekusi sesuai dengan DELETE-request untuk setiap case yang sudah selesai - dan kita sudah memiliki semuanya $completed
. Dengan demikian, $todos
setelah penghapusan kasus yang diselesaikan, apa yang seharusnya sudah masuk$active
,'#clear-completed' .d("! (completed $completed.length)format") .ui("$todos=$active; *@ $completed; (@method`DELETE .url:dehttp):query")
Kami melihat : kami membuat beberapa urusan yang tidak perlu, menandainya dengan daw dan menghapus. Tab Network akan menunjukkan horor dari pendekatan ini ke operasi batch.15. Status di bilah alamat
Kembali ke pemilihan filter. Dalam contoh asli, filter yang dipilih tercermin di bilah alamat setelah #. Saat mengubah # -fragment di bilah alamat secara manual atau selama navigasi, filter yang dipilih juga berubah. Ini memungkinkan Anda untuk pergi ke halaman aplikasi dengan URL dengan filter tugas yang sudah dipilih.Anda dapat menulis dengan location.hash
operator urlhash
, misalnya, di aturan-a elemen #todoapp
(atau keturunannya), yang akan dieksekusi dengan setiap pembaruan$filter
.a("urlhash $filter")
Dan Anda dapat menginisialisasi variabel dengan $filter
nilai dari bilah alamat dan kemudian memperbarui oleh acara hashchange menggunakan pseudo-converter :urlhash
yang mengembalikan keadaan saat ini location.hash
(tanpa #) .d("$todos= $filter=:urlhash $recount=" .e("hashchange","$filter=:urlhash")
Acara hashchange dimunculkan oleh browser ketika # -fragment di bilah alamat berubah. Namun, untuk beberapa alasan hanya window
, dan document.body
dapat mendengarkan acara ini. Untuk melacak peristiwa ini dari suatu elemen #todoapp
, Anda harus menambahkan operator ke aturan-d listen
yang menandatangani elemen untuk menyampaikan peristiwa dari objekwindow
'#todoapp' .a("urlhash $filter") .e("hashchange","$filter=:urlhash") .d("$todos= $filter=:urlhash $recount=; listen @hashchange"
Kami melihat : beralih filter, melacak perubahan di bilah alamat, ikuti tautan dengan #Active , #All , #Completed . Semuanya berfungsi. Tapi kembali ke aslinya. Di sana, tampaknya, pilihan filter diterapkan - dengan mengklik tautan. Meski tidak terlalu praktis, kami akan melakukan hal yang sama demi kelengkapan. ,'UL#filters'.d("* filter" ,'LI'.d("" ,'A'.d("!! (`# .filter)concat@href .filter@") ) )
Dan untuk membuat filter yang dipilih menonjol, tambahkan operator stylization bersyarat !?
yang akan menambahkan kelas CSS ke elemen selected
jika nilai dalam bidang .filter
konteks sama dengan nilai variabel$filter
,'A'.d("!! (`# .filter)concat@href .filter@; !? (.filter $filter)eq@selected")
Dalam formulir ini, fungsi aplikasi dap kami sudah sepenuhnya (sejauh yang saya tahu) konsisten dengan apa yang dilakukan oleh aslinya .16. Beberapa sentuhan akhir
Saya tidak begitu suka dengan aslinya, bentuk kursor tidak berubah pada elemen yang aktif, jadi kami akan menambahkan head
gaya ini ke dokumen HTML kami [ui=click]{cursor:pointer}
Jadi setidaknya kita akan melihat di mana Anda bisa mengklik.Oh ya! Masih menulis kata "todos" dalam huruf kapital. Tapi di sini saya, mungkin, akan membiarkan diri saya akhirnya menunjukkan sedikit imajinasi dan kreativitas, dan alih-alih hanya "todos" saya akan menulis "dap todos" ,'H1'.d("","dap todos")
Wow Sekarang aplikasi kita dapat dianggap lengkap, dan tutorialnya diadakan (jika Anda dengan jujur membaca baris-baris ini).Kesimpulannya
Mungkin, saat membaca, Anda mendapat kesan bahwa program dap ditulis dengan coba-coba - ini semua "mari kita lihat apa yang terjadi", "tampaknya berhasil, tetapi ada nuansa", dll. Ini sebenarnya tidak demikian.
Semua nuansa ini cukup jelas dan dapat diprediksi saat menulis kode. Tapi saya pikir akan bermanfaat untuk menunjukkan dengan contoh nuansa ini mengapa keputusan ini atau itu ada dalam aturan dan mengapa itu dilakukan dengan cara ini dan bukan sebaliknya.Tanyakan, seperti kata mereka, pertanyaan.