PhoneGap Tutorial: A Cross-Platform Zombie App

PhoneGap is a mobile development framework that allows developers to build applications for a variety of mobile platforms, using familiar web technologies such as HTML, CSS, and JavaScript. By Dani Arnaout.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Structuring Your App

When your app first launches it displays index.html, so in this section you’ll edit that to give your app some bones to build upon.

Replace all of the existing template code in index.html with the following:

<!DOCTYPE html>
<html>
    <head>
        <title>Zombie Apocalypse To-Do List</title>
        <link rel="stylesheet" type="text/css" href="css/index.css"/>
        <script type="text/javascript" language="JavaScript">
            
        </script>
    </head>
    <body>
        
    </body>
</html>

What you see above is the main structure of any HTML code that leverages JavaScript; it contains html, head, body, and script tags. On a standard web page, JavaScript is usually found between the head tags, while the HTML lives between the body tags.

Now you’ll need to structure the app as designed in the mockup above. Recall that there were two main buttons at the top, followed by a table.

Add the following code to index.html between the body tags:

<input type="button" value="Add To-Do"/>
<input type="button" value="Remove Completed Tasks"/>
<br/><br/>
<table id="dataTable" width="100%" border="1">

</table>

Build and run your app! If all goes according to plan, it should look like this:

With a few simple lines of code, you now have some functional buttons displaying on the screen.

Now you need to add some logic behind the buttons to really bring your app to life!

Bring on the Javscript: An Overview

In order to support your app’s functionality, you’ll need to implement a number of JavaScript functions.

Before you start implementing them, let’s start with an overview of the functions you’ll need to create for this app.

createNewToDo()

Prompts the user to enter a new to-do item. Once the user presses the OK button on the resulting dialog, a new row is added to the table.

addTableRow(todoDictionary, appIsLoading)

Adds a row to the to-do list table. It accepts several arguments: a dictionary containing both the text of the to-do along with the checkbox state, as well as with a boolean value which indicates if the app is loading or not. This will be explained a little later.

checkBoxClicked()

Called whenever the state of a checkbox is changed. It loops through all checkboxes and either applies or removes a strike-through style to the text of the corresponding to-do.

viewSelectedRow(todoTextField)

Displays the value of the passed-in text field in a popup dialog.

deleteSelectedRow(deleteButton)

Deletes the row that corresponds to the row of the button that was passed into the function.

removeCompletedTasks()

Loops through all the table rows and removes the rows where the to-do item is marked as complete.

saveToDoList()

Saves the to-do list using the Local Storage API.

loadToDoList()

Loads the to-do list when the app is first launched using the Local Storage API.

deleteAllRows()

Removes all rows from the table.

And that’s it. You can refer back to this section if you have any questions about how each part fits in as you continue working through this PhoneGap tutorial.

Bring on the Javascript: Implementation

Now that the basic structure of the functions has been covered, it’s time to implement them in your app.

Note: Each code block that you encounter below, unless indicated, should be placed between the script tags in index.html.

Note: Each code block that you encounter below, unless indicated, should be placed between the script tags in index.html.

Add the code below:

// create a new to-do
function createNewToDo()
{
    var todoDictionary = {};
    
    // prompt the user to enter to-do
    var todo = prompt("To-Do","");
    if (todo != null)
    {
        if (todo == "")
        {
            alert("To-Do can't be empty!");
        }
        else
        {
            // append the new to-do with the table
            todoDictionary = { check : 0 , text : todo};
            addTableRow(todoDictionary, false);
        }
    }
    
}

createNewToDo() is relatively straightforward; it simply prompts the user to enter a new to-do item.

If the text of the to-do is empty, this function alerts the user, otherwise it calls addTableRow(), passing a dictionary containing the text of the to-do along with the initial checkbox state. In the case of a new to-do item, the checkbox state will be set as false.

Add the following code:

// add a row to the table
var rowID = 0;
function addTableRow(todoDictionary, appIsLoading)
{
    rowID +=1;
    var table = document.getElementById("dataTable");
    var rowCount = table.rows.length;
    var row = table.insertRow(rowCount);
    
    // create the checkbox
    var cell1 = row.insertCell(0);
    var element1 = document.createElement("input");
    element1.type = "checkbox";
    element1.name = "chkbox[]";
    element1.checked = todoDictionary["check"];
    element1.setAttribute("onclick", "checkboxClicked()");
    cell1.appendChild(element1);
    
    // create the textbox
    var cell2 = row.insertCell(1);
    var element2 = document.createElement("input");
    element2.type = "text";
    element2.name = "txtbox[]";
    element2.size = 16;
    element2.id = "text" + rowID;
    element2.value = todoDictionary["text"];
    element2.setAttribute("onchange", "saveToDoList()");
    cell2.appendChild(element2);
    
    // create the view button
    var cell3 = row.insertCell(2);
    var element3 = document.createElement("input");
    element3.type = "button";
    element3.id = rowID;
    element3.value = "View";
    element3.setAttribute("onclick", "viewSelectedRow(document.getElementById('text' + this.id))");
    cell3.appendChild(element3);
    
    // create the delete button
    var cell4 = row.insertCell(3);
    var element4 = document.createElement("input");
    element4.type = "button";
    element4.value = "Delete";
    element4.setAttribute("onclick", "deleteSelectedRow(this)");
    cell4.appendChild(element4);
    
    // update the UI and save the to-do list
    checkboxClicked();
    saveToDoList();
    
    if (!appIsLoading) alert("Task Added Successfully.");
}

When addTableRow() is called it adds a new row to the table with four separate cells. Then it adds an HTML element to each cell, namely a checkbox, a text box, a View button, and a Delete button. This also sets the necessary attributes on these four elements.

Finally, it calls checkboxClicked() and saveToDoList(), which update the UI and save the to-do list respectively.

addTableRow() is called not only when the user adds a new to-do item, but it’s also called when the app is first launched and the to-do list is being loaded. Hence, that’s why you pass the appIsLoading boolean value to this function.

If you didn’t check this condition, then the user would be alerted for every to-do that’s loaded on startup — which would get annoying REALLY fast! :]

Add the following code:

// add the strike-through styling to completed tasks
function checkboxClicked()
{
    var table = document.getElementById("dataTable");
    var rowCount = table.rows.length;
    
    // loop through all rows of the table
    for(var i = 0; i < rowCount; i++)
    {
        var row = table.rows[i];
        var chkbox = row.cells[0].childNodes[0];
        var textbox = row.cells[1].childNodes[0];
        
        // if the checkbox is checked, add the strike-through styling
        if(null != chkbox && true == chkbox.checked)
        {
            if(null != textbox)
            {		
                textbox.style.setProperty("text-decoration", "line-through");
            }
        }
        
        // if the checkbox isn't checked, remove the strike-through styling
        else
        {
            textbox.style.setProperty("text-decoration", "none");
        }
        
    }
    
    // save the to-do list
    saveToDoList();
}

checkboxClicked() performs only a single task. It loops through the all the rows of the table, determines the state of each checkbox, and applies the strike-through styling to the to-do text if the checkbox is set. Once complete, checkboxClicked() then calls saveToDoList().

Add the following code:

// view the content of the selected row
function viewSelectedRow(todoTextField)
{
    alert(todoTextField.value);
}

There's not much to viewSelectedRow(); it's just a simple function that displays an alert containing the full text of the to-do item.

Add the following code:

// delete the selected row
function deleteSelectedRow(deleteButton)
{
    var p = deleteButton.parentNode.parentNode;
    p.parentNode.removeChild(p);
    saveToDoList();
}

deleteSelectedRow() is another relatively simple function. It takes the button reference that was passed in and deletes the corresponding to-do item from the list.

Add the following code:

// remove completed tasks
function removeCompletedTasks()
{
    var table = document.getElementById("dataTable");
    var rowCount = table.rows.length;
    
    // loop through all rows of the table
    for(var i = 0; i < rowCount; i++)
    {
        // if the checkbox is checked, delete the row
        var row = table.rows[i];
        var chkbox = row.cells[0].childNodes[0];
        if(null != chkbox && true == chkbox.checked)
        {
            table.deleteRow(i);
            rowCount--;
            i--;
        }
    }
    
    // save the to-do list
    saveToDoList();
    
    alert("Completed Tasks Were Removed Successfully.");
}

removeCompletedTasks() loops through all table rows and determines the state of each row's checkbox. If the checkbox is checked, then the table row is removed. Then the updated to-do list is saved and the user is alerted that the action is complete.

Add the following code:

// save the to-do list
function saveToDoList()
{
    var todoArray = {};
    var checkBoxState = 0;
    var textValue = "";
    
    var table = document.getElementById("dataTable");
    var rowCount = table.rows.length;
    
    if (rowCount != 0)
    {
        // loop through all rows of the table
        for(var i=0; i<rowCount; i++)
        {
            var row = table.rows[i];
            
            // determine the state of the checkbox
            var chkbox = row.cells[0].childNodes[0];
            if(null != chkbox && true == chkbox.checked)
            {
                checkBoxState = 1;
            }
            else
            {
                checkBoxState= 0;
            }
            
            // retrieve the content of the to-do
            var textbox = row.cells[1].childNodes[0];
            textValue = textbox.value;
            
            // populate the array
            todoArray["row" + i] =
            {
                check : checkBoxState,
                text : textValue
            };
        }
    }
    else
    {
        todoArray = null;
    }
    
    // use the local storage API to persist the data as JSON
    window.localStorage.setItem("todoList", JSON.stringify(todoArray));
}

saveToDoList() does what it says on the tin. It loops through all table rows and populates a dictionary for each row with the checkbox state and to-do text. This collection of dictionaries are then added to an array, which is then saved using the local storage API.

Now that you have your to-do list saved, you'll need a way to load it back up, won't you?

Add the following code:

// load the to-do list
function loadToDoList()
{
    // use the local storage API load the JSON formatted to-do list, and decode it
    var theList = JSON.parse(window.localStorage.getItem("todoList"));
    
    if (null == theList || theList == "null")
    {
        deleteAllRows();
    }
    else
    {
        var count = 0;
        for (var obj in theList)
        {
            count++;
        }
        
        // remove any existing rows from the table
        deleteAllRows();
        
        // loop through the to-dos
        for(var i = 0; i < count; i++)
        {
            // adding a row to the table for each one
            addTableRow(theList["row" + i], true);
        }
    }
}

loadToDoList() provides a mechanism to load up the to-do list that you've persisted with saveToDoList().

The Local Storage API is leveraged to the load the JSON formatted to-do list and then decode it. Any existing rows are removed from the current table, then a new row is added for each to-do item. As before, you pass true to addTableRow() to prevent the user from being alerted as each row is loaded at startup.

Add the following code:

// delete all the rows
function deleteAllRows()
{
    var table = document.getElementById("dataTable");
    var rowCount = table.rows.length;
    
    // loop through all rows of the table
    for(var i = 0; i < rowCount; i++)
    {
        // delete the row
        table.deleteRow(i);
        rowCount--;
        i--;
    }
    
    // save the to-do list
    saveToDoList();
}

deleteAllRows() simply removes all rows from the table; it's used to clean up the UI in an efficent fashion.

Okay, that's it for the JavaScript functions! Now you need to update the HTML in your app to make use of these functions.

Dani Arnaout

Contributors

Dani Arnaout

Author

Over 300 content creators. Join our team.