</> Code Review

The application populates two sections using the following functions:

  • buildPaymentSchedule
  • calculatePayment
  • getPayments
  • displayStats
  • displayData
Click the section links below to learn more about the code.

Build Payment Schedule

This function is the trigger point of the application.

Lines 3 - 5 Return the required form data.

Line 7 Returns the mortgage payment using the provided form data.

Line 8 Returns the calculated array of payments based on the variables provided.

Lines 9 - 10 Call the functions which will display the data statistics and schedule.

// Build Payment Schedule
function buildPaymentSchedule() {
  let loan = +document.getElementById('loanAmount').value;
  let months = +document.getElementById('loanTerm').value * 12;
  let mthRate = (+document.getElementById('loanRate').value * 0.01) / 12;

  let payment = calculatePayment(loan, months, mthRate);
  let paymentArray = getPayments(loan, payment, months, mthRate);
  displayStats(payment, loan, paymentArray);
  displayData(paymentArray);
  return false;
}

Calculate Payment

Standard mortgage payment calculation sourced from Oreilly.com

// Calculate Payment
function calculatePayment(loan, months, mthRate) {
  let x = Math.pow(1 + mthRate, months);
  return (loan * x * mthRate) / (x - 1);

  // Source: https://www.oreilly.com/library/view/javascript-the-definitive/0596000480/ch01s08.html
}

Get Payments

The objective of function is to return the amortization data as an array.

Lines 9 - 25 A for loop is used to iteratively build the array by calculating a data object for each month.

Lines 12 - 15 Calculates those variables which change with each iteration.

Lines 17 - 22 Populate an object with the required fields. This object is then pushed to the array.

// Get Payments (build payment array)
function getPayments(loan, payment, months, mthRate) {
  let paymentArray = [];
  let totalInterest = 0;
  let balance = loan;
  let interest = 0;
  let principal = 0;

  for (let i = 1; i <= months; i++) {
    let obj = {};

    interest = balance * mthRate;
    principal = payment - interest;
    totalInterest += interest;
    balance -= principal;

    obj['month'] = i;
    obj['payment'] = payment;
    obj['principal'] = principal;
    obj['interest'] = interest;
    obj['totalInterest'] = totalInterest;
    obj['balance'] = Math.abs(balance); // Fix negative value

    paymentArray.push(obj);
  }                          

Display Stats

This function provides the view of the final array summary statistics. Note that key information for some fields has already been provided by the function arguments.

Lines 4 - 5 Calculate the total interest using the reduce function and total cost. All of the remaining fields have been provided in the function call.

// Set Stats
function displayStats(payment, loan, paymentArray) {
  // Total Interest
  let totalInterest = paymentArray.reduce((acc, cv) => acc + cv.interest, 0);
  let totalCost = totalInterest + loan;

  document.getElementById('payment').innerHTML = payment.toLocaleString(
    'en-US',
    { style: 'currency', currency: 'USD' }
  );
  document.getElementById('totalPrincipal').innerHTML = loan.toLocaleString(
    'en-US',
    {
      style: 'currency',
      currency: 'USD',
    }
  );
  document.getElementById(
    'totalInterest'
  ).innerHTML = totalInterest.toLocaleString('en-US', {
    style: 'currency',
    currency: 'USD',
  });
  document.getElementById('totalCost').innerHTML = totalCost.toLocaleString(
    'en-US',
    {
      style: 'currency',
      currency: 'USD',
    }
  );
}
                          

Display Data

Templates are used to pull the data and dynamically populate the HTML. This technique is particularly powerful as it keeps the HTML out of the Javascript.

Lines 3 and 4 Identifies the template and the code destination.

Line 10 In the for loop a copy of the template is populated with data then appended to the target destination.

// Display event data
function displayData(paymentArray) {
  const myTemplate = document.getElementById('Data-Template');
  const resultsBody = document.getElementById('resultsBody');

  // clear table first
  resultsBody.innerHTML = '';

  // Number format reference https://www.w3schools.com/jsref/jsref_tolocalestring_number.asp
  for (let i = 0; i < paymentArray.length; i++) {
    const dataRow = document.importNode(myTemplate.content, true);

    dataRow.getElementById('month').textContent = paymentArray[i].month;
    dataRow.getElementById('payment').textContent = paymentArray[
      i
    ].payment.toLocaleString('en-US', {
      style: 'decimal',
      minimumFractionDigits: '2',
      maximumFractionDigits: '2',
    });
    dataRow.getElementById('principal').textContent = paymentArray[
      i
    ].principal.toLocaleString('en-US', {
      style: 'decimal',
      minimumFractionDigits: '2',
      maximumFractionDigits: '2',
    });
    dataRow.getElementById('interest').textContent = paymentArray[
      i
    ].interest.toLocaleString('en-US', {
      style: 'decimal',
      minimumFractionDigits: '2',
      maximumFractionDigits: '2',
    });
    dataRow.getElementById('totalInterest').textContent = paymentArray[
      i
    ].totalInterest.toLocaleString('en-US', {
      style: 'decimal',
      minimumFractionDigits: '2',
      maximumFractionDigits: '2',
    });
    dataRow.getElementById('balance').textContent = paymentArray[
      i
    ].balance.toLocaleString('en-US', {
      style: 'decimal',
      minimumFractionDigits: '2',
      maximumFractionDigits: '2',
    });

    resultsBody.appendChild(dataRow);
  }
}