پروژه ی محاسبه وام: تکمیل UI و موارد جزئی

20 آبان 1398
پروژه ی محاسبه وام: تکمیل UI و موارد جزئی

تکمیل UI و موارد جزئی

در قسمت قبل توانستیم فرمول های ریاضی محاسبه کننده ی وام را پیاده سازی کنیم و حالا دیگر مشکلی در زمینه ی محاسبه ی وام و سود آن نداریم اما در این قسمت می خواهیم UI را بهینه تر کنیم. به طور مثال قسمت پاسخ به کاربر را از اول نشان ندهیم بلکه تنها زمانی نشان دهیم که کاربر مقداری را در فرم وارد کرده و آن را ثبت می کند. همچنین در این قسمت می خواهیم فایل loading.gif را نیز در برنامه پیاده سازی کنیم تا زمانی که کاربر روی دکمه ی Calculate کلیک کرد علامت loading برایش به نمایش در بیاید. با این کار حس پردازش و واقعی تر بودن برنامه به کاربر القا می شود و UI کلی بهبود پیدا می کند.

اولین قدم برای انجام این کار خارج کردن loader از حالت کامنت است. اگر به فایل HTML ای که در جلسات قبلی به شما دادم بروید قسمتی بدین شکل را پیدا می کنید:

<!-- <div id="loading">
    <img src="img/loading.gif" alt="">
</div> -->

این قسمت کامنت شده ی loader است که شما باید آن را از حالت کامنت بیرون بیاورید. همچنین اگر ساختار پوشه های برنامه ی شما با این ساختار تفاوت دارد باید قسمت src را طبق ساختار صحیح خودتان تغییر دهید. به محض اینکه این کد را از حالت کامنت خارج کنید، تصویر loader نیز در صفحه نمایش داده می شود.

من می خواهم هم تصویر loader و هم قسمت دوم را به طور کامل مخفی کنم و فقط در زمان پردازش داده های کاربر نمایش دهم. شما می توانید از یک فایل CSS برای اضافه کردن این کدها استفاده کنید اما من درون head در فایل HTML استایل دهی ها را انجام می دهم:

<style>
    #loading,
    #results {
        display: none;
    }
</style>

با اضافه کردن این کد درون تگ های <head> هم تصویر loading و هم قسمت نتایج مخفی می شوند.

حالا باید به app.js برویم تا تغییراتی را در آن ایجاد کنیم. در حال حاضر یک event-listener داریم:

document.getElementById('loan-form').addEventListener('submit', calculateResults);

بنابراین به محض کلیک روی این فرم، تابع calculateResults اجرا می شود اما ما می خواهیم کمی تاخیر در آن ایجاد کنیم تا loading بهتر نمایش داده شود. بنابراین این کد را تغییر می دهیم:

document.getElementById('loan-form').addEventListener('submit', function () {
  
});

بنابراین قرار است با کلیک روی دکمه ی calculate یک تابع دیگر اجرا شود که هنوز کدهایش را ننوشته ایم. قبل از نوشتن کدهای این تابع باید بدانیم که calculateResults دیگر event-handler ما نیست بنابراین هیچ رویدادی را به عنوان پارامتر نمی گیرد و e.preventDefault ای هم نخواهیم داشت پس این دو مورد را حذف کنید؛ حذف پارامتر:

function calculateResults(){

حالا قدم اول مخفی کردن results هنگام کلیک است:

document.getElementById('loan-form').addEventListener('submit', function (e) {

    // Hide results
    document.getElementById('results').style.display = 'none';

    e.preventDefault();
});

احتمالا با خودتان می گویید result که از اول مخفی است چرا دوباره آن را مخفی می کنیم؟ اگر کاربر روی calculate کلیک کند و ما نتایج را به او نمایش دهیم و او دوباره با مقادیری متفاوت روی calculate کلیک کند نتایج همانطور نشان داده می شوند. ما می خواهیم این اتفاق نیفتد، بلکه نتایج بسته شده و دوباره باز شوند.

قدم بعدی نمایش loader است:

document.getElementById('loan-form').addEventListener('submit', function (e) {

    // Show loader
    document.getElementById('loading').style.display = 'block';

    e.preventDefault();
});

اگر در حال حاضر به مرورگر برویم، با کلیک روی Calculate تصویر loading را می بینیم اما loading متوقف نمی شود. ما می خواهیم تصویر loading برای 2 ثانیه (یا هر چقدر که شما مد نظر دارید) نمایش داده شود بنابراین:

// Listen for submit
document.getElementById('loan-form').addEventListener('submit', function (e) {
    // Hide results
    document.getElementById('results').style.display = 'none';

    // Show loader
    document.getElementById('loading').style.display = 'block';

    setTimeout(calculateResults, 2000);

    e.preventDefault();
});

با استفاده از تابع setTimeout تابع calculateResults را بعد از 2 ثانیه صدا می زنیم. حالا باید به تابع calculateResults برویم تا وضعیت display نتایج را تغییر دهیم:

if (isFinite(monthly)) {
    monthlyPayment.value = monthly.toFixed(2);
    totalPayment.value = (monthly * calculatedPayments).toFixed(2);
    totalInterest.value = ((monthly * calculatedPayments) - principal).toFixed(2);

    // Show results
    document.getElementById('results').style.display = 'block';
} else {
    showError('Please check your numbers');
}

این کد باید درون شرط isFinite قرار بگیرد چرا که فقط زمانی می خواهیم نتایج را نمایش بدهیم که کاربر اطلاعات صحیح را وارد کرده باشد. در مرحله ی بعد باید loading را مخفی کنیم بنابراین می گوییم:

if (isFinite(monthly)) {
    monthlyPayment.value = monthly.toFixed(2);
    totalPayment.value = (monthly * calculatedPayments).toFixed(2);
    totalInterest.value = ((monthly * calculatedPayments) - principal).toFixed(2);

    // Show results
    document.getElementById('results').style.display = 'block';

    // Hide loader
    document.getElementById('loading').style.display = 'none';

} else {
    showError('Please check your numbers');
}

حالا اگر به مرورگر بروید و مقداری را در فرم وارد کنید و سپس calculate را کلیک کنید، در ظاهر هیچ مشکلی نیست. Loading برای 2 ثانیه نمایش داده می شود و سپس از بین رفته و به جای آن نتایج ظاهر می شود. مشکل جزئی برنامه ی ما این است که اگر کاربر مقدار اشتباهی را وارد فیلدها کند و روی calculate کلیک کند، loading برایش نمایش داده شده اما هیچ وقت از بین نمی رود! حتی پس از اینکه پیام خطای Please check your numbers را دریافت می کنیم loading از بین نمی رود. اگر به شرط isFinite بالا نگاه کنید، در قسمت else آن یک تابع به نام showError داریم بنابراین می توانیم دستور مخفی کردن loading را در آن قرار دهیم:

// Show Error
function showError(error){
  // Hide results
  document.getElementById('results').style.display = 'none';
  
  // Hide loader
  document.getElementById('loading').style.display = 'none';

  // Create a div
  const errorDiv = document.createElement('div');

  // Get elements
  const card = document.querySelector('.card');
  const heading = document.querySelector('.heading');

  // Add class
  errorDiv.className = 'alert alert-danger';

  // Create text node and append to div
  errorDiv.appendChild(document.createTextNode(error));

  // Insert error above heading
  card.insertBefore(errorDiv, heading);

  // Clear error after 3 seconds
  setTimeout(clearError, 3000);
}

همانطور که می بینید دو خط اول مخصوص مخفی کردن results و loading هستند. حالا اگر به برنامه بروید و کدها را تست کنید، می بینید که بدون هیچ مشکلی اجرا می شوند.

دانلود کدهای پروژه ی محاسبه گر وام

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

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