پروژه ی محاسبه وام: ساخت UI + فرمول های ریاضی

15 آبان 1398
پروژه ی محاسبه وام: ساخت UI + فرمول های ریاضی

ساخت UI

به پروژه ی دوم از سری «پروژه های مدرن جاوا اسکریپت» خوش آمدید. در این پروژه قصد داریم یک نرم افزار محاسبه ی وام ایجاد کنیم که سه فیلد مختلف را از شما می گیرد:

  • مقدار وام (load amount)
  • سود سالانه وام (Annual Interest)
  • بازه زمانی پرداخت وام در واحد سال (Repayment Years)

و در ازای آن ها سه مورد دیگر را به شما می دهد:

  • مقدار بازپرداخت ماهانه (Monthly Payment)
  • مجموع بازپرداخت (Total Payment)
  • مجموع سود پرداختی توسط شما (Total Interest)

همچنین اگر اعداد را غیرمرتبط وارد کنید برنامه به شما خطا داده و پیام خطا بعد از سه ثانیه از بین می رود. ظاهر برنامه ی ما بدین شکل است:

ظاهر برنامه ی محاسبه گر وام
ظاهر برنامه ی محاسبه گر وام

ما قصد داریم که در این جلسه ظاهر برنامه (UI) را تکمیل کنیم و برای سریع تر شدن کار از بوت استرپ (Bootstrap) استفاده خواهیم کرد. بنابراین به وب سایت https://getbootstrap.com/ مراجعه کرده و روی get started کلیک کنید:

نمایی از صفحه ی اول وب سایت بوت استرپ
نمایی از صفحه ی اول وب سایت بوت استرپ

با کلیک روی این لینک، به صفحه ای برده می شوید که در ابتدا به شما CDN های مختلف بوت استرپ را می دهد اما ما می خواهیم از starter template استفاده کنیم. برای شروع کار یک پوشه (در هر جایی مثل دسکتاپ) به نام Loan Calculator ایجاد کنید. سپس درون این پوشه فایل های index.html و app.js و یک پوشه به نام img ایجاد کنید تا تصویر loading را در آن قرار دهیم. شما باید تصویر loading را از این لینک دانلود کرده و درون این پوشه قرار بدهید. حالا به starter template بروید و کد آن را درون index.html کپی کنید (این کدها تنها وابستگی های بوت استرپ هستند - مواردی مانند CDN ها و...). البته باید تغییرات کمی را در آن ایجاد کنیم:

  • تگ <h1> را حذف کنید.
  • به تگ <body> کلاس bg-dark بدهید.
  • درون تگ <body> یک div با کلاس container و یک div دیگر درون div قبلی با کلاس row ایجاد کنید.
  • تگ <script> را به انتهای <body> اضافه کنید تا js خودمان را وارد پروژه کنیم.
  • از آنجایی که نمی خواهم وقت دوره را بگیرم کدها را دراختیار شما قرار می دهم بنابراین بقیه ی تغییرات را به صورت زیر ایجاد کنید:
<!doctype html>
<html lang="en">

<head>
    <title>Hello, world!</title>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
        integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>

<body class="bg-dark">
    <div class="container">
        <div class="row">
            <div class="col-md-6 mx-auto">
                <div class="card card-body text-center mt-5">
                    <h1 class="heading display-5 pb-3">Loan Calculator</h1>
                    <form id="loan-form">
                        <div class="form-group">
                            <div class="input-group">
                                <span class="input-group-addon">$</span>
                                <input type="number" class="form-control" id="amount" placeholder="Loan Amount">
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="input-group">
                                <span class="input-group-addon">%</span>
                                <input type="number" class="form-control" id="interest" placeholder="Interest">
                            </div>
                        </div>
                        <div class="form-group">
                            <input type="number" class="form-control" id="years" placeholder="Years To Repay">
                        </div>
                        <div class="forn-group">
                            <input type="submit" value="Calculate" class="btn btn-dark btn-block">
                        </div>
                    </form>
                    <!-- LOADER -->
                    <!--
            <div id="loading">
              <img src="img/loading.gif" alt="">
            </div>
            -->
                    <!-- RESULTS -->
                    <div id="results" class="pt-4">
                        <h5>Results</h5>
                        <div class="form-group">
                            <div class="input-group">
                                <span class="input-group-addon">Monthly Payment</span>
                                <input type="number" class="form-control" id="monthly-payment" disabled>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="input-group">
                                <span class="input-group-addon">Total Payment</span>
                                <input type="number" class="form-control" id="total-payment" disabled>
                            </div>
                        </div>

                        <div class="form-group">
                            <div class="input-group">
                                <span class="input-group-addon">Total Interest</span>
                                <input type="number" class="form-control" id="total-interest" disabled>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
        integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"
        integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh"
        crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"
        integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ"
        crossorigin="anonymous"></script>
    <script src="app.js"></script>
</body>

</html>

ظاهر برنامه برایتان باید چیزی شبیه به شکل زیر باشد:

ظاهر برنامه ی محاسبه گر وام
ظاهر برنامه ی محاسبه گر وام

پیاده سازی فرمول های ریاضی

حالا که UI برنامه تکمیل شده است موقع کدنویسی جاوا اسکریپت است. وارد فایل app.js شوید و مشخصا اولین کاری که می کنیم گوش دادن به دکمه ی Calculate (ثبت فرم) است چرا که این دکمه قرار است فرم ما را submit کند:

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

loan-form در واقع id فرم ما است و در صورت submit شدن تابعی به نام calculateResults را اجرا می کند. بنابراین باید این تابع را تعریف کنیم:

// Calculate Results
function calculateResults(e) {

    e.preventDefault();
}

اولین کاری که برای این تابع انجام داده ایم، جلوگیری از submit شدن فرم است (preventDefault) تا بتوانیم با استفاده از جاوا اسکریپت مقدار مورد نظر خودمان را محاسبه کنیم. در مرحله ی بعد باید تمام متغیرهای UI را تعریف کنیم. منظور من از متغیرهای UI متغیر هایی هستند که به عنصری در HTML اشاره می کنند. اگر یادتان باشد در پروژه ی قبلی نیز این کار را انجام دادیم. بنابراین می گوییم:

// Calculate Results
function calculateResults(e) {
    // UI Vars
    const amount = document.getElementById('amount');
    const interest = document.getElementById('interest');
    const years = document.getElementById('years');
    const monthlyPayment = document.getElementById('monthly-payment');
    const totalPayment = document.getElementById('total-payment');
    const totalInterest = document.getElementById('total-interest');

    e.preventDefault();
}

شما می توانید با مراجعه به فایل index.html تمام این متغیر ها را چک کرده و بر اساس id پیدایشان کنید. حالا نوبت نوشتن فرمول های محاسبه است. این فرمول های ریاضی برای محاسبه ی سود وام و ... استفاده می شوند و ریاضی محض هستند بنابراین اگر متوجه آن ها نمی شوید زیاد جای نگرانی نیست چرا که ربطی به برنامه نویسی ندارند:

  const principal = parseFloat(amount.value); 
  const calculatedInterest = parseFloat(interest.value) / 100 / 12;
  const calculatedPayments = parseFloat(years.value) * 12;

اولین متغیر principle است که مقدار وارد شده (مقدار وام درخواستی - فیلد Loan Amount) توسط کاربر را می گیرد. توجه داشته باشید که متغیر بالاتر از آن که amount نام داشت به خود فیلد اشاره می کرد نه مقدار تایپ شده درون آن، بنابراین amount.value مقدار وارد شده را به ما می دهد. برای محاسبه ی دقیق وام و سود آن باید مقدار وارد شده را به صورت اعشاری داشته باشیم بنابراین از تابع parseFloat استفاده کرده ایم که اعداد را تبدیل به اعداد اعشاری می کند.

دومین متغیر calculatedInterest است که «درصد سود» وام را دریافت می کند. این درصد توسط کاربر وارد می شود بنابراین فیلد interest را گرفته و مقدار (value) آن را دریافت می کنیم. این مقدار نیز باید به صورت اعشاری باشد بنابراین آن را درون parseFloat قرار داده ایم. در نهایت آن را تقسیم بر 100 و سپس تقسیم بر 12 کرده ایم. این موضوع به کدنویسی ما مربوط نیست بلکه فرمول محاسبه ی سود وام به همین شکل است که یک فرمول ریاضی است.

سومین متغیر calculatedPayments است که تعداد سال های بازپرداخت را مشخص می کند. به طور مثال یک وام 10000 دلاری را اگر با سود x در 10 ماه پرداخت کنیم، ماهانه چقدر باید بپردازیم. فرمول محاسبه ی آن هم به شکلی است که میبینید و از طرف کاربر دریافت می شود.

توجه داشته باشید که هنوز درون تابع calculateResults هستیم. در مرحله ی بعد باید مقدار بازپرداخت ماهیانه را حساب کنیم بنابراین:

    // Compute monthly payment
    const x = Math.pow(1 + calculatedInterest, calculatedPayments);
    const monthly = (principal * x * calculatedInterest) / (x - 1);

ابتدا متغیر x را تعریف کرده ایم. برای مقدار آن، سود بازپرداخت را به علاوه ی 1 کرده و آن را به توان calculatedPayments (سال های بازپرداخت) میرسانیم. Math.pow پارامتر اول را به عنوان پایه و پارامتر دوم را به عنوان توان قرار می دهد. همانطور که گفتم این ها فرمول های ریاضی برای محاسبه ی وام است و ربطی به برنامه نویسی ندارد. حالا متغیر x را گرفته و مقدار وام را در آن ضرب می کنیم، سپس حاصل را ضربدر سود بازپرداخت می نماییم و نتیجه را بر x-1 تقسیم می کنیم.

حالا باید مطمئن شویم که عدد monthly یک عدد محدود باشد (یعنی تا بی نهایت رقم اعشار نداشته باشد یا مقدارش برابر بی نهایت نشود بلکه مقداری محدود داشته باشد تا بتوانیم روی آن عملیات ریاضی انجام دهیم) چرا که برخی اوقات در جاوا اسکریپت اعداد اعشاری رفتارهای عجیبی از خود نشان داده و تا 10 یا 20 رقم اعشار جلو میروند! برای مطمئن شدن از محدود بودن monthly می توانیم از تابعی به نام isFinite استفاده کنیم:

    if (isFinite(monthly)) {
        monthlyPayment.value = monthly.toFixed(2);
        totalPayment.value = (monthly * calculatedPayments).toFixed(2);
        totalInterest.value = ((monthly * calculatedPayments) - principal).toFixed(2);
    } else {
        showError('Please check your numbers');
    }

بنابراین اگر monthly عددی محدود بود، مقدارش را درون فیلد Monthly Payment (مقدار بازپرداخت ماهانه) قرار می دهیم:

monthlyPayment.value = monthly.toFixed(2);

اما برای آنکه بیشتر از 2 رقم اعشار را نمایش ندهد از تابع toFixed استفاده کرده و به عنوان پارامتر عدد 2 (به معنی تا 2 رقم اعشار) را به آن داده ایم. همچنین برای total payment (مجموع بازپرداخت) باید مبلغ بازپرداخت ماهیانه را در تعداد سال های بازپرداخت ضرب کنیم. باز هم از toFixed برای نمایش حداکثر 2 رقم اعشار استفاده کرده ایم. برای مقدار سوم که محاسبه ی «مبلغ بازپرداختی به عنوان سود کل» است گفته ایم همان پرداخت کل را از principle تفریق کن. باز هم از toFixed استفاده می کنیم.

حالا اگر مقدار monthly عددی محدود نبود (قابل اندازه گیری نبود) در قسمت else قرار می گیریم که یعنی احتمالا کاربر اعداد را به صورت صحیح وارد نکرده است. برای این قسمت تابعی به نام showError قرار داده ایم که باید آن را تعریف کنیم. این تابع پیامی را دریافت می کند که در اینجا Please check your numbers را دریافت کرده است (به معنی «لطفا اعداد وارد شده را چک کنید»).

حالا خارج از تابع calculateResults تابع showError را کدنویسی می کنیم:

// Show Error
function showError(error) {
    // 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);
}

کد بالا را تا حدودی در پروژه ی قبل نیز داشتیم. ابتدا یک div ساخته ایم و کلاس های 'alert alert-danger' را نیز به آن داده ایم تا بوت استرپ خطای ما را قرمز نشان دهد. سپس عنصر card و heading را از DOM گرفته ایم تا به جاوا اسکریپت بگوییم خطای ما را درون card و قبل از heading وارد کن. با استفاده از appendChild متن خطا را به div متصل کرده ایم و در نهایت تابع insertBefore را صدا زده ایم. این تابع روی عنصر پدر صدا زده می شود (card) و دو پارامتر می گیرد: پارامتر اول همان عنصری است که قرار است به صفحه اضافه شود و پارامتر دوم عنصری است که پارامتر اول قبل از آن اضافه خواهد شد.

در نهایت از setTimeout استفاده کرده ایم تا پس از 3 ثانیه (3000 میلی ثانیه) پیام خطا حذف شود چرا که از نظر UX خوب نیست که خطا همیشه باقی بماند. setTimeout بعد از 3 ثانیه تابعی به نام clearError را اجرا می کند که باید آن را تعریف کنیم:

// Clear error
function clearError() {
    document.querySelector('.alert').remove();
}

این تابع نیز بسیار ساده است، فقط باید alert خود را از صفحه remove کنیم. اگر به مرورگر برویم و بدون هیچ مقداری فرم را ثبت کنیم چنین خطایی را مشاهده خواهیم کرد:

نمایش خطا و حذف آن پس از 3 ثانیه
نمایش خطا و حذف آن پس از 3 ثانیه

و پس از سه ثانیه حذف خواهد شد. در قسمت بعد کدهایمان را تکمیل خواهیم کرد تا علامت loading را نیز نمایش دهیم.

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

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

roozbeh
26 مرداد 1400
Agha karetoon kheili doroste dametoon garm

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

داریوش مزدیسنا
17 تیر 1399
سلام وقت بخیر امیر آقا کارت خیلی درسته دمت گرم امیدوارم هرجا هستی موفق باشی ده ها آموزش ری اکت و جاوااسکریپت خریدم ولی حتی باهاشون نتونستم از مقدماتی بالاتر برم ولی با آموزش های متنی ری اکت و جاوااسکریپت شما تونستم به سطح پیشرفته و حرفه ایی برسم هرچقدر تشکر کنم کمه و ممنون میشم برای ری اکت هم پروژه های کوچک و اینچنین کاربردی بسازید و حتی هزینه ایی هم براش جداگانه قرار بدید هرچقدر باشه حاضر به پرداختش هستم با قدرت ادامه بده انشالله همیشه موفق باشی.

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