Saturday, 11 June 2016

Web Tutorial: The Scrooge Expense Tracker Mobile App (Part 2/4)

Now, we'll be making the Settings page of the app. This will retrieve from, and write to, the "scrooge_settings" file in localStorage. Make this change to your index.html file. This adds a row to your content. Within that, there's a label and a textbox. There will be four expenditure categories in the app, generically named "A", "B", "C" and "D". These will be editable by the users.
        <div data-role="page" id="settings" data-theme="b">
            <div data-role="header" data-position="fixed">
                SETTINGS
            </div>

            <div data-role="main" class="ui-content">               
                <div data-role="fieldcontainer">
                    <label for="txtA">Category A: </label>
                    <input id="txtA_name" name="txtA" placeholder="Enter title...">
                </div>
            </div>

            <div data-role="footer" data-position="fixed">
                <a href="#selectdate" data-icon="info">Select Month</a>
                <a href="#entries" data-icon="grid">Entries</a>
                <a href="#settings" data-icon="gear">Settings</a>
            </div>
        </div>


This is what you should see. You'll need to click on the Settings button to view this.


Now add input for Categories B, C and D.
        <div data-role="page" id="settings" data-theme="b">
            <div data-role="header" data-position="fixed">
                SETTINGS
            </div>

            <div data-role="main" class="ui-content">               
                <div data-role="fieldcontainer">
                    <label for="txtA">Category A: </label>
                    <input id="txtA_name" name="txtA" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtB">Category B: </label>
                    <input id="txtB_name" name="txtB" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtC">Category C: </label>
                    <input id="txtC_name" name="txtC" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtD">Category D: </label>
                    <input id="txtD_name" name="txtD" placeholder="Enter title...">
                </div>
            </div>

            <div data-role="footer" data-position="fixed">
                <a href="#selectdate" data-icon="info">Select Month</a>
                <a href="#entries" data-icon="grid">Entries</a>
                <a href="#settings" data-icon="gear">Settings</a>
            </div>
        </div>


Looking good so far!


Next, we allow the user to specify the monthly budget and intervals for subtotals.
        <div data-role="page" id="settings" data-theme="b">
            <div data-role="header" data-position="fixed">
                SETTINGS
            </div>

            <div data-role="main" class="ui-content">               
                <div data-role="fieldcontainer">
                   <label for="txtA">Category A: </label>
                   <input id="txtA_name" name="txtA" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                   <label for="txtB">Category B: </label>
                    <input id="txtB_name" name="txtB" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtC">Category C: </label>
                    <input id="txtC_name" name="txtC" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtD">Category D: </label>
                    <input id="txtD_name" name="txtD" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                   <label for="txtD">Monthly Budget: </label>
                   <input id="txtBudget" name="txtD" placeholder="Enter amount...">
                </div>
               <div data-role="fieldcontainer">
                   <label for="txtD">Interval (days): </label>
                    <input id="txtInterval" name="txtD" placeholder="Enter days...">
                </div>
            </div>

            <div data-role="footer" data-position="fixed">
                <a href="#selectdate" data-icon="info">Select Month</a>
                <a href="#entries" data-icon="grid">Entries</a>
                <a href="#settings" data-icon="gear">Settings</a>
            </div>
        </div>


And this is what you should have...

Now add the Edit button for the final touch to the layout.
        <div data-role="page" id="settings" data-theme="b">
            <div data-role="header" data-position="fixed">
                SETTINGS
            </div>

            <div data-role="main" class="ui-content">               
                <div data-role="fieldcontainer">
                    <label for="txtA">Category A: </label>
                    <input id="txtA_name" name="txtA" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtB">Category B: </label>
                    <input id="txtB_name" name="txtB" placeholder="Enter title...">
                </div>
               <div data-role="fieldcontainer">
                    <label for="txtC">Category C: </label>
                    <input id="txtC_name" name="txtC" placeholder="Enter title...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtD">Category D: </label>
                    <input id="txtD_name" name="txtD" placeholder="Enter title...">
                </div>
               <div data-role="fieldcontainer">
                   <label for="txtD">Monthly Budget: </label>
                   <input id="txtBudget" name="txtD" placeholder="Enter amount...">
                </div>
                <div data-role="fieldcontainer">
                    <label for="txtD">Interval (days): </label>
                    <input id="txtInterval" name="txtD" placeholder="Enter days...">
                </div>
                <a href="#settings" data-role="button" data-icon="edit">Edit</a>
            </div>

            <div data-role="footer" data-position="fixed">
                <a href="#selectdate" data-icon="info">Select Month</a>
                <a href="#entries" data-icon="grid">Entries</a>
                <a href="#settings" data-icon="gear">Settings</a>
            </div>
        </div>


Yep, we're getting warmer. The layout is done, and what's left is functionality.


Add this to your JavaScript. This specifies that before displaying the Settings page, data will be taken from the global object glb_settings (which in turn was populated by data from "scrooge_settings", remember?) and the various textboxes populated by that data.
        $(document).ready(function()
        {
            if (localStorage.getItem("scrooge_settings") == null)
            {
                localStorage.setItem("scrooge_settings","[{\"category\":\"A\",\"name\":\"A\"},{\"category\":\"B\",\"name\":\"B\"},{\"category\":\"C\",\"name\":\"C\"},{\"category\":\"D\",\"name\":\"D\"},{\"budget\":\"1000.00\"},{\"interval\":\"5\"}]");
            }

            glb_settings = JSON.parse(localStorage.getItem("scrooge_settings"));
        });

        $(document).on("pagebeforeshow", "#settings", function(event, data)
        {
            $("#txtA_name").val(glb_settings[0].name);
            $("#txtB_name").val(glb_settings[1].name);
            $("#txtC_name").val(glb_settings[2].name);
            $("#txtD_name").val(glb_settings[3].name);
            $("#txtBudget").val(glb_settings[4].budget);
            $("#txtInterval").val(glb_settings[5].interval);
        });


Refresh. This is the result.


Of course, you should be able to edit this data, so modify your Edit button as follows.
    <a href="#settings" data-role="button" data-icon="edit" onclick="update_settings();">Edit</a>


And add in the update_settings() function to your JavaScript. This changes the values in the global variable glb_settings.
        $(document).ready(function()
        {
            if (localStorage.getItem("scrooge_settings") == null)
            {
                localStorage.setItem("scrooge_settings","[{\"category\":\"A\",\"name\":\"A\"},{\"category\":\"B\",\"name\":\"B\"},{\"category\":\"C\",\"name\":\"C\"},{\"category\":\"D\",\"name\":\"D\"},{\"budget\":\"1000.00\"},{\"interval\":\"5\"}]");
            }

            glb_settings = JSON.parse(localStorage.getItem("scrooge_settings"));
        });

        $(document).on("pagebeforeshow", "#settings", function(event, data)
        {
            $("#txtA_name").val(glb_settings[0].name);
            $("#txtB_name").val(glb_settings[1].name);
            $("#txtC_name").val(glb_settings[2].name);
            $("#txtD_name").val(glb_settings[3].name);
            $("#txtBudget").val(glb_settings[4].budget);
            $("#txtInterval").val(glb_settings[5].interval);
        });

        function update_settings()
        {
            glb_settings[0].name=$("#txtA_name").val();
            glb_settings[1].name=$("#txtB_name").val();
            glb_settings[2].name=$("#txtC_name").val();
            glb_settings[3].name=$("#txtD_name").val();
            glb_settings[4].budget=$("#txtBudget").val();
            glb_settings[5].interval=$("#txtInterval").val();
        }


And then we save the glb_settings object to "scrooge_settings" in localStorage after converting it to a JSON string. After that, we perform a simple alert() function to inform the user that it's done.
        function update_settings()
        {
            glb_settings[0].name=$("#txtA_name").val();
            glb_settings[1].name=$("#txtB_name").val();
            glb_settings[2].name=$("#txtC_name").val();
            glb_settings[3].name=$("#txtD_name").val();
            glb_settings[4].budget=$("#txtBudget").val();
            glb_settings[5].interval=$("#txtInterval").val();

            localStorage.setItem("scrooge_settings", JSON.stringify(glb_settings));

            alert ("Settings saved.");
        }


Try it. Are you able to save? Look at what the values of "scrooge_settings" in localStorage are, after updating. For this example, I've changed the titles of the Categories. They should no longer say "A", "B", "C" and "D".


But hold on...

What happens if you enter a non-numeric value into the budget and interval fields? Now, we simply can't allow that because a lot hinges on these values being numeric. So what we do is add this function to the text fields.
            <div data-role="fieldcontainer">
                <label for="txtD">Monthly Budget: </label>
                <input id="txtBudget" name="txtD" placeholder="Enter amount..." onchange="fixinput(this,2);">
                </div>
            <div data-role="fieldcontainer">
                <label for="txtD">Interval (days): </label>
                <input id="txtInterval" name="txtD" placeholder="Enter days..." onchange="fixinput(this,0);">
                </div>
                <a href="#settings" data-role="button" data-icon="edit" onclick="update_settings();">Edit</a>
        </div>


And add the fixinput() function to your JavaScript. This passes the object from which the function is triggered, as a parameter, along with an integer vardec that specifies the number of decimal places the output should be in. From there, we check the textbox's value, and ensure that if the value is not a number or blank, the default is 0, with vardec decimal places. Otherwise, the number is forced to vardec decimal places. In the above example, the Budget is a monetary value, therefor the output should be in 2 decimal places. The Interval is an integer representing a number of days, therefore it is a whole number and there will be 0 decimal places.
        function fixinput(varobj,vardec)
        {
            if (isNaN(varobj.value)||varobj.value.trim()=="")
            {
                varobj.value=(0).toFixed(vardec);
            }
            else
            {
                varobj.value=parseFloat(varobj.value).toFixed(vardec);
            }
        }

        function update_settings()
        {
            glb_settings[0].name=$("#txtA_name").val();
            glb_settings[1].name=$("#txtB_name").val();
            glb_settings[2].name=$("#txtC_name").val();
            glb_settings[3].name=$("#txtD_name").val();
            glb_settings[4].budget=$("#txtBudget").val();
            glb_settings[5].interval=$("#txtInterval").val();

            localStorage.setItem("scrooge_settings", JSON.stringify(glb_settings));

            alert ("Settings saved.");
        }


Test this again. Are you able to save a non-numeric value in budget and interval? Does the value automatically get converted?

What about negative numbers?

Well, I figure this being something of an accounting app (very loosely speaking), the user should be able to enter negative numbers if he feels like it. You may alter the fixinput() function if you disagree.

Next

You've seen how a simple page with simple updating functions is created. We'll expand that knowledge to create the first page of this app.

No comments:

Post a Comment