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