-

- -

Monday, January 12, 2015

jQuery Mobile with JSON and Local Storage 2

---
jQuery Mobile with JavaScript Object, JSON and Local Storage

5) Create LocalStorage Object

5.1) If you uncomment the the following code (highlighted in green colour), you will get an error during emulation..
5.2) Create another JavaScript file local-storage.js
Enter the following codes
var LocalStorageDB = function(successCallback, errorCallback) {
}
5.3) Add the reference to the HEAD section of index.html
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>jQuery Mobile To Do</title>
    <link rel="stylesheet"  href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">
    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
<script src="./js/local-storage.js"></script>
<script src="./js/app.js"></script>
</head>
Info:
“<script src="./js/local-storage.js"></script>” must be entered before “<script src="./js/app.js"></script>” so that LocalStorageDB would be recognised by the codes in app.js

6) Insert Call Delay error capture code

Call Delay error capture sets the time-out limit during which operation must be completed or else terminated.
var LocalStorageDB = function(successCallback, errorCallback) {
        // Used to simulate async calls. This is done to provide a consistent interface with storage methods like WebSQL and serverside ajax calls
    var callDelay = function(callback, data) {
        if (callback) {
            setTimeout(function() {
                callback(data);
            }, 100);
        }
    }
/* other codes will be placed here */
    callDelay(successCallback);
}

7) Insert tasks codes to app.js and uncomment call to tasks

(add these codes on top of the function initialize in app.js file)
findAll: function() {...} ,
findById: function () {...} ,
markCompleted: function(id) {...} ,
markOutstanding: function(id) {...} ,
insert: function(json) {...} ,
update: function(json) {...} ,
delete: function(json) {...} ,
Don’t forget to put a comma between the function definitions
    findAll: function() {
        console.log('DEBUG - 2. findAll() triggered');
        this.store.findAll(function(todos) {
            var l = todos.length;
            var td;
            // Create new arrays so we can order them with outstanding first
            outstanding = [];
            completed = [];
            allTodos = [];
            // Loop through todos, build up lis and push to arrays
            for (var i=0; i<l; i++) {
                td = todos[i];
                // If not completed
                if (td.status == 0) {
                    outstanding.push('<li data-row-id="' + td.id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + td.id +'">Mark as completed</a></li>');
                }
                // If is completed
                else {
                    completed.push('<li data-row-id="' + td.id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + td.id +'">Mark as outstanding</a></li>');
                }
            }
            // Join both arrays
            allTodos = outstanding.concat(completed);
            // Remove any previously appended
            $('.todo-listview li').remove();
            // Append built up arrays to ULs here.
            $('.todo-listview').append(allTodos);            
            // Refresh JQM listview
            $('.todo-listview').listview('refresh');
        });
    },
    findById: function(id) {
       
        this.store.findById(id, function(result) {
           
            $('#title').val(result.title);
            $('#title').attr('data-id', id);
            $('#description').val(result.description);
            $('#id').val(id);
       
        });
    },
    markCompleted: function(id) {
        // Passing json as any store will be able to handle it (even if we change to localStorage etc)
        this.store.markCompleted(id, function(result) {
            // DB updates successful
            if(result) {
                console.log("DEBUG - Success, db updated and marked as completed");
                // Find original row and grab details
                var originalRow =  $('#home *[data-row-id="'+id+'"]'),
                    title = originalRow.find("h2").text(),
                    desc = originalRow.find("p").text();
                // Remove from pending row
                originalRow.remove();
                // Re-build the li rather than clone as jqm generates a lot of fluff
                var newRow = '<li data-row-id="' + id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + id +'">Mark as outstanding</a></li>';
                // Add to completed
                $('.todo-listview').append(newRow);
                // Refresh dom
                $('.todo-listview').listview('refresh');
                // Kept for debugging use
                //console.log("id length = " + $('[data-row-id='+id+']').length);
            } else {
                alert("Error - db did not update and NOT marked as completed");
            }
        });
    },
    markOutstanding: function(id) {
        // Passing json as any store will be able to handle it (even if we change to localStorage, indexedDB etc)
        this.store.markOutstanding(id, function(result) {
            // DB updates successful
            if(result) {
                console.log("DEBUG - Success, db updated and marked as outstanding");
                // Find original row and grab details
                var originalRow =  $('*[data-row-id="'+id+'"]'),
                    title = originalRow.find("h2").text(),
                    desc = originalRow.find("p").text();
                // Remove from pending row
                originalRow.remove();
                // Re-build the li rather than clone as jqm generates a lot of fluff
                var newRow = '<li data-row-id="' + id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + id +'">Mark as completed</a></li>';
                // Add to completed
                $('.todo-listview').prepend(newRow);
                // Refresh dom
                $('.todo-listview').listview('refresh');
                // Kept for debugging use
                //console.log("id length = " + $('[data-row-id='+id+']').length);
            } else {
                alert("Error - db did not update and NOT marked as outstanding");
            }
        });
    },
    insert: function(json) {
        // Passing json as any store will be able to handle it (even if we change to localStorage etc)
        this.store.insert(json, function(result) {
            // On successful db insert
            if(result) {
                console.log("DEBUG - Success,  add returned true");
                // Redirect back to #home page, add a transition andchange the hash
                $.mobile.changePage( $("#home"), {
                    transition: "slide",
                    reverse: true,
                    changeHash: true,
                });
            } else {
                alert("Error on insert!");
            }
        });
    },
    update: function(json) {
        // Passing json as any store will be able to handle it (even if we change to localStorage etc)
        this.store.update(json, function(result) {
            // On succuessful db update
            if(result) {
                console.log("DEBUG - Success, updated returned true");
            } else {
                alert("Error on update!");
            }
        });
    },
    delete: function(json) {
        // Passing json as any store will be able to handle it (even if we change to localStorage etc)
        this.store.delete(json, function(result) {
            // On successful db delete
            if(result) {
                console.log("DEBUG - Success, delete returned true");
                // Redirect back to #home page
                $.mobile.changePage( $("#home"), {
                    transition: "slide",
                    reverse: true,
                    changeHash: true
                });
            } else {
                alert("Error on delete!");
            }
        });
    },

8) Emulate

You will get error because your store object has yet to contain the required methods

9) Edit Local-Storage.js

9.1) Find the comment codes “/* other codes will be placed here */
9.2) Replace with the following codes
The green highlighted codes show the method binding for the store object
this.findAll
this.findById
this.markCompleted
this.markOutstanding
this.insert
this.update
this.delete
    // Allows us to sort an array of object - Use array.sort(sortByProperty('firstName'));
    var sortByProperty = function(property) {
        'use strict';
        return function (a, b) {
            var sortStatus = 0;
            if (a[property] < b[property]) {
                sortStatus = -1;
            } else if (a[property] > b[property]) {
                sortStatus = 1;
            }
     
            return sortStatus;
        };
    }

        // Sample Data (An array of objects)
    var todos = [
        {"id": 1, "title": "Go to the shop", "description": "Get milk and bread", "status": 0},
        {"id": 2, "title": "Post office", "description": "Collect mail", "status": 0},
        {"id": 3, "title": "Email Dad", "description": "About birthday", "status": 0},
        {"id": 4, "title": "Haircut", "description": "Well overdue", "status": 1}
    ];
    // Add the sample data to localStorage
    window.localStorage.setItem("todos", JSON.stringify(todos));
    this.findAll = function(callback) {
            // Parse a string as json
            var todos = JSON.parse(window.localStorage.getItem("todos"));
        callDelay(callback, todos);
    }
    this.findById = function(id, callback) {
        var todos = JSON.parse(window.localStorage.getItem("todos")),
                todo = null,
                len = todos.length,
            i = 0;
       
        for (; i < len; i++) {
            if (todos[i].id === id) {
                todo = todos[i];
                break;
            }
        }
        callDelay(callback, todo);
    }
    this.markCompleted = function(id, callback) {
        // Get all todos
        var todos = JSON.parse(window.localStorage.getItem("todos")),
            todo = null,
            len = todos.length,
            i = 0;
       
        // Loop through them and update the value
        $.each(todos, function(i, v) {
            if ( v.id === id ) {
                v.status = 1;
                return false;
            }
        });
        // Save the JSON back to localStorage
        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {
            callDelay(callback, "true");
        } else {
            callDelay(callback, "false");
        }
    }
    this.markOutstanding = function(id, callback) {
        // Get all todos
        var todos = JSON.parse(window.localStorage.getItem("todos")),
            todo = null,
            len = todos.length,
            i = 0;
       
        // Loop through them and update the value
        $.each(todos, function(i, v) {
            if ( v.id === id ) {
                v.status = 0;
                return false;
            }
        });
        // Save the JSON back to localStorage
        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {
            callDelay(callback, "true");
        } else {
            callDelay(callback, "false");
        }
    }
    this.insert = function(json, callback) {
        // Converts a JavaScript Object Notation (JSON) string into an object.
        var passedJson = JSON.parse(json),
            status = 0;
       
        // Get all todos
        var todos = JSON.parse(window.localStorage.getItem("todos")),
            todo = null,
            len = todos.length,
            i = 0;
       
        // Sort the json by ID (default is ASC)
        todos.sort(sortByProperty('id'));
        // Generate a new ID, pop the last obj in the array and grab the ID
        var lastTodo = todos.pop(),
            newID = lastTodo.id + 1;
        // Create the new Todo
        var newTodo = {"id": newID, "title": passedJson.title, "description": passedJson.description, "status": 0}
        // Add it to the existing todos        
        todos.push(lastTodo); // Add the popped one back in
        todos.push(newTodo);
        // Save the JSON back to localStorage
        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {
            callDelay(callback, "true");
        } else {
            callDelay(callback, "false");
        }
    }
    this.update = function(json, callback) {
        // Converts a JavaScript Object Notation (JSON) string into an object.
        var passedJson = JSON.parse(json);
        // Get all todos
        var todos = JSON.parse(window.localStorage.getItem("todos")),
            todo = null,
            len = todos.length,
            i = 0;
       
        // Loop through them and update the value
        $.each(todos, function(i, v) {
            if ( v.id == passedJson.id ) {                
                v.title = passedJson.title;
                v.description = passedJson.description;
                return false;
            }
        });
        // Save the JSON back to localStorage
        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {
            callDelay(callback, "true");
        } else {
            callDelay(callback, "false");
        }
    }
    this.delete = function(json, callback) {
        // Converts a JavaScript Object Notation (JSON) string into an object.
        var passedJson = JSON.parse(json);
        // Get all todos
        var todos = JSON.parse(window.localStorage.getItem("todos")),
            todo = null,
            len = todos.length,
            i = 0;
        // Loop through existing todos and remove one to be deleted
        for(var i=0; i<todos.length; i++){
            if(todos[i].id == passedJson.id){
                todos.splice(i, 1);  //removes 1 element at position i
                break;
            }
        }
        // Save the JSON back to localStorage
        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {
            callDelay(callback, "true");
        } else {
            callDelay(callback, "false");
        }
    }
9.3) Emulate again.

10) Form Action Processing

10.1) There are two Form Actions; Add and Delete.
10.2) At the moment, if you click on the buttons, you will get error message. The method to convert form data into JSON object is missing.
10.3) Add a method “serializeObject” into app.js as shown below
// jQuery plugin - Encode a set of form elements as a JSON object for manipulation/submission.
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};
10.4) Test in emulator.
Click the test item shown above.
Click the Delete button.
Download Source Files for this part:
---

No comments:

Post a Comment