برنامه مدیریت کالری غذا: اضافه کردن آیتم به UI

Tracalorie Project: Adding Items to the UI

21 مرداد 1399
برنامه ی مدیریت کالری غذا: اضافه کردن آیتم به UI

در قسمت قبل آیتم های خود را به Data structure اضافه کردیم و طی این مکانیسم آیدی های هر آیتم را نیز تعیین کردیم. اگر یادتان باشد برای اضافه کردن آیتم ها به data structure از متد زیر استفاده کرده بودیم:

  // Add item submit
  const itemAddSubmit = function(e){
    // Get form input from UI Controller
    const input = UICtrl.getItemInput();

    // Check for name and calorie input
    if(input.name !== '' && input.calories !== ''){
      // Add item
      const newItem = ItemCtrl.addItem(input.name, input.calories);
    }

    e.preventDefault();
  }

بنابراین ثابتی به نام newItem داریم که همان آیتم جدید ما است و حالا باید آن را به UI برنامه اضافه کنیم. من برای اضافه کردن آن از متد زیر استفاده می کنم:

  // Add item submit
  const itemAddSubmit = function(e){
    // Get form input from UI Controller
    const input = UICtrl.getItemInput();

    // Check for name and calorie input
    if(input.name !== '' && input.calories !== ''){
      // Add item
      const newItem = ItemCtrl.addItem(input.name, input.calories);

      // Add item to UI list
      UICtrl.addListItem(newItem);

      // Clear fields
      UICtrl.clearInput();
    }

    e.preventDefault();
  }

من در این کد از متد addListItem برای اضافه کردن آیتم به UI استفاده کرده ام و همچنین متدی به نام clearInput را نیز صدا زده ام که کارش پاک کردن فیلدهای input است (بعد از ثبت یک آیتم نمی خواهیم درون input باقی بماند). می دانید که هنوز هیچ کدام از این توابع را تعریف نکرده ایم بنابراین با تعریف addListItem در UICtrl شروع می کنیم:

// بقیه کدها //
getItemInput: function(){
      return {
        name:document.querySelector(UISelectors.itemNameInput).value,
        calories:document.querySelector(UISelectors.itemCaloriesInput).value
      }
    },
    addListItem: function(item){
      // Create li element
      const li = document.createElement('li');
      // Add class
      li.className = 'collection-item';
      // Add ID
      li.id = `item-${item.id}`;
      // Add HTML
      li.innerHTML = `<strong>${item.name}: </strong> <em>${item.calories} Calories</em>
      <a href="#" class="secondary-content">
        <i class="edit-item fa fa-pencil"></i>
      </a>`;
      // Insert item
      document.querySelector(UISelectors.itemList).insertAdjacentElement('beforeend', li)
    }

تابع addListItem در کد بالا تعریف شده است. ما یک <li> ساخته و کلاس 'collection-item' را به آن داده ایم. سپس انتظار داریم که id عنصر به همراه خود item به ما پاس داده شود بنابراین id عنصر <li> را برابر همان id گذاشته ایم. در واقع اگر یادتان باشد هر item ما یک شیء است که خصوصیت id و name و calories را درون خودش دارد. سپس کلاس ها و ساختار این <li> را مطابق بقیه <li> ها در فایل index.html تعریف کرده ایم (innerHTML). در نهایت با استفاده از insertAdjacentElement مشخص کرده ایم که عنصر تازه ساخته شده باید beforeend (آخرین مورد) در لیست باشد و آن را وارد UI کرده ایم.

حالا می توانید به مرورگر رفته و کدها را تست کنید، با کلیک روی دکمه Add Meal هر آیتم اضافه خواهد شد اما متوجه می شوید که هیچ کدام از فیلدهای input خالی نمی شوند و هر چه تایپ کردید در آن باقی می ماند. به همین خاطر است که باید clearInput را تعریف کنیم. من clearInput را بعد از تابع addListItem اضافه می کنم:

addListItem: function(item){
      // Create li element
      const li = document.createElement('li');
      // Add class
      li.className = 'collection-item';
      // Add ID
      li.id = `item-${item.id}`;
      // Add HTML
      li.innerHTML = `<strong>${item.name}: </strong> <em>${item.calories} Calories</em>
      <a href="#" class="secondary-content">
        <i class="edit-item fa fa-pencil"></i>
      </a>`;
      // Insert item
      document.querySelector(UISelectors.itemList).insertAdjacentElement('beforeend', li)
    },
    clearInput: function(){
      document.querySelector(UISelectors.itemNameInput).value = '';
      document.querySelector(UISelectors.itemCaloriesInput).value = '';
    },

از آنجایی که فقط دو input داریم، باید آن دو را خالی کنیم که با قرار دادن Value روی یک رشته خالی انجام می شود و کار سختی نیست. حالا می توانیم آیتم های پیش فرضی که درون کدها نوشته بودیم را کامنت کنیم:

  // Data Structure / State
  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
  }

از این لحظه به بعد باید خودمان آیتم ها را اضافه کنیم و اگر صفحه را refresh کنید هیچ آیتم پیش فرضی نخواهیم داشت. حتی اگر خودتان آیتمی را اضافه کنید اما صفحه را refresh کنید، آن آیتم از بین می رود. چرا؟ به دلیل اینکه هنوز آیتم ها را وارد local storage نکرده ایم.

نکته بعدی اینجاست که اگر صفحه را refresh کنید تا هیچ آیتمی نداشته باشید و سپس کدهای HTML مرورگر را نگاه کنید، به <ul> برمی خورید. یعنی با اینکه هیچ آیتمی وجود ندارد اما <ul> را داریم! من نمی خواهم این اتفاق بیفتد بنابراین در همان کنترلر UI، متد دیگری به نام hideList تعریف می کنم:

// بقیه کدها //
    clearInput: function(){
      document.querySelector(UISelectors.itemNameInput).value = '';
      document.querySelector(UISelectors.itemCaloriesInput).value = '';
    },
    hideList: function(){
      document.querySelector(UISelectors.itemList).style.display = 'none';
    },
    getSelectors: function(){
      return UISelectors;
    }
  }
})();

این کار با استفاده از خصوصیت display در CSS به راحتی قابل انجام است بنابراین از همین راه ساده و none کردن Display توانسته ایم <ul> را حذف کنیم. اما به نظر شما باید در چه قسمتی از برنامه آن را صدا بزنیم؟ اگر یادتان باشد تابع init در همان ابتدای بارگذاری برنامه به شکل زیر اجرا می شد:

// بقیه کدها //
  // Public methods
  return {
    init: function(){
      // Fetch items from data structure
      const items = ItemCtrl.getItems();

      // Populate list with items
      UICtrl.populateItemList(items);

      // Load event listeners
      loadEventListeners();
    }
  }
  
})(ItemCtrl, UICtrl);

// Initialize App
App.init();

بنابراین بهترین کار این است که پس از دریافت آیتم ها (متد getItems در کد بالا) وجود آن ها را با یک شرط if ساده چک کنیم. در صورتی که آیتمی وجود نداشت می توانیم متد hideList را صدا بزنیم:

// بقیه کدها //
  // Public methods
  return {
    init: function(){
      // Fetch items from data structure
      const items = ItemCtrl.getItems();

      // Check if any items
      if(items.length === 0){
        UICtrl.hideList();
      } else {
        // Populate list with items
        UICtrl.populateItemList(items);
      }

      // Load event listeners
      loadEventListeners();
    }
  }
  
})(ItemCtrl, UICtrl);

// Initialize App
App.init();

به عبارت ساده تر اگر آیتمی وجود داشت با استفاده از متد populateItemList درون <ul> قرار می گیرد و اگر آیتمی وجود نداشت، خود <ul> خالی را از نظر کاربر مخفی می کنیم تا فضا را بیهوده اشغال نکند. در حال حاضر برنامه ما مشکلی دارد. آیا می توانید مشکل را حدس بزنید؟

فرض کنید برنامه برای اولین بار اجرا شود. در بار اول هیچ آیتمی وجود ندارد بنابراین hideList صدا زده شده و <ul> ما مخفی می شود. سپس کاربر یک آیتم جدید را اضافه می کند اما <ul> دیگر هیچ گاه از حالت مخفی خارج نمی شود بنابراین هیچ آیتمی برای کاربر نمایش داده نمی شود. متوجه مشکل شدید؟ برای حل این مشکل باید به تابع addListItem برویم (در کنترلر UI) و <ul> را به حالت قبلی برگردانیم:

    addListItem: function(item){
      // Show the list
      document.querySelector(UISelectors.itemList).style.display = 'block';
      // Create li element
      const li = document.createElement('li');
      // Add class
      li.className = 'collection-item';
      // Add ID
      li.id = `item-${item.id}`;
      // Add HTML
      li.innerHTML = `<strong>${item.name}: </strong> <em>${item.calories} Calories</em>
      <a href="#" class="secondary-content">
        <i class="edit-item fa fa-pencil"></i>
      </a>`;
      // Insert item
      document.querySelector(UISelectors.itemList).insertAdjacentElement('beforeend', li)
    }

با block کردن حالت display می توانیم دوباره آن را نمایش بدهیم.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری پروژه‌های مدرن جاوا اسکریپت توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.