پروژه بزرگ Tracalorie (مدیریت کالری غذا)

Tracalorie Project

20 مرداد 1399
پروژه ی بزرگ Tracalorie (مدیریت کالری غذا)

با سلام و خسته نباشید خدمت همراهان گرامی روکسو. تا این قسمت از سری آموزشی پروژه های مدرن جاوا اسکریپت با عناصر زیادی در زبان جاوا اسکریپت کار کردیم و حالا نوبت به یک پروژه بزرگ می رسد. این پروژه چندین قسمت طول خواهد کشید و در آن از الگوی Module Pattern در جاوا اسکریپت استفاده خواهیم کرد. اگر با این الگو آشنایی ندارید به مقاله آشنایی با الگوی Module Pattern سری بزنید.

این برنامه، یک برنامه تغذیه ای است که به کاربر کمک می کند تعداد کالری های دریافتی خود در روز را حساب کند. در این پروژه برای قسمت جاوا اسکریپت از هیچ فریم ورک یا کتابخانه ای مثل جی کوئری استفاده نخواهد شد و فقط برای ظاهر برنامه (کدهای CSS) از کتابخانه های Materialize و Font Awesome استفاده خواهیم کرد که نیاز به جی کوئری دارد. من UI پروژه را برای شما کپی می کنم چرا که قصد آموزش کدنویسی HTML را نداریم:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
  <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
  <title>Tracalorie | Meal & Calorie Tracker</title>
</head>
<body>
  <!-- Navbar -->
  <nav>
    <div class="nav-wrapper blue">
      <div class="container">
        <a href="#" class="brand-logo center">Tracalorie</a>
        <ul class="right">
          <li><a href="#" class="clear-btn btn blue lighten-3">Clear All</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <br>

  <div class="container">
    <!-- Form Card -->
    <div class="card">
      <div class="card-content">
        <span class="card-title">Add Meal / Food Item</span>
        <form class="col">
          <div class="row">
            <div class="input-field col s6">
              <input type="text" placeholder="Add Item" id="item-name">
              <label for="item-name">Meal</label>
            </div>
            <div class="input-field col s6">
                <input type="text" placeholder="Add Calories" id="item-calories">
                <label for="item-calories">Calories</label>
            </div>
            <button class="add-btn btn blue darken-3"><i class="fa fa-plus"></i> Add Meal</button>
            <!--
            <button class="update-btn btn orange"><i class="fa fa-pencil-square-o"></i> Update Meal</button>
            <button class="delete-btn btn red"><i class="fa fa-remove"></i> Delete Meal</button>
            -->
            <button class="back-btn btn grey pull-right"><i class="fa fa-chevron-circle-left"></i> Back</button>
          </div>
        </form>
      </div>
    </div>

    <!-- Calorie Count -->
    <h3 class="center-align">Total Calories: <span class="total-calories">0</span></h3>

    <!-- Item List -->
    <ul id="item-list" class="collection">
      <!--
      <li class="collection-item" id="item-0">
        <strong>Steak Dinner: </strong> <em>1200 Calories</em>
        <a href="#" class="secondary-content">
          <i class="fa fa-pencil"></i>
        </a>
      </li>
      <li class="collection-item" id="item-0">
        <strong>Cookie: </strong> <em>400 Calories</em>
        <a href="#" class="secondary-content">
          <i class="fa fa-pencil"></i>
        </a>
      </li>
      <li class="collection-item" id="item-0">
        <strong>Eggs: </strong> <em>300 Calories</em>
        <a href="#" class="secondary-content">
          <i class="fa fa-pencil"></i>
        </a>
      </li>
    -->
    </ul>
  </div>

  <script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>    
  <script src="app.js"></script>
</body>
</html>

قسمت هایی که کامنت شده اند، قسمت هایی هستند که توسط جاوا اسکریپت مدیریت می شوند و در حالت عادی نمایش داده نمی شوند (زمانی که کاربر ما بخواهد یکی از ورودی های خود را ویرایش کند، دکمه های ویرایش و حذف نمایش داده خواهند شد). من این قسمت ها را کامنت کرده ام تا هنگام توسعه گیج کننده نباشند (چندین دکمه در یک صفحه جالب نیست) اما زمانی که به قسمت ویرایش پروژه برسیم آن ها را مدیریت خواهیم کرد. شما می توانید ظاهر برنامه را در تصویر زیر ببینید:

شکل ظاهری پروژه ی Tracalories
شکل ظاهری پروژه Tracalories

حالا سراغ app.js می رویم. در این جلسه فایل app.js به چهار قسمت اصلی تقسیم می شود:

  • کنترلر یا مدیریت کننده Storage (مربوط به local storage)
  • کنترلر یا مدیریت کننده Item های غذایی
  • کنترلر یا مدیریت کننده UI و ظاهر برنامه
  • کنترلر یا مدیریت کننده خود برنامه (app) که کنترلر اصلی تمام برنامه خواهد بود

من این قسمت ها را در فایل app.js مشخص کرده ام:

// Storage Controller

// Item Controller

// UI Controller

// App Controller

از بین این چهار قسمت اصلی، فعلا Storage را کنار می گذاریم و بعدا سراغ آن می رویم. فعلا بدنه کلی کار را می نویسیم:

// Storage Controller

// Item Controller
const ItemCtrl = (function(){

})();

// UI Controller
const UICtrl = (function(){
  
})();

// App Controller
const App = (function(ItemCtrl, UICtrl){
  
})(ItemCtrl, UICtrl);

همانطور که در کد بالا می بینید، بدنه تک تک این کنترلرها را نوشته ام. همچنین در کنترلر App که کنترلر اصلی برنامه است باید دو کنترلر دیگر را به آن پاس بدهید چرا که می خواهیم درون آن از هر دو کنترلر دیگر استفاده کنیم. در مرحله اول با Item Controller شروع می کنیم. ما باید درون این کنترلر یک تابع برای ساختن آیتم ها داشته باشیم بنابراین:

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

این constructor به سادگی خصوصیات ما را می سازد بنابراین حالا باید ساختار داده های برنامه را تعریف کنیم:

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

  // Data Structure / State
  const data = {
    items: [],
    currentItem: null,
    totalCalories: 0
  }
})();

در اینجا شیء ای به نام data داریم که ساختار داده های برنامه ما را مشخص می کند. اگر با برنامه های react یا angular آشنا باشید، می دانید که این کار چیزی شبیه به ساخت state در این برنامه ها است. Items ما آرایه ای از انواع آیتم های غذایی خواهد بود (مثلا شیرینی، استیک و ...)، currentItem نیز در هنگام ویرایش به کار ما می آید و آیتمی را مشخص می کند که کاربر روی آن کلیک کرده باشد (آیتم فعلی) و نهایتا totalCalories را داریم که مجموع کالری کل آیتم های غذایی را به ما می دهد. ساختار داده های ما در حالت اولیه به شکل بالا خواهد بود. البته برای اینکه کار ما آسان تر شود و ببینیم با چه آیتم هایی طرف هستیم و ظاهر برنامه چطور می شود باید آیتم های پیش فرضی را درون items قرار بدهیم:

  // 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
  }

اولین آیتم استیک و دومین آیتم کلوچه و سومین آیتم تخم مرغ است که به همراه id و مقدار کالری شان تعریف شده اند. تمام چیز هایی که تا این قسمت نوشته ایم همگی خصوصی یا private هستند و در نهایت باید به نوعی به آن ها دسترسی داشته باشیم. برای این کار تابعی را return می کنیم که این کار را برایمان انجام دهد:

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

  // 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
  }

  // Public methods
  return {
    logData: function(){
      return data;
    }
  }
})();

تابعی که من تعریف کرده ام قرار است data را برگرداند تا وضعیت برنامه را بدانیم (در مراحل مختلف توسعه به دردمان می خورد). مرحله بعد راه اندازی اولیه برنامه است. در واقع زمانی که برنامه برای اولین بار باز می شود می خواهیم همه چیز مرتب باشد و شاید بخواهیم قسمت هایی از برنامه به شکل های خاصی در بیاید (مثلا درون input ها هیچ چیزی نوشته نشده باشد). این کار بر عهده کنترلر App است که فقط یک متد را برمی گراند و آن متد مسئولیت راه اندازی اولیه برنامه است:

// App Controller
const App = (function(ItemCtrl, UICtrl){

  // Public methods
  return {
    init: function(){
      console.log('Initializing App...');
    }
  }
  
})(ItemCtrl, UICtrl);

// Initialize App
App.init();

من فعلا عبارت Initializing App را در آن log کرده ام تا بعدا آن را تکمیل کنیم. در نهایت باید App.init را صدا بزنیم تا برنامه راه اندازی اولیه شود، گرچه فعلا کاری به جز log کردن رشته Initializing App انجام نمی دهد. در جلسات بعد باید در این قسمت کدی بنویسیم که آیتم ها را از ItemCtrl دریافت کرده و وارد UI کند، بنابراین تمام این ماژول ها با هم کار خواهند کرد.

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

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

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