امیدوارم که توانسته باشید تا این قسمت پیش بیایید. حالا که قسمت اعظم برنامه را کدنویسی کرده ایم فقط بحث حذف کتاب ها و تبدیل به کلاس های ES6 مانده است. در پروژه ای که پیش روی ما است حروف x
کوچکی وجود دارد که برای حذف کتاب ها در نظر گرفته شده اند اما مشکلی وجود دارد. در زمان بارگذاری سایت و شروع برنامه هیچ کتابی وجود ندارد بنابراین هیچ دکمه ی حذفی نیز وجود ندارد و نمی توانیم در همان ابتدا این دکمه های حذف را درون یک const قرار دهیم. حتی اگر یک دکمه ی حذف وجود داشته باشد، بقیه ی دکمه هایی که بعدا توسط کاربر اضافه می شوند را چه کار کنیم؟
اگر یادتان باشد در چندین قسمت قبل و به زبان ساده مفهومی به نام event delegation را توضیح داده بودم که می توانید به آن مراجعه کنید. در اینجا هم راه حل همان event delegation است! بنابراین به انتهای فایل App.js و پس از event-listener فعلی بروید و یک event-listener دیگر ایجاد کنید:
// Event Listener for delete document.getElementById('book-list').addEventListener('click', function (e) { e.preventDefault(); }
همانطور که می بینید بر اساس مفهوم event delegation به جای هدف گرفتن دکمه های حذف (حروف x) پدر آن ها را (خود لیست) هدف قرار گرفته ایم. سپس اولین کاری که کرده ایم preventDefault کردن فرم بوده است تا چیزی ثبت نشود. من نمی خواهم کدهای بیشتری را به این قسمت اضافه کنم بلکه آن را به صورت یک متد در prototype می نویسم. من بالا تر از متد clearFields می نویسم:
// Delete Book UI.prototype.deleteBook = function(target){ if(target.className === 'delete') { target.parentElement.parentElement.remove(); } }
سوال: پارامتر target به صورت خودکار به این متد پاس داده خواهد شد. آیا می دانید چرا؟
پاسخ: همانطور که می دانید، شیء target به صورت خودکار درون event-listener ها وجود دارد و می توانید هر جایی از آن استفاده کنید. target به هدف آن event-listener اشاره می کند؛ به طور مثال اگر روی نام کتاب کلیک کنیم، target می شود قسمت «نام کتاب». با اینکه این متد خارج از event-listener ما تعریف شده است اما بعدا آن را برای اجرا، درون event-listener فراخوانی خواهیم کرد بنابراین شیء target را نیز به صورت دستی به آن پاس خواهیم داد.
قبل از اینکه این کد را توضیح بدهم باید به ساختار کلید های حذف کتاب (حروف X) در فایل HTML نگاهی بیندازیم. اگر یادتان باشد کد زیر مسئول ساخت این کلید ها در فایل HTML بود:
// Add Book To List UI.prototype.addBookToList = function(book){ const list = document.getElementById('book-list'); // Create tr element const row = document.createElement('tr'); // Insert cols row.innerHTML = ` <td>${book.title}</td> <td>${book.author}</td> <td>${book.isbn}</td> <td><a href="#" class="delete">X<a></td> `; list.appendChild(row); }
بنابراین یک تگ <a> درون <td> ما وجود دارد که همان حرف x است و کلاس delete را نیز دارد. همچنین برای حذف کامل یک ردیف از جدول باید تمام <tr> را حذف کنیم. از آنجایی که کلید حذف (کلاس delete) درون <td> است باید این مسیر را تا <tr> مسیر دهی کرده و سپس آن را حذف کنیم. حالا متوجه کد زیر می شوید؟
// Delete Book UI.prototype.deleteBook = function(target){ if(target.className === 'delete') { target.parentElement.parentElement.remove(); } }
بله <tr> پدر دوم تگ <a> است بنابراین دوبار از parentElement استفاده کرده ایم تا به آن برسیم. حالا می توانیم به event-listener خود برگردیم و از این متد در آن استفاده کنیم:
// Event Listener for delete document.getElementById('book-list').addEventListener('click', function(e){ // Instantiate UI const ui = new UI(); // Delete book ui.deleteBook(e.target); // Show message ui.showAlert('Book Removed!', 'success'); e.preventDefault(); });
از آنجایی که متد deleteBook در prototype مربوط به UI قرار دارد برای استفاده از آن باید یک نمونه (instance) از UI را بسازیم (new UI). سپس از متد deleteBook استفاده کرده ایم و همانطور که گفتم target را به صورت دستی به آن پاس داده ایم. در نهایت نیز با استفاده از متد showAlert پیام حذف موفقیت آمیز ردیف را نمایش خواهیم داد. اگر کد بالا را در مرورگر خود تست کنید، باید بدون هیچ نقصی کار کند. در صورت برخورد با مشکل سعی کنید دوباره کدهایتان را بررسی کنید و یا کدهای خود را با سورس کد این جلسه مقایسه نمایید (دانلود در انتهای این مقاله).
حالا که پروژه را به صورت کامل کدنویسی کردیم باید یک بار هم آن را با کلاس های ES6 بنویسیم تا با نحوه ی استفاده از آن ها نیز آشنا شویم. اولین کاری که باید انجام دهید، مراجعه به فایل index.html و تبدیل دستور script به appes6.js است:
<script src="appes6.js"></script>
حالا وارد فایل appes6.js شوید تا کار را شروع کنیم. اگر فایل app.js را به یاد داشته باشید، در آن دو constructor برای Book و UI داشتیم، بنابراین باید در appes6.js نیز دو کلاس به نام های Book و UI ایجاد کنیم:
class Book { } class UI { }
کلاس Book بسیار ساده خواهد بود. در واقع فقط یک constructor خواهد داشت:
class Book { constructor(title, author, isbn) { this.title = title; this.author = author; this.isbn = isbn; } }
این کد دقیقا معادل کد زیر در app.js است:
// Book Constructor function Book(title, author, isbn) { this.title = title; this.author = author; this.isbn = isbn; }
حالا به سراغ کلاس UI می رویم. متد اول ما addBookToList بود، همچنین متدهای دیگری را نیز داشتیم. بنابراین ابتدا بدنه ی خالی تمام متدهایمان را وارد فایل appes6.js می کنیم:
class UI { addBookToList(book) { } showAlert(message, className) { } deleteBook(target) { } clearFields() { } }
در همین ابتدا متوجه می شوید که استفاده از کلاس ها چقدر تمیزتر و جمع و جور تر است! کدهای درون این متدها دقیقا مانند ES5 عمل می کند. بنابراین می توانیم بدنه ی این توابع را از فایل App.js کپی کنیم:
class UI { addBookToList(book) { const list = document.getElementById('book-list'); // Create tr element const row = document.createElement('tr'); // Insert cols row.innerHTML = ` <td>${book.title}</td> <td>${book.author}</td> <td>${book.isbn}</td> <td><a href="#" class="delete">X<a></td> `; list.appendChild(row); } showAlert(message, className) { // Create div const div = document.createElement('div'); // Add classes div.className = `alert ${className}`; // Add text div.appendChild(document.createTextNode(message)); // Get parent const container = document.querySelector('.container'); // Get form const form = document.querySelector('#book-form'); // Insert alert container.insertBefore(div, form); // Timeout after 3 sec setTimeout(function(){ document.querySelector('.alert').remove(); }, 3000); } deleteBook(target) { if(target.className === 'delete') { target.parentElement.parentElement.remove(); } } clearFields() { document.getElementById('title').value = ''; document.getElementById('author').value = ''; document.getElementById('isbn').value = ''; } }
این کلاس UI نیز کامل شده است و برای بقیه ی این موارد نیز می توانیم کدهای app.js را کپی کنیم:
class Book { constructor(title, author, isbn) { this.title = title; this.author = author; this.isbn = isbn; } } class UI { addBookToList(book) { const list = document.getElementById('book-list'); // Create tr element const row = document.createElement('tr'); // Insert cols row.innerHTML = ` <td>${book.title}</td> <td>${book.author}</td> <td>${book.isbn}</td> <td><a href="#" class="delete">X<a></td> `; list.appendChild(row); } showAlert(message, className) { // Create div const div = document.createElement('div'); // Add classes div.className = `alert ${className}`; // Add text div.appendChild(document.createTextNode(message)); // Get parent const container = document.querySelector('.container'); // Get form const form = document.querySelector('#book-form'); // Insert alert container.insertBefore(div, form); // Timeout after 3 sec setTimeout(function(){ document.querySelector('.alert').remove(); }, 3000); } deleteBook(target) { if(target.className === 'delete') { target.parentElement.parentElement.remove(); } } clearFields() { document.getElementById('title').value = ''; document.getElementById('author').value = ''; document.getElementById('isbn').value = ''; } } // Event Listener for add book document.getElementById('book-form').addEventListener('submit', function(e){ // Get form values const title = document.getElementById('title').value, author = document.getElementById('author').value, isbn = document.getElementById('isbn').value // Instantiate book const book = new Book(title, author, isbn); // Instantiate UI const ui = new UI(); console.log(ui); // Validate if(title === '' || author === '' || isbn === '') { // Error alert ui.showAlert('Please fill in all fields', 'error'); } else { // Add book to list ui.addBookToList(book); // Show success ui.showAlert('Book Added!', 'success'); // Clear fields ui.clearFields(); } e.preventDefault(); }); // Event Listener for delete document.getElementById('book-list').addEventListener('click', function(e){ // Instantiate UI const ui = new UI(); // Delete book ui.deleteBook(e.target); // Show message ui.showAlert('Book Removed!', 'success'); e.preventDefault(); });
به همین راحتی توانستیم پروژه ی خودمان را از ES5 به ES6 بیاوریم. درست است که این پروژه یک پروژه ی ساده بود اما انجام این کار برای پروژه های سنگین نیز آنچنان سخت نیست. باور ندارید؟ خودتان امتحان کنید! در قسمت بعد تمام این پروژه را به Local Storage نیز اضافه می کنم تا با refresh شدن صفحه، محتوای آن از بین نرود.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.