MATIN MOLKARA
SERIES: JavaScript — PART 92 OF 108

92- آموزش جاوااسکریپت | پروژه عملی: قسمت 5 از 12- افزودن آیتم جدید به برنامه Calorie Tracker با Event Listener و Item Controller

در این بخش، فرایند افزودن آیتم جدید به برنامه Calorie Tracker را پیاده‌سازی می‌کنیم. ابتدا Event Listener مربوط به دکمه افزودن را می‌سازیم، سپس ورودی‌های فرم را از UI Controller می‌گیریم و در نهایت آیتم جدید را در Item Controller به ساختار داده اضافه می‌کنیم.

در بخش قبل، آیتم‌ها را از Item Controller دریافت کردیم و با استفاده از UI Controller آن‌ها را در رابط کاربری نمایش دادیم.

در این بخش، هدف ما این است که امکان افزودن یک وعده غذایی جدید همراه با مقدار کالری آن را فراهم کنیم.
برای این کار، باید رویداد کلیک روی دکمه افزودن را مدیریت کنیم، داده‌های فرم را بخوانیم، اعتبارسنجی اولیه انجام دهیم و در نهایت آیتم جدید را به ساختار داده اضافه کنیم.


ایجاد تابع loadEventListeners

بهتر است تمام رویدادهای برنامه را داخل یک تابع جداگانه قرار دهیم تا ساختار App Controller تمیزتر و قابل‌مدیریت‌تر باشد.

کد

// App Controller
const App = (function (ItemCtrl, UICtrl) {
  // بارگذاری رویدادها
  const loadEventListeners = function () {
    
  };

  return {
    init: function () {
      const items = ItemCtrl.getItems();
      UICtrl.populateItemList(items);

      loadEventListeners();
    }
  };
})(ItemCtrl, UICtrl);

App.init();

توضیح

در این ساختار، تمام Event Listenerها داخل تابع loadEventListeners قرار می‌گیرند و هنگام اجرای init فراخوانی می‌شوند.


دسترسی به UISelectors از داخل App Controller

از آنجا که آبجکت UISelectors داخل UI Controller تعریف شده و خصوصی است، نمی‌توانیم مستقیماً از App Controller به آن دسترسی داشته باشیم.
برای حل این موضوع، یک متد عمومی به نام getSelectors ایجاد می‌کنیم.

کد

// UI Controller
const UICtrl = (function () {
  const UISelectors = {
    itemList: '#item-list'
  };

  return {
    populateItemList: function (items) {
      let html = '';

      items.forEach(function (item) {
        html += `
          <li class="collection-item" id="item-${item.id}">
            <strong>${item.name}: </strong>
            <em>${item.calories} Calories</em>
            <a href="#" class="secondary-content">
              <i class="edit-item fa fa-pencil"></i>
            </a>
          </li>
        `;
      });

      document.querySelector(UISelectors.itemList).innerHTML = html;
    },

    getSelectors: function () {
      return UISelectors;
    }
  };
})();

افزودن سلکتور دکمه Add

برای ثبت رویداد کلیک روی دکمه افزودن، ابتدا باید سلکتور آن را به UISelectors اضافه کنیم.

کد

const UISelectors = {
  itemList: '#item-list',
  addBtn: '.add-btn'
};

توجه: مقدار کلاس باید مطابق HTML پروژه شما باشد. اگر در فایل HTML از کلاس دیگری استفاده کرده‌اید، همان مقدار را اینجا قرار دهید.


تعریف رویداد افزودن آیتم

حالا می‌توانیم در App Controller رویداد مربوط به افزودن آیتم را ثبت کنیم.

کد

// App Controller
const App = (function (ItemCtrl, UICtrl) {
  const loadEventListeners = function () {
    const UISelectors = UICtrl.getSelectors();

    document
      .querySelector(UISelectors.addBtn)
      .addEventListener('click', itemAddSubmit);
  };

  const itemAddSubmit = function (e) {
    e.preventDefault();
  };

  return {
    init: function () {
      const items = ItemCtrl.getItems();
      UICtrl.populateItemList(items);

      loadEventListeners();
    }
  };
})(ItemCtrl, UICtrl);

App.init();

توضیح

در اینجا:

  • با UICtrl.getSelectors() به سلکتورهای رابط کاربری دسترسی پیدا می‌کنیم
  • روی دکمه Add یک Event Listener از نوع click ثبت می‌کنیم
  • با e.preventDefault() از رفتار پیش‌فرض فرم جلوگیری می‌کنیم

دریافت داده‌های فرم از UI Controller

برای گرفتن مقدار فیلدهای فرم، بهتر است یک متد جداگانه در UI Controller تعریف کنیم.
این متد مقدار نام آیتم و کالری را برمی‌گرداند.


افزودن سلکتورهای فیلدهای ورودی

ابتدا باید شناسه فیلدهای فرم را به UISelectors اضافه کنیم.

کد

const UISelectors = {
  itemList: '#item-list',
  addBtn: '.add-btn',
  itemNameInput: '#item-name',
  itemCaloriesInput: '#item-calories'
};

ساخت متد getItemInput

حالا یک متد عمومی ایجاد می‌کنیم که ورودی‌های فرم را برگرداند.

کد

// UI Controller
const UICtrl = (function () {
  const UISelectors = {
    itemList: '#item-list',
    addBtn: '.add-btn',
    itemNameInput: '#item-name',
    itemCaloriesInput: '#item-calories'
  };

  return {
    populateItemList: function (items) {
      let html = '';

      items.forEach(function (item) {
        html += `
          <li class="collection-item" id="item-${item.id}">
            <strong>${item.name}: </strong>
            <em>${item.calories} Calories</em>
            <a href="#" class="secondary-content">
              <i class="edit-item fa fa-pencil"></i>
            </a>
          </li>
        `;
      });

      document.querySelector(UISelectors.itemList).innerHTML = html;
    },

    getItemInput: function () {
      return {
        name: document.querySelector(UISelectors.itemNameInput).value,
        calories: document.querySelector(UISelectors.itemCaloriesInput).value
      };
    },

    getSelectors: function () {
      return UISelectors;
    }
  };
})();

توضیح

این متد یک آبجکت شامل دو مقدار برمی‌گرداند:

  • name
  • calories

به این ترتیب، منطق خواندن داده‌های فرم داخل UI Controller باقی می‌ماند.


استفاده از ورودی فرم در itemAddSubmit

حالا می‌توانیم داخل تابع itemAddSubmit داده‌های فرم را دریافت کنیم.

کد

const itemAddSubmit = function (e) {
  const input = UICtrl.getItemInput();

  console.log(input);

  e.preventDefault();
};

اگر همه چیز درست باشد، با کلیک روی دکمه افزودن، آبجکتی شامل name و calories در کنسول نمایش داده می‌شود.


اعتبارسنجی اولیه فرم

قبل از افزودن آیتم، باید مطمئن شویم هر دو فیلد فرم مقدار دارند.

کد

const itemAddSubmit = function (e) {
  const input = UICtrl.getItemInput();

  if (input.name !== '' && input.calories !== '') {
    console.log('Valid Input');
  }

  e.preventDefault();
};

توضیح

در این مرحله فقط بررسی می‌کنیم که هر دو فیلد خالی نباشند.
اگر هر دو مقدار داشته باشند، می‌توانیم ادامه فرایند را اجرا کنیم.


ساخت متد addItem در Item Controller

اکنون باید متدی در Item Controller بسازیم که آیتم جدید را به آرایه items اضافه کند.

کد

// Item Controller
const ItemCtrl = (function () {
  const Item = function (id, name, calories) {
    this.id = id;
    this.name = name;
    this.calories = calories;
  };

  const data = {
    items: [
      { id: 0, name: 'Steak Dinner', calories: 1200 },
      { id: 1, name: 'Cookie', calories: 400 },
      { id: 2, name: 'Eggs', calories: 300 }
    ],
    currentItem: null,
    totalCalories: 0
  };

  return {
    getItems: function () {
      return data.items;
    },

    addItem: function (name, calories) {
      let id;

      if (data.items.length > 0) {
        id = data.items[data.items.length - 1].id + 1;
      } else {
        id = 0;
      }

      calories = parseInt(calories);

      const newItem = new Item(id, name, calories);

      data.items.push(newItem);

      return newItem;
    }
  };
})();

منطق تولید id

هر آیتم باید یک شناسه یکتا داشته باشد.
برای این کار:

  • اگر آرایه items خالی نباشد، id آخرین آیتم را می‌گیریم و یک واحد به آن اضافه می‌کنیم
  • اگر آرایه خالی باشد، id را برابر 0 قرار می‌دهیم

کد

if (data.items.length > 0) {
  id = data.items[data.items.length - 1].id + 1;
} else {
  id = 0;
}

این روش یک رفتار شبیه auto-increment ایجاد می‌کند.


تبدیل کالری به عدد

مقدار فیلدهای فرم به‌صورت رشته دریافت می‌شود.
اما کالری باید عدد باشد، بنابراین با parseInt آن را تبدیل می‌کنیم.

کد

calories = parseInt(calories);

ساخت و ذخیره آیتم جدید

بعد از آماده شدن id و calories، یک نمونه جدید از Item می‌سازیم و آن را به آرایه اضافه می‌کنیم.

کد

const newItem = new Item(id, name, calories);

data.items.push(newItem);

return newItem;

توضیح

در پایان، newItem را برمی‌گردانیم تا در مراحل بعد بتوانیم از آن برای به‌روزرسانی رابط کاربری استفاده کنیم.


فراخوانی addItem هنگام ثبت فرم

حالا باید بعد از اعتبارسنجی، آیتم جدید را از طریق Item Controller اضافه کنیم.

کد

const itemAddSubmit = function (e) {
  const input = UICtrl.getItemInput();

  if (input.name !== '' && input.calories !== '') {
    const newItem = ItemCtrl.addItem(input.name, input.calories);

    console.log(newItem);
  }

  e.preventDefault();
};

توضیح

اگر فرم معتبر باشد:

  1. name و calories به ItemCtrl.addItem() ارسال می‌شوند
  2. آیتم جدید ساخته می‌شود
  3. آیتم به آرایه items اضافه می‌شود
  4. همان آیتم جدید در متغیر newItem ذخیره می‌شود

بررسی نتیجه در ساختار داده

در این مرحله، آیتم جدید به ساختار داده اضافه می‌شود، حتی اگر هنوز در رابط کاربری نمایش داده نشود.
برای بررسی این موضوع، می‌توانید از متدی مثل logData استفاده کنید.

کد

console.log(ItemCtrl.logData());

اگر متد logData را از قبل داشته باشید، می‌توانید ببینید که آیتم جدید با id مناسب و مقدار کالری عددی به آرایه اضافه شده است.


نسخه کامل کد این بخش

app.js

// Storage Controller

// Item Controller
const ItemCtrl = (function () {
  const Item = function (id, name, calories) {
    this.id = id;
    this.name = name;
    this.calories = calories;
  };

  const data = {
    items: [
      { id: 0, name: 'Steak Dinner', calories: 1200 },
      { id: 1, name: 'Cookie', calories: 400 },
      { id: 2, name: 'Eggs', calories: 300 }
    ],
    currentItem: null,
    totalCalories: 0
  };

  return {
    getItems: function () {
      return data.items;
    },

    addItem: function (name, calories) {
      let id;

      if (data.items.length > 0) {
        id = data.items[data.items.length - 1].id + 1;
      } else {
        id = 0;
      }

      calories = parseInt(calories);

      const newItem = new Item(id, name, calories);

      data.items.push(newItem);

      return newItem;
    }
  };
})();

// UI Controller
const UICtrl = (function () {
  const UISelectors = {
    itemList: '#item-list',
    addBtn: '.add-btn',
    itemNameInput: '#item-name',
    itemCaloriesInput: '#item-calories'
  };

  return {
    populateItemList: function (items) {
      let html = '';

      items.forEach(function (item) {
        html += `
          <li class="collection-item" id="item-${item.id}">
            <strong>${item.name}: </strong>
            <em>${item.calories} Calories</em>
            <a href="#" class="secondary-content">
              <i class="edit-item fa fa-pencil"></i>
            </a>
          </li>
        `;
      });

      document.querySelector(UISelectors.itemList).innerHTML = html;
    },

    getItemInput: function () {
      return {
        name: document.querySelector(UISelectors.itemNameInput).value,
        calories: document.querySelector(UISelectors.itemCaloriesInput).value
      };
    },

    getSelectors: function () {
      return UISelectors;
    }
  };
})();

// App Controller
const App = (function (ItemCtrl, UICtrl) {
  const loadEventListeners = function () {
    const UISelectors = UICtrl.getSelectors();

    document
      .querySelector(UISelectors.addBtn)
      .addEventListener('click', itemAddSubmit);
  };

  const itemAddSubmit = function (e) {
    const input = UICtrl.getItemInput();

    if (input.name !== '' && input.calories !== '') {
      const newItem = ItemCtrl.addItem(input.name, input.calories);

      console.log(newItem);
    }

    e.preventDefault();
  };

  return {
    init: function () {
      const items = ItemCtrl.getItems();

      UICtrl.populateItemList(items);

      loadEventListeners();
    }
  };
})(ItemCtrl, UICtrl);

App.init();

جمع‌بندی

در این بخش، امکان افزودن آیتم جدید به برنامه را از نظر منطقی پیاده‌سازی کردیم.

در این مرحله:

  • تابع loadEventListeners را برای مدیریت رویدادها ساختیم
  • متد getSelectors را برای دسترسی به سلکتورها ایجاد کردیم
  • ورودی‌های فرم را با getItemInput دریافت کردیم
  • اعتبارسنجی اولیه برای خالی نبودن فیلدها انجام دادیم
  • متد addItem را در Item Controller پیاده‌سازی کردیم
  • برای هر آیتم جدید id یکتا تولید کردیم
  • مقدار کالری را از رشته به عدد تبدیل کردیم
  • آیتم جدید را به ساختار داده اضافه کردیم

در بخش بعدی، آیتم جدید را بعد از ثبت، مستقیماً در رابط کاربری نمایش می‌دهیم تا فرایند افزودن آیتم کامل شود.

// 0 comments
#JavaScript#Module Pattern#Event Listener#Item Controller#UI Controller#App Controller#Form Handling#DOM Manipulation#Calorie Tracker#آموزش جاوااسکریپت

نظرات (0)

نظر خود را بنویسید