Artikel ini memperkenalkan ide implementasi gambar umum di halaman web untuk secara langsung menghasilkan pratinjau gambar kecil pada halaman setelah mengunggahnya. Mempertimbangkan bahwa fungsi ini memiliki penerapan tertentu, logika yang relevan dienkapsulasi ke dalam komponen ImageuploadView. Efek penggunaan aktual dapat digunakan untuk melihat rendering git di paragraf berikutnya. Dalam proses menerapkan komponen ini, kami menggunakan konten yang relevan yang diperkenalkan di blog sebelumnya, seperti warisan pustaka kelas.js, dan pustaka manajemen acara Eventbase.js dari komponen apa pun, yang juga mencakup beberapa pemikiran tentang pemisahan tanggung jawab, pemisahan kinerja dan perilaku. Selamat datang untuk membaca dan berkomunikasi.
Efek demonstrasi:
Catatan: Karena kode untuk demonstrasi semuanya statis, komponen yang diunggah oleh file disimulasikan oleh SetTimeout, tetapi metode panggilannya persis sama seperti ketika saya menggunakan komponen unggah dalam pekerjaan aktual saya, sehingga implementasi kode dari efek demonstrasi sepenuhnya sejalan dengan persyaratan fungsional yang sebenarnya.
Menurut ide blog saya sebelumnya, mari kita pertama kali memperkenalkan persyaratan untuk fungsi pratinjau unggahan ini.
1. Analisis Persyaratan
Menurut rendering demonstrasi sebelumnya, persyaratan analisis adalah sebagai berikut:
1) Pada area unggahan awal, hanya satu tombol unggahan yang dapat diklik ditampilkan. Saat mengklik tombol ini, gambar yang diunggah akan ditampilkan di area pratinjau berikutnya.
2) Setelah gambar yang diunggah ditambahkan ke area pratinjau, Anda dapat menghapusnya dengan menghapus tombol.
3) Ketika jumlah total gambar yang diunggah mencapai batas tertentu, misalnya, batas unggahan dalam demonstrasi adalah 4, hapus tombol unggah;
4) Ketika jumlah total gambar yang diunggah mencapai batas tertentu, jika gambar tertentu dihapus melalui operasi penghapusan, tombol unggahan harus ditampilkan.
Persyaratan di atas terlihat. Berdasarkan pengalaman, persyaratan yang dapat dianalisis adalah sebagai berikut:
1) Jika halaman dalam keadaan pengeditan, yaitu, status diminta dari database, selama daftar gambar tidak kosong, gambar harus ditampilkan di awal; dan itu juga harus mengontrol apakah tombol unggah ditampilkan sesuai dengan panjang daftar gambar yang ditemukan dan batas unggah;
2) Jika halaman saat ini adalah keadaan yang hanya dapat dilihat tetapi tidak diubah, maka tombol unggah dan tombol hapus harus dilepas pada tahap awal.
Setelah analisis persyaratan selesai, izinkan saya menjelaskan ide implementasi saya.
2. Ide Implementasi
Karena ini adalah halaman formulir, jika Anda ingin mengirimkannya ke latar belakang setelah mengunggah gambar, Anda harus memerlukan bidang teks. Jadi ketika saya melakukan halaman statis, saya mempertimbangkan bidang teks ini. Setelah mengunggah gambar baru dan menghapus gambar, saya harus memodifikasi nilai bidang teks ini. Struktur bagian ini ketika halaman statis dibuat adalah sebagai berikut:
<div><label>Electronic version of the legal person ID card</label><div><input id="legalPersonIDPic-input" name="legalPersonIDPic"type="hidden"><ul id="legalPersonIDPic-view"><li><a href="javascript:;">+</a></li></ul><p>Please make sure the pictures are clear and the text is identifiable<a href = "#"> <i> </i> Lihat contoh </a> </p> </div> </div>
Dari struktur ini, kita juga dapat melihat bahwa saya meletakkan seluruh area unggahan dalam satu UL, dan kemudian menggunakan ul li pertama sebagai tombol unggahan. Untuk menyelesaikan fungsi ini, tugas utama kami adalah: mengunggah dan mengunggah panggilan balik, menambahkan atau menghapus pratinjau gambar, dan mengelola nilai bidang teks. Dari sudut pandang ini, dikombinasikan dengan gagasan pemisahan tanggung jawab, fungsi ini membutuhkan setidaknya tiga komponen, satu yang bertanggung jawab atas pengunggahan file, satu yang bertanggung jawab untuk manajemen pratinjau gambar, dan yang lain yang bertanggung jawab untuk manajemen nilai domain teks. Jangan pernah merangkum ketiga fungsi ini berpasangan atau semuanya. Dalam hal ini, kopling fungsional terlalu kuat dan komponen yang ditulis dapat diperluas dan dapat digunakan kembali. Jika interaksi diperlukan antara ketiga komponen ini, kita hanya perlu mendefinisikannya ke antarmuka yang disebut secara eksternal dengan bantuan fungsi callback atau menerbitkan mode berlangganan.
Namun, manajemen nilai domain teks sangat sederhana, dan tidak masalah apakah itu ditulis sebagai komponen atau tidak, tetapi setidaknya enkapsulasi tingkat fungsi diperlukan. Meskipun komponen unggahan file bukanlah fokus artikel ini, ada banyak plug-in open source siap pakai di Internet, seperti WebUploader, yang dapat diterapkan apakah itu digunakan secara langsung atau enkapsulasi sekunder. Fungsi pratinjau gambar adalah konten inti dari artikel ini. Komponen ImageuploadView adalah enkapsulasi. Dari sudut pandang persyaratan, hanya ada tiga metode contoh semantik untuk komponen ini, yaitu Render, Tambahkan, dan Delitem. Render digunakan untuk menampilkan daftar pratinjau awal setelah inisialisasi selesai, Append digunakan untuk menambahkan pratinjau gambar baru setelah mengunggah, dan delitem digunakan untuk menghapus pratinjau gambar yang ada. Menurut ide dasar ini, kita hanya perlu merancang opsi dan acara untuk itu berdasarkan persyaratan dan pengalaman dalam pengembangan komponen.
Dari persyaratan sebelumnya, kami menemukan bahwa render komponen ImageUploadView ini akan dipengaruhi oleh status halaman. Saat halaman dalam mode tampilan, komponen ini tidak dapat mengunggah dan menghapus, sehingga Anda dapat mempertimbangkan untuk menambahkan opsi readitly ke dalamnya. Pada saat yang sama, operasi unggahan dan penghapusannya juga akan mempengaruhi logika UI dari tombol unggah, yang terkait dengan batas unggahan. Untuk fleksibilitas, batas unggahan juga harus dianggap sebagai opsi. Dari tiga metode instance yang disebutkan dalam paragraf sebelumnya, sesuai dengan pengalaman Anda sebelumnya dalam mendefinisikan peristiwa, metode instan umumnya mendefinisikan sepasang peristiwa, seperti plug-in bootstrap. Misalnya, metode render dapat menentukan render. Sebelum. Acara ini dipicu sebelum logika utama render dieksekusi. Jika pendengar eksternal memanggil metode preventDefault () dari acara ini, maka logika utama render tidak akan dieksekusi; Ada juga acara render. Setelah, yang dipicu setelah logika utama render dieksekusi. Keuntungan dari peristiwa definisi berpasangan ini adalah tidak hanya menyediakan metode eksternal untuk memperluas fungsionalitas komponen, tetapi juga meningkatkan manajemen perilaku default komponen.
Akhirnya, dari pengalaman kerja saya sebelumnya, selain mengunggah gambar untuk pratinjau, saya juga telah melakukan pengunggahan video, mengunggah audio, mengunggah dokumen biasa, dll., Jadi ketika saya menemukan fungsi ini kali ini, saya merasa bahwa saya harus mengekstrak hal -hal serupa dari fungsi -fungsi ini sebagai kelas dasar, mengunggah gambar, mengunggah video, dll., Masing -masing warisan yang melekat. Keuntungan lain dari kelas dasar ini adalah bahwa ia dapat sepenuhnya memisahkan logika umum dari struktur HTML. Di kelas dasar ini, hanya hal -hal umum yang dilakukan, seperti definisi opsi dan perilaku komponen (render, tambahkan, delitem), serta mendengarkan dan memicu peristiwa umum. Hanya perlu meninggalkan antarmuka tetap untuk diterapkan oleh subkelas. Dalam implementasi selanjutnya, saya mendefinisikan komponen FileUploadBaseview untuk menyelesaikan fungsi kelas dasar ini. Kelas dasar ini tidak mengandung logika apa pun untuk pemrosesan HTML atau CSS. Ini hanya mengabstraksi fungsi yang ingin kami selesaikan dan tidak menangani logika bisnis apa pun. Subkelas yang diimplementasikan sesuai dengan logika bisnis akan dibatasi oleh struktur HTML, sehingga ruang lingkup penerapan subkelas kecil; Dan karena kelas dasar sepenuhnya terpisah dari struktur HTML, ia memiliki ruang lingkup aplikasi yang lebih besar.
3. Detail Implementasi
Dari ide implementasi Bagian 2, kelas yang akan diimplementasikan adalah: FileuploadBaseview dan ImageuploadView, yang pertama adalah kelas dasar dari yang terakhir. Pada saat yang sama, mengingat bahwa komponen perlu menyediakan fungsi manajemen acara, kita perlu menggunakan eventbase.js di blog sebelumnya, dan FileUploadBasEview harus mewarisi komponen eventbase dari perpustakaan; Mempertimbangkan bahwa ada definisi dan warisan kelas, kita juga perlu menggunakan kelas pustaka warisan.js yang ditulis sebelumnya untuk mendefinisikan komponen dan hubungan warisan komponen. Hubungan warisan komponen terkait adalah: ImageuploadView memperluas FileUploadBasEview Exprest EventBase.
(Catatan: Kode terkait berikut menggunakan Seajs dalam modularitas.)
Apa yang dilakukan FileuploadBaseview adalah:
1) Tentukan opsi umum dan manajemen acara umum
Dalam konfigurasi default komponen ini, Anda dapat melihat definisi semua opsi umum dan acara umum:
var DEFAULTS = {data: [], //The list of data to be displayed must be of object type, such as [{url: 'xxx.png'},{url: 'yyyy.png'}]sizeLimit: 0, //To limit the number of elements displayed in BaseView, to 0, it means that it does not limit readonly: false, //To control whether elements in BaseView allow Penambahan dan Penghapusan Onbeforerender: $ .noop, //forereport.Baha acara, pemicu Onrender: $ .noop, //forerport.Ephter Acara sebelum metode render dipanggil, dan OnBeforeAppend: $ .noop, // memuaskan dengan append.ebefore peristiwa, pemicu onpendpend: $ .noop, // patuh dengan append. $ .noop, // mematuhi acara Delitem.BEFORE, memicu OnDelitem: $ .noop sebelum metode Delitem disebut // mematuhi acara Delitem. Setelah dipicu};Dalam metode init komponen, Anda dapat melihat logika inisialisasi untuk opsi umum dan manajemen acara:
init: function (elemen, opsi) {// panggil metode init dari eventbase kelas induk melalui this.base; // instance atribute var opts = this.options = this.getOptions (opsi); this.data = resolvedata (opts.data); hapus opts.data; this.sizelimit = opts.sizelimit; this.readOnly = opts.readonly; // mengikat peristiwa this.on ('render.ebefore', $ .proxy (opts.onbeforerender, ini)); this.on ('render.after', $ .proxy (opts.onrender, this)); this.on ('append.before', $ .proxy (opts.onbeforeAppend, this)); this.on ('append.after', $ .proxy (opts.onAppend, this)); this.on ('delitem.before', $ .proxy (opts.onbeforedelitem, ini)); this.on ('delitem.after', $ .proxy (opts.ondelitem, this));},2) Tentukan perilaku komponen dan antarmuka cadangan yang dapat diimplementasikan oleh subkelas:
render: function () {/*** render adalah templat. Subkelas tidak perlu mengganti metode render, ia hanya perlu mengganti metode _render* ketika metode render subclass dipanggil, metode render dari kelas induk disebut* tetapi ketika mengeksekusi metode _render, _render metode subkelas ini disebut* Ini akan menyatukan operasi pemicu dari sebelum dan sesudah peristiwa*/var; this.trigger (e = $ .event ('render.beFore')); if (e.isdefaultPrevented ()) kembali; this._render (); this.trigger ($. event ('render.after'));}, // subclass perlu mengimplementasikan _render method_render: function () {}, append: function (item) {var e; if (! item) return; item = resolvedataitem (item); this.trigger (e = $. eTEMEV) ('eVENT (' eVENT. return; this.data.push (item); this._append (item); this.trigger ($. event ('append.after'), item);}, // subclass perlu mengimplementasikan _append method_append: function (data) {}, delitem: function (uuid) {var e, item = this. = $ .Event ('delitem.before'), item); if (e.isdefaultPrevented ()) return; this.data.splice (this.getDataitemIndex (uuid), 1); this._delitem (item); this.trigger ($. Event ('delitem.after'), item); method_delitem: function (data) {}Untuk menangani logika distribusi peristiwa secara seragam sebelum dan sesudah perilaku, logika utama render, append, delitem diekstraksi ke dalam metode _render, _pen dan _delitem yang perlu diimplementasikan oleh subkelas. Ketika metode render suatu subkelas dipanggil, metode kelas induk sebenarnya disebut, tetapi ketika kelas induk menjalankan metode _render, metode subkelas dieksekusi, dan dua metode lainnya juga serupa. Perlu dicatat bahwa subkelas tidak dapat mengganti tiga metode yang dirender, ditambahkan, dan delitem, jika tidak, Anda harus menangani logika pemicu peristiwa terkait sendiri.
Implementasi keseluruhan FileuploadBasEview adalah sebagai berikut:
Define (function (membutuhkan, ekspor, modul) {var $ = membutuhkan ('jQuery'); var class = membutuhkan ('mod/class'); var eventBase = membutuhkan ('mod/eventbase'); var defaults = {data: [], // untuk menampilkan daftar data, elemen daftar harus dari tipe objek, seperti [{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{. 'yyyy.png'}] sizelimit: 0, // untuk membatasi jumlah elemen yang ditampilkan dalam baseeview, jika 0 berarti tidak ada batasan readonly: false, // untuk mengontrol apakah elemen dalam baseeview diizinkan untuk ditambahkan dan dihapus pada ridger: $ .noop, // rendering render. OnBeforeAppend: $ .noop, // Acara append. delItem.before event, trigger onDelItem: $.noop //corresponding delItem.after event, trigger };/*** Data processing, add a _uuid attribute to each record of data to facilitate searching */function resolveData(data) {var time = new Date().getTime();return $.map(data, function (d) {return resolveDataItem(d, waktu);});} Fungsi diselesaikan dengan (data, waktu) {waktu = waktu || Kelas Parentbase melalui this.base; // Instance Atribut var opts = this.options = this.getOptions (opsi); $ .proxy (opts.onbeforerender, ini)); this.on ('delitem.before', $ .proxy (opts.onbeforedElitem, this)); Default;}, getDataitem: function (uuid) {// dapatkan dateitemreturn this.data.filter (function (item) {return item._uuid === uuid;}) [0];}, getDataitemindex: function (uuid) {var ret; this.datae. foring (uuuak (uuid (uuid) {var ret; this.data. && (ret = i);}); return ret;}, render: function () {/*** render adalah templat. memicu operasi sebelum dan sesudah peristiwa*/var e; {var e; if (! item) return; item = resolvedataitem (item); this.trigger (e = $ .event ('append.beFore'), item); if (e.isdefaultPrevented () kembali; this.data.push (item); this. _append method_append: function (data) {}, delitem: function (uuid) {var e, item = this.getDataitem (uuid); if (! item) return; this.trigger (e = $ .event ('dellitem.before'), item); if (if (e.isDefrevent () return; this.data.splice (this.getDataitemIndex (uuid), 1); this._delitem (item); this.trigger ($. event ('delitem.after'), item);}, // subclass perlu mengimplementasikan _delitem method_delitem: function (data) {{{} {{} {{{{{ev events. Default}}); return fileuploadBaseview;});Implementasi ImageuploadView relatif sederhana, mirip dengan mengisi kekosongan. Ada beberapa poin untuk dijelaskan:
1) default kelas ini perlu memperluas default kelas induk untuk menambahkan opsi default subkelas ini, dan juga mempertahankan definisi opsi default kelas induk; Menurut struktur halaman statis, acara OnAppendClick telah ditambahkan, dan metode yang relevan dari komponen pengunggahan file dapat disebut secara eksternal dalam acara ini:
// mewarisi dan memperluas default default Default dari kelas induk default = $ .extend ({}, fileuploadbaseview.defaults, {onAppendClick: $ .noop // callback saat mengklik tombol unggah});2) Dalam metode init, metode init dari kelas induk perlu dipanggil untuk menyelesaikan pemrosesan logis umum tersebut; Di akhir init, Anda harus secara manual memanggil metode render sehingga Anda dapat melihat efeknya setelah komponen dipakai:
Implementasi lainnya adalah implementasi logika bisnis murni dan terkait erat dengan persyaratan Bagian 2.
Implementasi keseluruhan dari ImageuploadView adalah sebagai berikut:
define (fungsi (membutuhkan, ekspor, modul) {var $ = memerlukan ('jQuery'); var class = membutuhkan ('mod/class'); var fileuploadbaseview = membutuhkan ('mod/fileuploadbaseview'); // warisan dan memperluas default DefaultSvar Defaults = $ .Extend (},},}, {}, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{. $ .noop // Callback Saat mengklik tombol unggah}); var imageuploadView = class ({instanceMembers: {init: function (elemen, opsi) {var $ element = this. $ element = $ (elemen); var option = this.getoptions (opsi);/hubungi metode init dari kelas induk untuk melengkapi opsi, $ ini. opsi); // Tambahkan unggahan dan hapus pendengar dan pemicu pemrosesan if (! this.readonly) {var that = this; that.on ('appendClick', $ .proxy (opts.onAppendClick, this)); $ element.on ('click.pepend', '.view-Act-add', function (e) {E. E) ('click.pend', '.) itu.trigger ('appendClick');}); $ element.on ('click.remove', '.view-act-del', function (e) {var $ this = $ (e.currentTarget); that.delitem ($ this.data ('uuid')); e.preventDefault (); {return defaults;}, _ setitemaddHtml: function () {this. $ element.prepend ($ ('<li> <a href = "javascript:;">+</a> </li>');}, _ cleaseitemaddhtml: function ($ itemaddli)) {{ceedeItemaddhtml: function ($ itemaddli) {{{{{ceedeIteMaddHtml: function ($ itemaddli) {{{{{{{{{{_) {{_) () {var html = [], itu = this; // Jika itu bukan keadaan baca saja dan belum mencapai batas unggahan, tambahkan tombol unggah if (! (this.readonly || (this.sizeLimit && this.sizelimit <= this.data.length))) {this.setemad.sizeLimit <= this.data.length))) {this.setemad.sizeLimit <= this.data.length))) {this.setemad) (function (=) {this.seteMad.seteMad (tis. {html.push (that._getitemrenderHtml (item))}); this. $ element.append ($ (html.join ('')));}, _ getitemrenderHtml: function (item) {return ['<li id = "', item._uuid, '>"> ";" A haP = "<li id ="', item._uuid, '">; src = "', item.url,'"> ', this.readonly? this. $ element.find ('li.view-item-add'); // Jika batas unggah telah tercapai, hapus tombol unggah jika (this.sizelimit && this.sizelimit <= this.data.length && $ itemaddli.length) {this.cleareTemaddhtmlhtmlhtmlhtmly ($) {this.clearitemaddhtmlhtmlhtml ($ ($) {this.clearitemaddhtmlhtmlhtmlhtmlhtmlhtml ($) (! $ itemaddli.length) {this._setitemaddhtml ();}}}, _ append: function (data) {this. $ element.append ($ (this._getitemrenderHtml (data))); this._dealwithSizeLimit ();}, _ delit)); function._dealwithSizelimit (); data._uuid) .remove (); this._dealwithsizelimit ();}}, extople: FileuploadBaseview});4. Instruksi demo
Struktur proyek demo adalah:
Apa yang dibingkai adalah kode inti dari demonstrasi. Di antara mereka, FileUploadBaserView.js dan ImageuploadView.js adalah dua komponen inti yang diterapkan dalam implementasi sebelumnya. FileUploader.js digunakan untuk mensimulasikan komponen pengunggahan. Contohnya memiliki panggilan balik onsuccess, menunjukkan bahwa unggahan berhasil; Ada juga OpenChooseFilewin yang digunakan untuk mensimulasikan proses membuka jendela file seleksi dan mengunggahnya:
define (function (membutuhkan, ekspor, modul) {return function () {var imglist = ['../img/1.jpg','../img/2.jpg','../img/3.jpg','/img/4.jpg'], i = 0; var itu = this. function () {setTimeOut (function () {that.onsuccess (imGlist [i ++]); if (i == imglist.length) {i = 0;}}, 1000);}}});App/Regist.js adalah kode logis dari halaman demonstrasi, dan bagian -bagian utama telah dijelaskan dengan komentar:
Define (function (membutuhkan, ekspor, modul) {var $ = membutuhkan ('jQuery'); var imageuploadview = membutuhkan ('mod/imageuploadview'); var fileuploader = membutuhkan ('mod/fileuploader. Berhasil diunggah dan setelah komponen ImageUploadView menghapus item tertentu, itu akan mempengaruhi nilai $ legalPersonidpic var $ legalPersonidpic = $ ('#legalPersonIdicpic-input'), data = json.parse ($ legalpersonidpic () | | [] '); // Data adalah nilai awal. Component ImageUploadView // Setelah unggahan file berhasil diunggah, simpan file yang baru diunggah ke nilai $ legalPersonidpic // $ legalPersonidpic store var appendImageInputValue = function ($ input, item) {var value = json.parse ($ input.val () || '[]'); value.push (item); $ input.val (json.stringify (value));}; // Ketika komponen ImageUploadView dipanggil untuk menghapus item, informasi yang disimpan dalam $ legalPersonIdpic ($ uU -uU -u {varson. '[]'), index; value.foreach (function (item, i) {if (item._uuid === uuid) {index = i;}}); value.splice (index, 1); $ input.val (json.stringify (value));}; var fileuploader = new FileUploader () (nilai); {variLoader. item = {url: uploadValue};legalPersonIDPicView.append(item);appendImageInputValue($legalPersonIDPic, item);};var legalPersonIDPicView = new ImageUploadView('#legalPersonIDPic-view', {data: data,sizeLimit: 4,onAppendClick: function () {//Open the window to select the file FileUploader.openchooseFileWin ();}, ondeditem: function (data) {removeImageInputValue ($ legalPersonidpic, data._uuid);}});});5. Ringkasan artikel ini
Komponen ImageuploadView tidak sulit untuk diimplementasikan pada akhirnya, tetapi saya juga menghabiskan banyak waktu untuk memikirkannya dan kelas orang tua lainnya untuk mengimplementasikannya, dan sebagian besar waktu dihabiskan untuk mengabstraksikan pemisahan tanggung jawab dan pemisahan perilaku. Pandangan tentang dua aspek ide pemrograman yang diungkapkan dalam artikel ini hanyalah pengalaman pribadi saya. Karena level abstrak, metode pemikiran semua orang dan pemahaman akhir tidak akan sama. Karena itu, saya tidak dapat secara langsung mengatakan apakah saya benar atau salah. Tujuan menulis adalah untuk berbagi dan berkomunikasi, dan melihat apakah ada teman berpengalaman lainnya yang bersedia memberi tahu Anda tentang ide -ide mereka dalam hal ini. Saya percaya bahwa setelah semua orang membaca terlalu banyak tentang ide -ide orang lain, mereka juga akan membantu pelatihan ide pemrograman mereka sendiri.