Jonnie Grieve Digital Media: Blog

Home
by on 27th October, 2023 - 11:19am (0)

Blog: Create a Cashflow Forecaster #4 (More Posts)

In this series, I am creating another app that mimics something I made on an Excel Spreadsheet. It’s an application that takes a starting figure and calculates changes in the forecast based on inputs and outputs on any given row of data.

You can follow the work I’ve done in my previous blogs in this series below.

We’re now going to focus on finishing the data persistence by allowing users to enter their own comments in the main table cells, so they can fully customise their own forecasts – that is to say, if a mortgage payment is due they can write that in the comments column for that given row.

Let’s prepare the table element for this.

<table class="cashflow_table">

    <tbody>

        <tr>

            <!--<th>October</th>-->
            <th class="budget_in">In (£)</th>
            <th class="budget_out">Out (£)</th>
            <th class="forecast_date">Date</th>
            <th class="forecast_balance">Balance (£)</th>
            <th class="comments">Comments</th>
            <th class="estimated">Estimated(?) </th>
        </tr>
        <tr>
            <!-- Other TD cells above - class removed from tabel cell -->
            <td class="toggle_estimated"></td>
        </tr>
<tbody>
</table>

The first “Comment” Table Cell has the class .yes_estimated. I’ll take this off now as we now have the ability to toggle all of these cells as accomplished in the last blog.

I’d also previously added a default value with the value attribute, in order to test browser behaviour for smaller screens.

<input type="text" class="forecast_comment" id="" placeholder="Edit comment for each transaction...." title="Enter your comments for each transaction...." value="Rent and housing costs to be paid. This text will overflow. Will overflow." maxlength="80" />

We can remove this now, and leave it as an empty value. Although I have kept it as an HTML comment above the input field.

<!-- Max Char: 80 Example Text: Rent and housing costs to be paid. This text will overflow. Will overflow.-->
<input type="text" class="forecast_comment" id="comment-cell-1" placeholder="Edit comment for each transaction...." title="Enter your comments for each transaction...." value="" maxlength="80" />

In anticipation that we’d need to use unique IDs for the comment cells as we did for the balance column, I added new IDs to each of the cells.

#comment-cell-1, #comment-cell-2.. etc

Handling Data persistence

Okay. We can now get on to data persistence in the Comments Column.

We’re going to be using a couple of functions to do this.

  • listenForCommentsChangesAndSaveToLocalStorage()
  • loadInitialCommentValuesFromLocalStorage()

Yes, those names are a little lenghty but they’re descriptive. We need functions to set unique localStorage keys that store string values and then load those strings.

function listenForCommentsChangesAndSaveToLocalStorage() {
    const commentsInputs = document.querySelectorAll('.forecast_comment');

    commentsInputs.forEach((input, index) => {
        input.addEventListener('input', function () {
        localStorage.setItem(`commentValue${index}`, input.value);
    });
});

// Call this function to set initial values from localStorage
loadInitialCommentValuesFromLocalStorage();
}

Now, each of the table cells for the comment column also has a class of .forecast_comment which is selected dynamically and stored in a constant variable called commentsInputs. Then we’re using foreach() to iterate through these elements and save the last value of the inputs to localStorage.

To save to localStorage we use JavaScript wizardry to give each setting a unique value according to the table row it is on. The key is in the format commentValue${index}, and the value is the content of the input field.

It seems to be extensible so long as you keep the unique IDs consistent in their format.

Finally, we make sure we call the load function to make sure the saved values are loaded when the page loads.

Load the saved comments

This function populates the comment input fields with their previously saved values from localStorage, ensuring that the user’s comments are restored when they revisit the page.

function loadInitialCommentValuesFromLocalStorage() {

    const commentsInputs = document.querySelectorAll('.forecast_comment');

    commentsInputs.forEach((input, index) => {

        input.value = localStorage.getItem(`commentValue${index}`) || '';
    });
}

What we are doing is loading the values we’ve just saved back to the screen.

As before, we use a DOM selector to get all HTML elements with the class name “forecast_comment” and store them in the commentsInputs  variable using

document.querySelectorAll('.forecast_comment').

We again use the forEach iteration method, and for each input field, we find the date inputs, retrieves the values from localStorage, and sets them as the input values

It retrieves a value from localStorage using a key format that includes the index of the input field (e.g., commentValue${index}).

It assigns the retrieved value (or an empty string if no value is found in localStorage) to the value property of the input field. This effectively populates the input fields with their previous content that was saved in localStorage.

Moving onto the “Date” column

This leaves just one more data set to handle. We still need to ensure dates are correctly persisted when they’re selecte.  There’s one date input field in each cell.  As you can see below, the code to set unique localStorage keys is practically the same.  We take the specific input element, assign it an index based on the row number, and use that to get our unique localStorage key.

dateValue0
dateValue1
dateValue2
dateValue3

These are the functions we use.

// Select date inputs
const dateInputs = document.querySelectorAll('.balance_date');

// Function to save date values to localStorage
function saveDateValuesToLocalStorage() {
    dateInputs.forEach((input, index) => {
        input.addEventListener('input', function () {
            localStorage.setItem(`dateValue${index}`, input.value);
        });
    });
}

// Call this function to set initial date values from localStorage
saveDateValuesToLocalStorage();

 

// Function to load initial date values from localStorage
function loadInitialDateValuesFromLocalStorage() {
    dateInputs.forEach((input, index) => {
        input.value = localStorage.getItem(`dateValue${index}`) || '';
    });
}

// Call this function to load initial date values from localStorage when the page loads
loadInitialDateValuesFromLocalStorage();

In the same way that we stored values of the comment text before we also do also with the date selections.  This does of course mean there’s scope for code refactoring but we can deal with that at a later time.

A final summary for this blog of where we’re at.

We’ve reached the end of this phase of the project.

  • We have the Cashflow correctly formatting data and adapting the calculations according to the starting balance of any given row.
  • It is correctly adapting the forecasting when the “In” and “Out” number fields are interacted with.
  • We have the data fully persisting using localStorage so users do not have to start forecasting data again when they leave or refresh the page.
  • We have given the user the ability to indicate whether a particular forecast on a given table row is an estimated forecast.
  • And we now text in the comments Column saved directly to localStorage which handles data persistence there for us.

In the next blog, we’ll work on calculating total incoming and outgoing amounts for any given Cashflow Forecast and give the user the ability to clear all data and start again.

This post has been assigned to the following categories

    Leave a Reply

    Your email address will not be published. Required fields are marked *