Tutorial dari tutorial Ember.js. Aplikasi Super Rentals. Bagian 1.2

Kami terus menerbitkan terjemahan tutorial dari panduan resmi Ember.js. Tutorial terdiri dari dua bagian dan ini adalah bagian kedua dari bagian pertama tutorial. Kami mengingatkan Anda bahwa Anda dapat membaca paruh pertama di tautan ini


Daftar topik yang dibahas dalam tutorial menyarankan:


  • Menggunakan Ember CLI
  • Navigasi file aplikasi dan folder Ember
  • Buat dan tautkan antara halaman
  • Templat dan komponen
  • Pengujian Otomatis
  • Bekerja dengan data server
  • Segmen dinamis dalam rute
  • Layanan di Ember
  • Perpustakaan Data Ember
  • Adaptor dan serialisator
  • Pola Komponen Penyedia

Duduk, buka terminal, temukan proyek di komputer Anda dan mari kita lanjutkan. Dan ingat bahwa jika Anda mengalami kesulitan, Anda selalu dapat meminta bantuan di saluran komunitas Discord (di saluran Rusia # lang-russian ), serta di saluran telegram berbahasa Rusia ember_js


Rincian Komponen


Saatnya akhirnya bekerja di daftar rental:



Dengan menyusun daftar properti sewaan ini, Anda akan belajar tentang:


  • Pembuatan komponen
  • Pengorganisasian kode menggunakan komponen namespace
  • Meneruskan atribut HTML dengan ...attributes
  • Menentukan jumlah cakupan tes yang tepat

Pembuatan Komponen


Mari kita mulai dengan membuat komponen <Rental> . Kali ini kita akan menggunakan generator komponen untuk membuat templat dan file uji untuk kita:


 $ ember generate component rental installing component create app/components/rental.hbs skip app/components/rental.js tip to add a class, run `ember generate component-class rental` installing component-test create tests/integration/components/rental-test.js 

Generator membuat dua file baru untuk kita: templat komponen di app/components/rental.hbs dan file uji komponen dalam tests/integration/components/rental-test.js .


Kami akan mulai dengan mengedit templat. Mari kita buat hardcode detail untuk satu objek sewaan, lalu ganti dengan data nyata dari server.



Kemudian kami akan menulis tes untuk memastikan bahwa semua detail hadir. Kami akan mengganti tes templat yang dibuat untuk kami oleh tim kami sendiri, seperti yang kami lakukan sebelumnya untuk komponen <Jumbo> :



Tes harus lulus.



Akhirnya, mari kita tambahkan komponen baru ke templat indeks untuk mengisi halaman.



Dalam hal ini, kita harus melihat komponen <Rental> menunjukkan Grand Old Mansion kita tiga kali di halaman:



Semuanya terlihat cukup bagus untuk pekerjaan kecil!


Pengorganisasian kode menggunakan ruang nama


Selanjutnya, tambahkan gambar untuk properti sewaan. Kami akan menggunakan generator komponen untuk ini lagi:


 $ ember generate component rental/image installing component create app/components/rental/image.hbs skip app/components/rental/image.js tip to add a class, run `ember generate component-class rental/image` installing component-test create tests/integration/components/rental/image-test.js 

Kali ini kami memiliki / atas nama komponen. Ini mengarah pada pembuatan komponen di app/components/rental/image.hbs , yang dapat disebut sebagai <Rental::Image> .


Komponen serupa dikenal sebagai komponen namespace . Namespace memungkinkan kita untuk mengatur komponen kita ke dalam folder sesuai dengan tujuannya. Ini sepenuhnya opsional, tetapi mudah, terutama ketika Anda sedang mengembangkan aplikasi besar dalam tim.


Meneruskan atribut HTML dengan ...attributes


Mari kita edit templat komponen:



Alih-alih mengatur nilai spesifik untuk atribut src dan alt di <img> , kami memilih ...attributes kata kunci ...attributes , yang kadang-kadang juga disebut "splattributes". Ini memungkinkan Anda untuk melewatkan atribut HTML sewenang-wenang saat memanggil komponen ini, misalnya seperti ini:



Kami menetapkan di sini atribut HTML src dan alt , yang akan diteruskan ke komponen dan dilampirkan ke elemen, di mana ...attributes diterapkan dalam templat komponen. Anda mungkin berpikir ini mirip dengan {{yield}} , tetapi hanya untuk atribut HTML dan bukan untuk konten tampilan. Bahkan, kami sudah menggunakan fungsi ini sebelumnya ketika kami meneruskan atribut class ke <LinkTo> .



Dengan demikian, komponen <Rental::Image> tidak terkait dengan properti sewaan tertentu di situs. Tentu saja, semuanya juga dikodekan dengan kami, tetapi kami akan segera mengatasinya. Sementara itu, kami akan membatasi seluruh hardcode ke komponen sehingga lebih mudah untuk menghapusnya ketika kami melanjutkan untuk mengekstraksi data nyata.

Secara umum, itu adalah ide yang baik untuk menambahkan ...attributes ke elemen root di komponen Anda. Ini akan memberikan fleksibilitas maksimum, karena pemrakarsa mungkin perlu lulus kelas penataan gaya atau atribut ARIA untuk meningkatkan aksesibilitas.


Sekarang mari kita menulis tes untuk komponen baru kami!



Menentukan jumlah cakupan tes yang tepat


Terakhir, kita juga perlu memperbarui tes untuk komponen <Rental> untuk memastikan bahwa kita berhasil memanggil <Rental::Image> .



Karena kami sudah memiliki tes tertulis yang terkait dengan <Rental::Image> , di sini kami dapat menghilangkan detail dan meminimalkan pemeriksaan. Dengan demikian, kami juga tidak perlu memperbarui tes <Rental> setiap kali kami membuat perubahan pada <Rental::Image> .



Komponen interaktif


Dalam bab ini, Anda menambahkan interaktivitas ke halaman, yang memungkinkan pengguna mengklik gambar untuk memperbesar atau menguranginya:



Dalam hal ini, Anda akan belajar tentang:


  • Menambahkan Perilaku ke Komponen dengan Kelas
  • Status akses contoh dari template
  • Manajemen Negara dengan Properti yang Dilacak
  • Menggunakan sintaksis bersyarat dalam template
  • Menanggapi interaksi pengguna dengan tindakan
  • Cara memanggil pengubah elemen
  • Pengujian interaksi pengguna

Menambahkan Perilaku ke Komponen dengan Kelas


Sejauh ini, semua komponen yang kami tulis adalah murni presentasi - mereka hanya bagian dari markup yang dapat digunakan kembali. Ini tentu saja luar biasa, tetapi dalam komponen Ember dapat melakukan lebih banyak lagi!


Terkadang Anda ingin mengaitkan beberapa perilaku dengan komponen Anda sehingga mereka dapat melakukan hal-hal yang lebih menarik. Misalnya, <LinkTo> dapat merespons klik dengan mengubah URL dan pindah ke halaman lain.


Di sini kita akan melakukan hal serupa! Kami akan menerapkan fungsi Β«View LargerΒ» dan Β«View SmallerΒ» , yang akan memungkinkan pengguna kami mengklik gambar di rumah, melihat versi yang lebih besar, dan mengkliknya lagi untuk kembali ke versi yang lebih kecil.


Dengan kata lain, kita membutuhkan cara untuk mengganti gambar antara satu dari dua status . Untuk melakukan ini, kita memerlukan cara agar komponen menyimpan dua status yang mungkin dan mengetahui status saat ini.


Ember juga memungkinkan kita untuk mengaitkan kode JavaScript dengan komponen yang khusus untuk tujuan ini. Kita dapat menambahkan file JavaScript untuk komponen <Rental::Image> dengan menjalankan generator komponen:


 $ ember generate component-class rental/image installing component-class create app/components/rental/image.js 

Perintah ini menghasilkan file JavaScript dengan nama yang sama dengan templat komponen kami di app/components/rentals/image.js . Ini berisi kelas JavaScript yang diwarisi dari @glimmer/component .


Zoe menjelaskan ...



@glimmer/component atau @glimmer/component Glimmer adalah salah satu dari beberapa kelas komponen yang tersedia untuk digunakan. Mereka adalah titik awal yang bagus ketika Anda ingin menambahkan perilaku ke komponen Anda. Dalam tutorial ini, kita hanya akan menggunakan komponen Glimmer.

Secara umum, komponen Glimmer harus digunakan sedapat mungkin. Namun, Anda juga dapat melihat @ember/components (komponen klasik) yang digunakan dalam aplikasi yang lebih lama. Anda dapat membedakan mereka dari satu sama lain dengan melihat jalur untuk mengimpornya (yang berguna saat mencari dokumentasi yang sesuai, karena mereka memiliki API yang berbeda dan tidak kompatibel ).

Ember instantiate kelas setiap kali komponen kita dipanggil. Kita dapat menggunakan contoh ini untuk menyimpan status kita:



Di sini, di konstruktor komponen , kami menginisialisasi variabel instan this.isLarge dengan nilai false , karena ini adalah keadaan default yang kami inginkan untuk komponen kami.


Status akses contoh dari template


Mari perbarui templat kami untuk menggunakan status ini yang baru saja kami tambahkan:



Dalam templat, kami memiliki akses ke variabel instan komponen. Sintaksis bersyarat {{#if ...}}...{{else}}...{{/if}} memungkinkan Anda menampilkan konten yang berbeda tergantung pada kondisinya (dalam hal ini, nilai variabel instan this.isLarge ). Menggabungkan kedua fungsi ini, kita masing-masing dapat memvisualisasikan versi gambar yang kecil dan besar.


Kami dapat memverifikasi ini dengan mengubah sementara nilai awal untuk file JavaScript kami. Jika kita mengubah app/components/rental/image.js untuk menginisialisasi this.isLarge = true ; di konstruktor kita harus melihat versi besar dari gambar properti di browser. Wow!



Setelah kami periksa, kami bisa mengubahnya. Kembali ke false .


Karena pola inisialisasi ini untuk variabel instan dalam konstruktor cukup umum, ada sintaks yang lebih pendek untuknya:



Fungsionalitas yang sama, tetapi jauh lebih pendek!


Tentu saja, pengguna kami tidak dapat mengedit kode sumber kami, jadi kami membutuhkan cara untuk mengganti ukuran gambar dari browser. Secara khusus, kami ingin mengaktifkan nilai this.isLarge setiap kali pengguna mengklik komponen kami.


Manajemen negara menggunakan properti yang dilacak


Mari kita ubah kelas kita dengan menambahkan metode untuk mengganti ukuran:



Kami melakukan beberapa hal di sini, jadi mari kita cari tahu.


Pertama, kami menambahkan dekorator @tracked ke isLarge instance @tracked . Anotasi ini memberi tahu Ember untuk melacak variabel ini untuk pembaruan. Setiap kali nilai variabel ini berubah, Ember secara otomatis menggambar ulang semua templat yang bergantung pada nilainya.


Dalam kasus kami, setiap kali kami menetapkan nilai baru untuk this.isLarge , anotasi this.isLarge @tracked memaksa Ember untuk mengevaluasi kembali kondisi {{#if this.isLarge}} di templat kami dan berganti masing-masing di antara dua blok.


Zoe menjelaskan ...



Jangan khawatir! Jika Anda merujuk ke variabel di templat, tetapi lupa menambahkan dekorator @tracked , dalam mode pengembangan Anda akan mendapatkan kesalahan yang jelas saat mengubah nilainya!

Kami memproses tindakan pengguna


Kemudian kami menambahkan metode toggleSize ke kelas kami, yang mengalihkan this.isLarge sebagai lawan dari kondisi saat ini ( false menjadi true atau true menjadi false ).


Akhirnya, kami menambahkan dekorator @action ke metode kami. Ini menunjukkan kepada Ember bahwa kami bermaksud menggunakan metode ini dari templat kami. Tanpa ini, metode tidak akan berfungsi dengan baik sebagai fungsi penanganan acara (dalam hal ini, penangan klik).


Zoe menjelaskan ...



Jika Anda lupa untuk menambahkan dekorator @action , Anda juga akan mendapatkan kesalahan ketika Anda mengklik tombol dalam mode pengembangan!

Sekarang saatnya menggunakan ini di templat:



Kami telah mengubah dua hal.


Pertama, karena kami ingin membuat komponen kami interaktif, kami mengganti tag yang berisi dari <div> ke <button> (ini penting karena alasan aksesibilitas). Dengan menggunakan tag semantik yang benar, kita juga akan mendapatkan fokus "bebas" dan interaksi keyboard.


Kemudian kami menggunakan pengubah {{on}} untuk melampirkan this.toggleSize sebagai penangan klik tombol.


Jadi, kami menciptakan komponen interaktif pertama kami. Coba cara kerjanya di browser!



Pengujian Interaksi Pengguna


Akhirnya, mari kita tulis tes untuk perilaku baru ini:




Hasil tes


Mari kita bersihkan template kita sebelum melanjutkan. Kami menambahkan banyak duplikat ketika kami memasukkan ekspresi kondisional ke dalam templat. Jika kita perhatikan lebih dekat, satu-satunya hal yang berbeda antara dua blok ini adalah:
1) Kehadiran kelas CSS "large" di <button> .
2) Teks «» dan «» .


Perubahan ini tersembunyi di banyak kode duplikat. Kami dapat mengurangi duplikasi dengan menggunakan ekspresi {{if}} sebagai gantinya:



Versi ekspresi {{if}} mengambil dua argumen. Argumen pertama adalah kondisinya . Argumen kedua adalah ekspresi, yang harus dieksekusi jika kondisinya benar.


Secara opsional, {{if}} dapat mengambil argumen ketiga ekspresi yang harus dieksekusi jika kondisinya salah. Ini berarti bahwa kami dapat menulis ulang label tombol sebagai berikut:



Apakah ini merupakan peningkatan dalam kejelasan kode kami adalah masalah selera. Bagaimanapun, kami telah secara signifikan mengurangi duplikasi dalam kode kami dan membuat potongan-potongan logika yang penting lebih terlihat.


Jalankan tes untuk terakhir kalinya untuk memastikan bahwa refactoring kami tidak merusak apa pun, dan kami akan siap untuk tantangan berikutnya!



Menggunakan kembali komponen


Bagian yang belum direalisasi dalam komponen adalah peta yang menunjukkan lokasi rumah, yang akan terus kami kerjakan:


Saat menambahkan peta, Anda akan belajar tentang:


  • Manajemen Konfigurasi Tingkat Aplikasi
  • Parameterisasi komponen dengan argumen
  • Akses ke argumen komponen
  • Menginterpolasi nilai dalam template
  • Mengganti atribut HTML di ... atribut
  • Refactoring dengan getter dan pelacakan otomatis (auto-track)
  • Mengambil Nilai JavaScript dalam Konteks Uji

Manajemen Konfigurasi Tingkat Aplikasi


Kami akan menggunakan API Mapbox untuk membuat peta untuk properti sewaan kami. Anda dapat mendaftar secara gratis dan tanpa kartu kredit.


Mapbox menyediakan API gambar peta statis yang menyajikan gambar peta dalam format PNG. Ini berarti bahwa kita dapat menghasilkan URL yang sesuai untuk parameter yang kita inginkan dan menampilkan peta menggunakan standar <img> . Kelas!


Jika Anda tertarik, Anda dapat menjelajahi opsi yang tersedia di Mapbox menggunakan kotak pasir interaktif .


Setelah Anda terdaftar pada layanan ini, ambil token publik Anda (token publik default) dan tempelkan ke config/environment.js :




Seperti namanya, config/environment.js digunakan untuk mengonfigurasi aplikasi kami dan menyimpan kunci API seperti ini. Nilai-nilai ini dapat diakses dari bagian lain dari aplikasi kami, dan mereka dapat memiliki nilai yang berbeda tergantung pada lingkungan saat ini (yang dapat berupa pengembangan, pengujian (pengujian) atau produksi (produksi)).


Zoe menjelaskan ...



Jika mau, Anda dapat membuat token akses Mapbox berbeda untuk digunakan di lingkungan yang berbeda. Minimal, setiap token harus memiliki cakupan "gaya: ubin" untuk menggunakan API Gambar Statis Mapbox.

Setelah menyimpan perubahan ke file konfigurasi kami, kami perlu memulai kembali server pengembangan kami untuk menerima perubahan file ini. Berbeda dengan file yang kami edit, config/environment.js tidak memulai ulang secara otomatis.


Anda dapat menghentikan server dengan menemukan jendela terminal tempat ember server berjalan, lalu tekan Ctrl + C Yaitu, dengan menekan tombol "C" pada keyboard sambil menahan tombol "Ctrl". Setelah berhenti, Anda dapat memulainya lagi menggunakan perintah ember server sama.


 $ ember server building... Build successful (13286ms) – Serving on http://localhost:4200/ 

Membuat komponen yang mengandung kelas komponen


Setelah menambahkan kunci API Mapbox ke aplikasi, mari kita buat komponen baru untuk peta kita.


 $ ember generate component map --with-component-class installing component create app/components/map.js create app/components/map.hbs installing component-test create tests/integration/components/map-test.js 

Karena tidak setiap komponen memiliki perilaku spesifik yang terkait dengannya, generator komponen secara default tidak membuat file JavaScript untuk kita. Seperti yang kita lihat sebelumnya, kita selalu dapat menggunakan generator komponen untuk menambahkannya nanti.


Namun, dalam hal komponen <Map> kami, kami cukup yakin bahwa kami akan memerlukan file JavaScript untuk beberapa perilaku yang belum kami definisikan! Oleh karena itu, kita dapat meneruskan flag --with-component-class generator --with-component-class sehingga kita memiliki semua yang kita butuhkan sejak awal.


Zoe menyarankan ...



Terlalu banyak mengetik? Gunakan ember g component map -gc . Bendera -gc menunjukkan komponen Glimmer ( g limmer c omponent), dan juga menghasilkan kelas ( g enerate c lass)

Parameterisasi komponen menggunakan argumen


Mari kita mulai dengan file JavaScript kita:



Di sini kita mengimpor token akses dari file konfigurasi dan mengembalikannya dari toter pengambil. Ini memungkinkan kita untuk mengakses token kita seperti ini. this.token di dalam kelas MapComponent dan di templat komponen. Penting juga untuk menyandikan token, jika mengandung karakter khusus yang tidak aman untuk URL.


Interpolasi nilai dalam pola


Sekarang mari kita beralih dari file JavaScript ke templat:



Pertama, kami memiliki elemen wadah untuk penataan.


Lalu kami memiliki <img> untuk meminta dan merender gambar peta statis dari Mapbox.


Template kami berisi beberapa nilai yang belum ada - @lat , @lng @lat , @lng , @zoom , dan @height . Ini adalah argumen dari komponen <Map> yang akan kami berikan ketika dipanggil.


Parameterisasi komponen kami dengan argumen, kami menciptakan komponen yang dapat digunakan kembali yang dapat dipanggil dari berbagai bagian aplikasi dan disesuaikan untuk memenuhi kebutuhan konteks spesifik ini. Kami sudah melihat ini dalam tindakan ketika menggunakan komponen <LinkTo> sebelumnya; kami perlu menentukan argumen @route ia tahu halaman mana yang harus dituju.


Kami telah memberikan nilai default yang masuk akal untuk atribut alt berdasarkan nilai @lng dan @lng . Anda mungkin memperhatikan bahwa kami secara langsung menginterpolasi nilai ke dalam nilai atribut alt . Ember akan secara otomatis menggabungkan nilai-nilai yang diinterpolasi ini ke dalam nilai string, termasuk pelaksanaan semua HTML yang diperlukan untuk melarikan diri.


Mengganti atribut HTML ke ...attributes


Kemudian kami menggunakan ...attributes untuk memungkinkan pemanggil untuk menyesuaikan <img> lebih lanjut, misalnya, meneruskan atribut tambahan seperti class , dan juga mengganti atribut alt default kami dengan atribut alt yang lebih spesifik atau ramah manusia.


Ketertiban itu penting! Ember menerapkan atribut sesuai urutan kemunculannya. Dengan menetapkan atribut nilai alt default terlebih dahulu (sebelum menerapkan ...attributes ), kami secara eksplisit memberi kesempatan kepada penelepon untuk memberikan atribut alt lebih khusus sesuai dengan kasus penggunaan yang diperlukan.


Karena atribut alt diteruskan (jika ada) akan muncul setelah kita, itu akan menimpa nilai yang kita tentukan. Di sisi lain, penting bahwa kita menetapkan atribut src , width dan height after ... sehingga mereka tidak secara tidak sengaja ditimpa oleh penyerang.


Atribut src menginterpolasi semua parameter yang diperlukan ke dalam format URL untuk API gambar statis Mapbox, termasuk token URL-safe this.token .


Akhirnya, karena kita menggunakan gambar @2x "retina", kita harus menentukan atribut width dan height . Jika tidak, <img> akan ditampilkan dua kali lebih banyak dari yang kami harapkan!


Kami telah menambahkan cukup banyak kode ke satu komponen, jadi mari kita menulis beberapa tes! Khususnya, kita perlu memastikan bahwa kita memiliki cakupan pengujian untuk perilaku atribut HTML yang diganti, yang kita bahas di atas.





Perhatikan bahwa hasAttribute qunit-dom dari qunit-dom mendukung penggunaan ekspresi reguler . , , src https://api.mapbox.com/ ( , ). , , .


… .



, ! , ? , <Map> <Rental> :



!



...



, , config/environment.js MAPBOX_ACCESS_TOKEN . ! , Ember-.

<Rental> , <Map> .



- (auto-track)


<Map> src <img> , . β€” JavaScript .


JavaScript API this.args.* . , URL .


...



this.args β€” API, Glimmer. (, «» ) (legacy) , API JavaScript .



! !



, @tracked . , , Ember .


, , , . , src lat , lng , width , height zoom this.args . , , , {{this.src}} .


Ember , . @tracked , Ember , (invalidate) , «» . (auto-track). , this.args ( , this.args.* ), @tracked Glimmer. , (Just works).


JavaScript


, :





API this.setProperties , .


, . ( !).


this , render . «» . .


!




<Rental> . , :



:


  • (model hook)
  • (mocking) JSON
  • (remote)
  • {{#each}}


<Rental> . , , , , . .


, , . , .


, Ember β€” . , , .


. app/routes/index.js :



, , . Route . , .


Route IndexRoute, , .



. ? model() . .


, . Ember , . , , ( ).


, . , async . await . ( : , Promise Javascript )


. , , JavaScript ( POJO (Plain Old Javascript Object)).


, , , . @model . POJO, .


, , title :



, .



Hebat!


, , , , , ! <Rental> , .


.


-, <Rental> @rental . <h1> , , :



@model <Rental> @rental , Β«Grand Old MansionΒ» <Rental> ! , @rental .




, Β«Grand Old MansionΒ», , .



, : , .


, , , , .


<Rental> . , setProperties , .



, <Rental> render @rental . , !



(mocking) JSON


, , , , , !


, , , API. , API . JSON . , JSON HTTP- β€” , API, β€” - . Keren!


? , JSON .zip. public .


, public :


 public β”œβ”€β”€ api β”‚ β”œβ”€β”€ rentals β”‚ β”‚ β”œβ”€β”€ downtown-charm.json β”‚ β”‚ β”œβ”€β”€ grand-old-mansion.json β”‚ β”‚ └── urban-living.json β”‚ └── rentals.json β”œβ”€β”€ assets β”‚ └── images β”‚ └── teaching-tomster.png └── robots.txt 4 directories, 6 files 

, , URL http://localhost:4200/api/rentals.json .



«» JSON. !


(remote)


. , .



?


, Fetch API JSON API /api/rentals.json , URL, .


, . Fetch API , fetch async , . , await .


Fetch API . , ; , JSON, json() . , await .



, , .




JSON:API , , .


-, JSON:API , "data" , . ; , , , β€” , .


, , . type id , (!). , , attributes .


, , , , : , , type , - . type "Standalone" "Community", , <Rental> .


JSON:API. .


:



(parsing) JSON attributes , type , . , , - .


! .


(helper) {{#each}}


, , β€” index.hbs , <Rental> . @rental @model . , @model β€” ()! , , .


.



{{#each}}...{{/each}} , . β€” β€” . β€” <Rental> , <li> .


{{rental}} . rental ? , ! as |rental| each . - , as |property| , {{property}} .


, .



Hore! , . fetch . , ?


, !



( 1.1 1.2)


Ember!


Ember, .


#Emberjs. Discord , ember_js


Ketika Anda kembali, kami akan mengandalkan apa yang kami pelajari di bagian pertama, dan beralih ke tingkat yang baru!


UPDATE: Dan pembongkaran terima kasih pengguna MK1301 untuk koreksi kesalahan.

Source: https://habr.com/ru/post/id482390/


All Articles