Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Create a CRUD web app using JQuery Mobile & XML

0.00/5 (No votes)
8 Apr 2015CPOL10 min read 21.2K   290  
Demonstrate how you can create a JQuery Mobile app and use XML as a backend - single XML record approach by using xml2json.js and json2xml.js.

Introduction

This article seeks to demonstrate how one can create a CRUD web app using JQuery Mobile and XML single file record as a backend for storing your records. I will assume a very simple library system where one will store book details. These will be Title, Author and ISBN for each book available.

The app itself will list available stored books by reading aand listing each book, have functionality to add a book, update it and also delete it.

The principles applied are are the same as in this article, however due to using XML as a backend, some tweaks have been done on the code to create and also read such files. These include adding javascript files within the app to convert xml to json and also convert json to xml, xml2json.js and json2xml.js respectively.

The xml files used as the backend are just basic files, where each record is stored inside <modelname> attribute.

Some silly assumptions: You are familiar with my article JQuery Mobile & JSON. The structure of the source code is the same as we are also using Ajax calls to PHP files to read, update and delete records from the database, however this case around its not json files but xml files. I'm assuming that you are also familiar with JQuery Mobile HTML definitions for UI creation.

By the way, the xml2json.js is a JQuery plugin that converts your xml to a json object. As its easy to manipulate json objects inside our app I apted for this approach. This was obtained here. This is used when reading each record in my app.

json2xml.js is used when saving my xml records. You will recall from my previous CRUD apps that I set screen contents entered by a user to a json object and save this object to a chosen backend. So this script basically does that, takes my json objects and converts it to xml so that I can save it as such. This script was obtained here.

Download LibraryXML.zip

Background

For the past couple of weeks, I have discussing creating CRUD apps with JQuery Mobile for the various backends. You can view my articles here for more clarity. The approach I will follow here is the same. My applications are just for simple CRUD operations and they just provide basic operations. At the end of this exercise, we want to habe an application that looks like this.

Figure 1: Springboard (the main menu of our JQuery Mobile Library App)

Image 1

Image 2

Figure 2: Book Listing (listview listing of all books recorded in our app)

Image 3

Figure 3: Add Book (screen to add a book to the library)

Image 4

Figure 4: Update/Delete Book (screen to update or delete book details)

Image 5

Figure 5: Book Report (table widget listing all books in our library)

Image 6

Figure 6: Book Report - Excel (an excel report of our Library)

Image 7

I will delve on the CRUDV operations here as you can refer to my previous articles for other sections of this article like, reports, exporting to excel etc.

Using the code

CR - eating Books

The books created and record with the app will be stored on a folder on your server called Book. This is based on the model name we have defined. The title of each book is the primary key for each book and the xml file name will then be a cleaned version of the book title with a .xml extension.

Add Book: HTML Definition:

HTML
<div data-model="Book" id="pgAddBook" data-role="page">
                <div data-position="left" data-display="reveal" data-position-fixed="true" id="pgAddBookPnl" data-role="panel">
                    <ul data-role="listview" id="pgAddBookPnlLV">
                        <li data-icon="plus"><a data-transition="slide" href="#pgAddBook">New</a></li>
                        <li data-icon="eye"><a data-transition="slide" href="#pgRptBook">Report</a></li>
                        <li data-icon="carat-l"><a data-transition="slide" href="#pgBook">Back</a></li>
                    </ul>
                </div>
                
                <header id="pgAddBookHdr" data-role="header" data-position="fixed">
                    <h1>Library</h1>
                    <a data-role="button" id="pgAddBookMenu" href="#pgAddBookPnl" data-icon="bars" class="ui-btn-left">Menu</a>
                </header>
                <div id="pgAddBookCnt" data-role="content">
                    <h3>Add Book</h3><form action="#" method="post" id="pgAddBookForm" name="pgAddBookForm">
                        <div data-role="fieldcontain">
                            <label for="pgAddBookTitle">Title<span style='color:red;'>*</span></label>
                            <input  required title="Enter title here." type="text" name="pgAddBookTitle" id="pgAddBookTitle" placeholder="Enter title here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddBookAuthor">Author<span style='color:red;'>*</span></label>
                            <input  required title="Enter author here." type="text" name="pgAddBookAuthor" id="pgAddBookAuthor" placeholder="Enter author here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                        <div data-role="fieldcontain">
                            <label for="pgAddBookISBN">ISBN<span style='color:red;'>*</span></label>
                            <input  required title="Enter isbn here." type="text" name="pgAddBookISBN" id="pgAddBookISBN" placeholder="Enter isbn here." autocomplete="off" data-clear-btn="true"></input>
                        </div>
                    </form>
                </div>
                
                <footer id="pgAddBookFtr" data-role="footer" data-position="fixed">
                    <div id="pgAddBookFtrNavBar" data-role="navbar">
                        <ul>
                            <li><a id="pgAddBookBack" data-icon="carat-l">Cancel</a>
                            </li>
                            <li><a type="submit" id="pgAddBookSave" data-icon="action">Save</a>
                            </li>
                        </ul>
                    </div>
                </footer></div>

We are just using three input controls for the books. The screen has a cancel button to take a user back to the book listing and a save button.

Add Book: Save button click event

JavaScript
// Save click event on Add page
            $('#pgAddBookSave').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                //get form contents into an object
                var BookRec = pgAddBookGetRec();
                //save object to XML
                app.addBook(BookRec);
            });

The details of the book entered as read and stored as BookRec by pgAddBookGetRec()

JavaScript
//read contents of each form input
        function pgAddBookGetRec() {
            //define the new record
            var BookRec = {};
            BookRec.Title = $('#pgAddBookTitle').val().trim();
            BookRec.Author = $('#pgAddBookAuthor').val().trim();
            BookRec.ISBN = $('#pgAddBookISBN').val().trim();
            return BookRec;
        }

and app.addBook(BookRec); is called, this function being passed the json record of the book.

JavaScript
// add a new record to server storage.
        app.addBook = function (BookRec) {
            // define a record object to store the current details
            var Title = BookRec.Title;
            // cleanse the record key of spaces.
            Title = Title.split(' ').join('-');
            BookRec.Title = Title;
            //convert record to xml to write to server
            var recordXML = json2xml(BookRec, "Book");
            // we are using a single profile approach, then append xml header
            recordXML = "<?xml version='1.0' encoding='utf-8'?>" + recordXML;
            // save the data to a server file, use the post method as it has 8MB minimum data limitation
            var req = Ajax("xmlSaveBook.php", "POST" , recordXML);
            if (req.status == 200) {
                //show a toast message that the record has been saved
                toastr.success('Book record saved.', 'Library');
                //find which page are we coming from, if from sign in go back to it
                var pgFrom = $('#pgAddBook').data('from');
                switch (pgFrom) {
                    case "pgSignIn":
                    $.mobile.changePage('#pgSignIn', {transition: pgtransition});
                    break;
                    default:
                    // clear the edit page form fields
                    pgAddBookClear();
                    //stay in the same page to add more records
                }
                } else {
                //show a toast message that the record has not been saved
                toastr.error('Book record not saved. Please try again.', 'Library');
            }
        };

The book title is cleaned to remove any spaces and these are converted to -. Then the contents of the entered book are converted from this json object to an xml string..

JavaScript
//convert record to xml to write to server
            var recordXML = json2xml(BookRec, "Book");

Then this book xml record is formatted property to have a xml header.

JavaScript
recordXML = "<?xml version='1.0' encoding='utf-8'?>" + recordXML;

Then a POST ajax call is made, thus saving the book record as an xml file to the server Book folder using the book title.

JavaScript
var req = Ajax("xmlSaveBook.php", "POST" , recordXML);

then depending on the success of the call we tell the end user in a toast. Looking at xmlSaveBook.php, this is what happens:

PHP
<?php
    // Get the data from the client.
    $record = file_get_contents('php://input');
    // we want to parse the XML elements
    $xmlrec = new SimpleXMLElement($record);
    // read the primary key
    $Title = $xmlrec->Title;
    //write the data out to a file on the server
    //make sure permissions are all OK!
    //create the parent folder
    if (!is_dir('./Book/')) {
        mkdir('./Book/');
    }
    //define the file
    $xmlFile = "Book/" . $Title . ".xml";
    $f = fopen($xmlFile, 'w') or die("Error: Can't open file. Got write permission?");
    fwrite($f, $record);
    fclose($f);
?>

This gets the xml contents passed to the server and uses SimpleXMLElement to parse it inside the php file. The title of the book is read then if the Book directory in the server does not exist, its created. The xmlFile is then defined to include the whole path and then the contents of the xml record saved to file.

The Ajax function is stored within the remoteserver.js file included and is depicted below:

JavaScript
function Ajax(URL, method, data, callback) {
    if (typeof(method) !== 'object') {
        var settings = new Object;
        if(!method || method === null || typeof(method) === 'undefined') method = "GET";
        settings.type = method.toUpperCase()
        if(!data || data === null || typeof(data) === 'undefined') data = "";
        settings.data = data;
        if (!callback) {
            settings.async = false;
            } else {
            settings.success = callback;
        settings.fail = callback}
    }
    return $.ajax(URL, settings);
}

U - pdating Books

Updating a book takes one selecting a book from available book list and then an edit screen appears. One can then update the book details, with the exception of the book title and clicking Update.

Update Book: HTML Definition

HTML
<div data-url="Title" data-model="Book" id="pgEditBook" data-role="page">
                    <div data-position="left" data-display="reveal" data-position-fixed="true" id="pgEditBookPnl" data-role="panel">
                        <ul data-role="listview" id="pgEditBookPnlLV">
                            <li data-icon="plus"><a data-transition="slide" href="#pgAddBook">New</a></li>
                            <li data-icon="eye"><a data-transition="slide" href="#pgRptBook">Report</a></li>
                            <li data-icon="carat-l"><a data-transition="slide" href="#pgBook">Back</a></li>
                        </ul>
                    </div>
                    
                    <header id="pgEditBookHdr" data-role="header" data-position="fixed">
                        <h1>Library</h1>
                        <a data-role="button" id="pgEditBookMenu" href="#pgEditBookPnl" data-icon="bars" class="ui-btn-left">Menu</a>
                    </header>
                    <div id="pgEditBookCnt" data-role="content">
                        <h3>Edit Book</h3><form action="#" method="post" id="pgEditBookForm" name="pgEditBookForm">
                            <div data-role="fieldcontain">
                                <label for="pgEditBookTitle">Title<span style='color:red;'>*</span></label>
                                <input  required readonly="readonly" data-clear-btn="true" autofocus="true" title="Enter title here." type="text" name="pgEditBookTitle" id="pgEditBookTitle" placeholder="Enter title here." autocomplete="off"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditBookAuthor">Author<span style='color:red;'>*</span></label>
                                <input  required title="Enter author here." type="text" name="pgEditBookAuthor" id="pgEditBookAuthor" placeholder="Enter author here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                            <div data-role="fieldcontain">
                                <label for="pgEditBookISBN">ISBN<span style='color:red;'>*</span></label>
                                <input  required title="Enter isbn here." type="text" name="pgEditBookISBN" id="pgEditBookISBN" placeholder="Enter isbn here." autocomplete="off" data-clear-btn="true"></input>
                            </div>
                        </form>
                    </div>
                    
                    <footer id="pgEditBookFtr" data-role="footer" data-position="fixed">
                        <div id="pgEditBookFtrNavBar" data-role="navbar">
                            <ul>
                                <li><a data-transition="slide" id="pgEditBookBack" href="#pgBook" data-icon="carat-l">Cancel</a>
                                </li>
                                <li><a type="submit" id="pgEditBookUpdate" data-icon="action">Update</a>
                                </li>
                                <li><a id="pgEditBookDelete" data-icon="delete">Delete</a>
                                </li>
                            </ul>
                        </div>
                    </footer></div>

The Update/Delete screen has Cancel, Update, Delete buttons at the footer to ensure that books are either updated or deleted. When a user opts to Update a book, the details are read from this html definition input properties for each book and then saved to a json object that is later saved to the server. Let's look at this.

Update Book: Update button click event

JavaScript
// Update click event on Edit Page
            $('#pgEditBookUpdate').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                //get contents of Edit page controls
                var BookRec = pgEditBookGetRec();
                //save updated records to XML
                app.updateBook(BookRec);
            });

When the Update button is clicked, the details entered on the form are read by pgEditBookGetRec and assigned to BookRec and this is later passed to app.updateBook to save it to the server.

JavaScript
//read contents of each form input
        function pgEditBookGetRec() {
            //define the new record
            var BookRec = {};
            BookRec.Title = $('#pgEditBookTitle').val().trim();
            BookRec.Author = $('#pgEditBookAuthor').val().trim();
            BookRec.ISBN = $('#pgEditBookISBN').val().trim();
            return BookRec;
        }

Our AddPage input controls were pgAddBookTitle and for the update page, these are pgEditBookTitle. We have used two screens for adding and updating to ensure easy maintenance of the code. Then app.updateBook is called passing the BookRec to it to save.

 

JavaScript
// update an existing record and save to server.
        app.updateBook = function (BookRec) {
            // define a record object to store the current details
            var Title = BookRec.Title;
            // cleanse the record key of spaces.
            Title = Title.split(' ').join('-');
            BookRec.Title = Title;
            //convert record to XML to write to server
            var recordXML = json2xml(BookRec, "Book");
            // we are using a single profile approach, then append xml header
            recordXML = "<?xml version='1.0' encoding='utf-8'?>" + recordXML;
            // save the data to a server file, use the post method as it has 8MB minimum data limitation
            var req = Ajax("xmlSaveBook.php", "POST" , recordXML);
            if (req.status == 200) {
                //show a toast message that the record has been saved
                toastr.success('Book record updated.', 'Library');
                // clear the edit page form fields
                pgEditBookClear();
                // show the records listing page.
                $.mobile.changePage('#pgBook', {transition: pgtransition});
                } else {
                //show a toast message that the record has not been saved
                toastr.error('Book record not updated. Please try again.', 'Library');
            }
        };

Just like the methods above, the edit record is converted to xml and a header included and this a POST done to the server using the same xmlSaveBook.php that was used for adding records. Remember, the record has changed however the saving methodology hasnt changed, thus using the same php file. You will note too that within the attached zip file, there are only three php files, to save, to get and to delete.

D - deleting Books

From the book update screen, one can select Delete to delete a book.

Image 8

When a user opts to delete a book, a prompt is availed to prompt the user if they really want to delete the book. Deleting the book from the server is an action that cannot be undone. The book title is read from the update screen and this is passed to a msgbox page.

Update Page: Delete button click event

JavaScript
$('#pgEditBookDelete').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                //read the record key from form control
                var Title = $('#pgEditBookTitle').val().trim();
                //show a confirm message box
                $('#msgboxheader h1').text('Confirm Delete');
                $('#msgboxtitle').text(Title.split('-').join(' '));
                $('#msgboxprompt').text('Are you sure that you want to delete this book? This action cannot be undone.');
                $('#msgboxyes').data('method', 'deleteBook');
                $('#msgboxno').data('method', 'editBook');
                $('#msgboxyes').data('id', Title.split(' ').join('-'));
                $('#msgboxno').data('id', Title.split(' ').join('-'));
                $('#msgboxyes').data('topage', 'pgEditBook');
                $('#msgboxno').data('topage', 'pgEditBook');
                $.mobile.changePage('#msgbox', {transition: 'pop'});
            });

This passed our msgbox attributes to a msgbox screen defined like this: a dialog pop up screen

HTML
<div data-dismissible="false" data-transition="pop" data-model="msgbox" id="msgbox" data-role="dialog">
                        
                        <header id="msgboxHdr" data-role="header" data-position="fixed">
                            <h1>Confirm</h1>
                        </header>
                        <div id="msgboxCnt" data-role="content">
                            <div id="msgboxtitle">
                            </div>
                            <br><div id="msgboxprompt">
                                <p>Are you sure you want to delete this record?</p>
                            </div>
                            <br><div style="text-align: center;" id="msgboxbuttons" class="ui-grid-a">
                                <div class="ui-block-a">
                                    <a data-method="" data-id="" data-topage="" id="msgboxyes" data-role="button" data-icon="check">Yes</a>
                                </div>
                                <div class="ui-block-b">
                                    <a data-method="" data-id="" data-topage="" id="msgboxno" data-role="button" data-icon="delete" data-theme="b">No</a>
                                </div>
                            </div>
                        </div>
                        
                    </div>

My previous articles have discussed this msgbox approach in detail, you can refer to that, however, when the Yes button is clicked, which was defined with

JavaScript
$('#msgboxyes').data('method', 'deleteBook');

The app.deleteBook method is fired, passing the id of the book saved like this:

JavaScript
$('#msgboxyes').data('id', Title.split(' ').join('-'));

MsgBox: Yes button click event

JavaScript
$('#msgboxyes').on('click', function (e) {
                e.preventDefault();
                e.stopImmediatePropagation();
                var yesmethod = $('#msgboxyes').data('method');
                var yesid = $('#msgboxyes').data('id');
                app[yesmethod](yesid);
            });

yes button reads the passed data attribute of the method and the id of the record to delete and then calls

JavaScript
app[yesmethod](yesid);

to delete the record. Let's look at the app.deleteBook function.

JavaScript
//delete a record from XML using record key
        app.deleteBook = function (Title) {
            Title = Title.split(' ').join('-');
            var req = Ajax("xmlDeleteBook.php/?Title=" + Title);
            if (req.status == 200) {
                toastr.success('Book record deleted.', 'Library');
                } else {
                toastr.error('Book record not deleted.', 'Library');
            }
            // show the page to display after a record is deleted, this case listing page
            $.mobile.changePage('#pgBook', {transition: pgtransition});
        };

This cleans the book title and calls xmlDeleteBook.php, passing  it the title of the book. If you notice, the extension of the book record is not done here. When the book is deleted, the user is told by a toast.

xmlDeleteBook.php's insides:

PHP
<?php
    //delete the xml record file from the server
    $Title = $_GET['Title'];
    unlink('./Book/'.$Title.'.xml');
?>

This gets the passed book title, then deletes the file with the complete path using the unlink php method. That concludes our XML CRUD operations, let's look at getting and listing all the books in the listview.

V - iewing Books

Getting all available books and getting a single book from the server uses xmlGetBook.php. This looks like this:

PHP
<?php
    //get the file contents from the server
    If (isset($_REQUEST['file'])) {
        $file = basename($_REQUEST['file']);
        echo file_get_contents('./Book/'.$file);
        } Else {
        If (is_dir('./Book') && $handle = opendir('./Book/')) {
            While (False !== ($entry = readdir($handle))) {
                If (!is_dir($entry)) {
                    echo basename($entry)."\n";
                }
            }
            closedir($handle);
            } Else {
            header("HTTP/1.0 404 Not Found");
        }
    }
?>

When an Ajax() call is made without a file arguement, all the files in the folder are returned delimited by a CRLF, i.e. \n. These are then extracted and listed on the listview on runtime. When the file name is specified, the individual file is read. Let's look at this in detail.

Book Listing: HTML Definition

HTML
<div data-model="Book" id="pgBook" data-role="page">
                <div data-position="left" data-display="reveal" data-position-fixed="true" id="pgBookPnl" data-role="panel">
                    <ul data-role="listview" id="pgBookPnlLV">
                        <li data-icon="plus"><a data-transition="slide" href="#pgAddBook">New</a></li>
                        <li data-icon="eye"><a data-transition="slide" href="#pgRptBook">Report</a></li>
                        <li data-icon="carat-l"><a data-transition="slide" href="#pgMenu">Back</a></li>
                    </ul>
                </div>
                
                <header id="pgBookHdr" data-role="header" data-position="fixed">
                    <h1>Library</h1>
                    <a data-role="button" id="pgBookMenu" href="#pgBookPnl" data-icon="bars" data-transition="slide" class="ui-btn-left">Menu</a>
                    <a data-role="button" id="pgBookNew" data-icon="plus" data-theme="b" class="ui-btn-right">New</a>
                </header>
                <div id="pgBookCnt" data-role="content">
                    <h3>Books</h3><ul data-role="listview" data-inset="true" id="pgBookList" data-filter="true" data-filter-placeholder="Search Books">
                        <li data-role="list-divider">Your Books</li>
                        <li id="noBook">You have no books</li>
                        
                    </ul>
                </div>
                
            </div>

This above defines the listview page that lists all available books in the library. The name of the listview is pgBookList. This listview is updated at runtime and is thus clickable to ensure that when each book is selected by the user and update screen will be opened by the app. Before the book listing page is shown, the web apps runs some operations.

JavaScript
app.BookBindings = function () {
            // code to run before showing the page that lists the records.
            //run before the page is shown
            $(document).on('pagebeforechange', function (e, data) {
                //get page to go to
                var toPage = data.toPage[0].id;
                switch (toPage) {
                    case 'pgBook':
                    $('#pgRptBookBack').data('from', 'pgBook');
                    // restart the storage check
                    app.checkForBookStorage();
                    break;

In this case, app.checkforBookStorage. This is defined like this.

JavaScript
//display records if they exist or tell user no records exist.
        app.checkForBookStorage = function () {
            //get records from XML.
            var BookObj = app.getBook();
            // are there existing Book records?
            if (!$.isEmptyObject(BookObj)) {
                // yes there are. pass them off to be displayed
                app.displayBook(BookObj);
                } else {
                // nope, just show the placeholder
                $('#pgBookList').html(BookHdr + noBook).listview('refresh');
            }
        };

This method calls a method that is used a lot within the app called app.getBook(). Whatever is executed by that method, it gets stored in variable BookObj. This BookObj is then checked if its empty or not, if empty, the listview will show no records, however, if there are records, method app.displayBook will be called passing the BookObj.

JavaScript
app.getBook = function () {
            // get Book records
            var BookObj = {};
            var icnt, itot;
            //get the list of files under directory
            var req = Ajax("xmlGetBook.php");
            if (req.status == 200) {
                var recFiles = req.responseText;
                recFiles = recFiles.split('\n');
                itot = recFiles.length - 1;
                for (icnt = 0; icnt <= itot; icnt++) {
                    var recFile = recFiles[icnt];
                    if (recFile.length > 0) {
                        // read the file contents and display them
                        var req = Ajax("xmlGetBook.php?file=" + encodeURIComponent(recFile));
                        if (req.status == 200) {
                            // convert to json
                            var BookRec = $.xml2json(req.responseText);
                            var Title = BookRec.Title;
                            Title = Title.split('-').join(' ');
                            BookRec.Title = Title
                            BookObj[Title] = BookRec;
                        }
                    }
                }
                //sort the objects
                var keys = Object.keys(BookObj);
                keys.sort();
                var sortedObject = Object();
                var i;
                for (i in keys) {
                    key = keys[i];
                    sortedObject[key] = BookObj[key];
                }
                BookObj = sortedObject;
                return BookObj;
            }
        };

As noted above, app.getBook calls xmlGetBook.php first without passing the file attribute. This returns all available files in the Book folder of the server for this app. This is returned as responseText. This is then parsed with the CRLF characters specified and then another ajax call is made to read each file, now passing the file name.

First method:

JavaScript
var req = Ajax("xmlGetBook.php");

Second method:

JavaScript
var req = Ajax("xmlGetBook.php?file=" + encodeURIComponent(recFile));

Then for each book record found, the BookObj is updated and then this is sorted by book title and returned to be what is eventually passed to app.displayBook.

JavaScript
BookObj[Title] = BookRec; (updates of the BookObj)

return BookObj; (return of the BookObj to be passed for display)

app.displayBook, receives all the books read from the server and displays them to the listview like this:

JavaScript
//display records in listview during runtime.
        app.displayBook = function (BookObj) {
            // create an empty string to contain html
            var html = '';
            // make sure your iterators are properly scoped
            var n;
            // loop over records and create a new list item for each
            //append the html to store the listitems.
            for (n in BookObj) {
                //get the record details
                var BookRec = BookObj[n];
                // clean the primary key
                var pkey = BookRec.Title;
                pkey = pkey.split('-').join(' ');
                BookRec.Title = pkey;
                //define a new line from what we have defined
                var nItem = BookLi;
                nItem = nItem.replace(/Z2/g,n);
                //update the title to display, this might be multi fields
                var nTitle = '';
                //assign cleaned title
                nTitle = n.split('-').join(' ');
                //replace the title;
                nItem = nItem.replace(/Z1/g,nTitle);
                //there is a description, update the list item
                var nDescription = '';
                nDescription += BookRec.Author;
                nDescription += ', ';
                nDescription += BookRec.ISBN;
                //replace the description;
                nItem = nItem.replace(/DESCRIPTION/g,nDescription);
                html += nItem;
            }
            //update the listview with the newly defined html structure.
            $('#pgBookList').html(BookHdr + html).listview('refresh');
        };

As you noted in the listing screen, we want to show the Author and ISBN in the description of each book. For each book read, these properties are then read and then the listview item updated and a single update to the listview done. This helps in speeding the DOM manipulation.

Book Table Report: HTML Definition

The book table report is initially blank and fed with the book list at runtime. This is defined like this.

HTML
<div data-model="Book" id="pgRptBook" data-role="page">
                
                <header id="pgRptBookHdr" data-role="header" data-position="fixed">
                    <h1>Library</h1>
                    <a data-role="button" id="pgRptBookBack" data-icon="carat-l" class="ui-btn-left">Back</a>
                    <a data-role="button" id="pgRptBookNew" href="#pgAddBook" data-icon="plus" data-theme="b" data-transition="slide" class="ui-btn-right">New</a>
                </header>
                <div id="pgRptBookCnt" data-role="content">
                    <table id="RptBook" data-column-btn-text="Columns To Display" data-column-btn-theme="b" data-role="table" data-mode="columntoggle" data-column-popup-theme="a" class="ui-responsive table-stroke table-stripe ui-shadow">
                        <caption>Books Report</caption>
                        <thead>
                            <tr class="ui-bar-a">
                                <th class="ui-bar-a">Title</th>
                                <th data-priority="2" class="ui-bar-a">Author</th>
                                <th data-priority="3" class="ui-bar-a">ISBN</th>
                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                        <tfoot>
                            <tr><td></td></tr>
                        <tr><td colspan="3">Powered by JQM.Show - https://play.google.com/store/apps/details?id=com.b4a.JQMShow</td></tr></tfoot>
                    </table>
                    <div data-role="fieldcontain">
                    <a download="Book.xls" onclick="return ExcellentExport.excel(this, 'RptBook', 'Book');" id="pgRptBookExport" data-corners="true" href="#" class="ui-btn ui-corner-all ui-shadow">Export to Excel</a></div>
                </div>
                
            </div>

The table is also generated during runtime by executing the app.getBook method discussed above and then looping through each book and generating a table row as depicted below. This is then fed to the table as defined in the html above.

JavaScript
//display records in table during runtime.
        app.BookRpt = function () {
            //clear the table and leave the header
            $('#RptBook tbody tr').remove();
            // get Book records.
            var BookObj = app.getBook();
            // create an empty string to contain all rows of the table
            var newrows = '';
            // make sure your iterators are properly scoped
            var n;
            // loop over records and create a new row for each
            // and append the newrows with each table row.
            for (n in BookObj) {
                //get the record details
                var BookRec = BookObj[n];
                //clean primary keys
                n = n.split('-').join(' ');
                //create each row
                var eachrow = '<tr>';
                eachrow += '<td class="ui-body-c">' + n + '</td>';
                eachrow += '<td class="ui-body-c">' + BookRec.Author + '</td>';
                eachrow += '<td class="ui-body-c">' + BookRec.ISBN + '</td>';
                eachrow += '</tr>';
                //append each row to the newrows variable;
                newrows += eachrow;
            }
            // update the table
            $('#RptBook').append(newrows);
            // refresh the table with new details
            $('#RptBook').table('refresh');
        };

Also here to speed DOM manipulation, the table is updated once after all the rows have been generated by appending eachrow to newrows.

That concludes our crucial points about this article. Enjoy.

Points of Interest

A previous article discussed how we can create a JQuery Mobile web apps using JSON single records as a backend. This seeked to follow the same approach but use XML as back end records. The manipulation of the xml files with the existence of the two libraries, xml2json and json2xml, it has made it both possible and easy for making this possible and I pass credits to the teams who worked on those two scripts.

The SimpleXMLElement PHP class also proved helpful in manipulation XML inside the php file to get the book title. This has helped my exposure to the power of PHP.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)