Click here to Skip to main content
15,867,453 members
Articles / Web Development / HTML

Create a CRUD web app using JQuery Mobile and LocalStorage

Rate me:
Please Sign up or sign in to vote.
4.98/5 (20 votes)
25 Feb 2015CPOL21 min read 45.2K   1.5K   29   11
Create a web app to create, read, update and delete records using JQuery Mobile and Localstorage

Introduction

Download notekeeper.zip

This article seeks to demonstrate how can one create a web application that can run from desktops, mobile devices by using the JQuery Mobile Framework and HTML5. The purpose of the application is to store an retrieve ones' Notes. This seeks to show how can one apply a simple methodology to store and retrieve records from LocalStorage. This example application will have 4 screens at the end, these being:

  1. The Springboard - this to provides a selection to access the notes
  2. Notes - a list of your notes in a listview and functionality to add a new note and go back to the sprintboard
  3. Add Note - a screen to add a new note and save it
  4. Edit Note - a screen to update and delete your note

NB: I am assuming you know a something about JQuery Mobile, LocalStorage, JavaScripta and HTML5, however I have created links above of where you can get part of the information from the web about them.

Background

I have been wanting to develop CRUD applications using a mobile framework for a while. I wanted something that I could easily apply and through Google I found JQuery Mobile. JQuery Mobile is a framework to develop mobile application using HTML5, the latest version of HTML. You can get a lot of information from the web in terms of this frameworks, however going to the source site of JQuery Mobile has been very enlightning for me. One of the most useful site for me as been W3School, where one can get a lot of information about the different frameworks available for web development.

Whilst this web application, which I will call NoteKeeper, can be easily be compiled to a Hybrid, using PhoneGap Build, that step will not be part of this article.

Most books I read about JQuery Mobile were more about designing the user interface and not the API to manipulate the user interface like reading and saving records. It was through reading JQuery related books that I found out how to be able to perform something as simple as storing and retrieving records to the localstorage. I wanted to demonstrate how this can be done for the different controls available and how one can also update them during runtime.

This will show how to bind and execute click events of buttons and anchors, use of data- attributes of elements, updating listviews during run time, define message box during runtime and execute code based on choices made by end user, use jquery to get and set element attributes, use of objects and use of json to mention a few.

Using the code

The first step in developing NoteKeeper will be to define the user interface, i.e. the screens that the end users will see. As mentioned above, we have four screens to list the notes, add a new note, edit an existing note and a springboard. You will get more content from going through the attached source code as I will touch on the main functionality here and what it will do. I will also explain the code behind the scences to handle your events when buttons like save are clicked etc.

This web app follows a Single Page Application framework where all the pages are designed within a single html page and defined with ids to reference them. From now on I will quickly explain the html definition per each of the pages and the resulting output of that definition. When looking at the html definition, please link it to the output screens generated thereof. As indicated, I am assuming some knowledge of JQuery Mobile, HTML5 and Javascript to be able to fully comprehend this article.

The Springboard - html definition and page output, this will be the first screen you see when the web app starts.

HTML
<div id="pgMenu" data-role="page" data-theme="b" class="my-page">
<header id="pgMenuheader" data-role="header" data-position="fixed"> 
<h1>NoteKeeper
</h1> 
</header> 
<div id="pgMenucontent" data-role="content" class="content"> 
<ul data-role="listview" data-inset="true" id="sbItems"> 
<li data-icon="false">
<a data-transition="slide" id="sbNote" href="#pgNote">
<h2>Notes
</h2>
<p>Maintain Notes
</p> 
<img height="200" width="100%" src="apps80.png" alt="Notes" class="ui-li-thumb">
</img> 
</a>
</li> 
</ul> 
</div> 
<footer id="pgMenufooter" data-role="footer" data-position="fixed"> 
<h1>Powered by JQM.Show © Anele Mbanga 2015
</h1> 
</footer> 
</div>

The springboard page has a header with a title NoteKeeper. The content of the page is a listview with a single item called Notes. This has a description to Maintain Notes. The resulting design of this html above is depicted in Figure 1 below. After selecting the Notes, a user will be taken to the Notes Listing where available notes will be listed.

Figure 1

What this screen achieves - this is a link between the notes available and the end user. In cases other pages might be added to this web app, this springboard can be used to provide links to other sections of the web app. For example, if this web app had functionality to store user details, a list item can be added to the existing items to access the users listing.

Notes Listing - html definition and page output

The notes listing screen provides one with the list of notes that have been saved into the web app's local storage. all the available lists are read from LocalStorage and then the listview updated just before this screen appears. I will later explain how that has been done in JavaScript, for now lets see the Notes Listing Definition.

HTML
<div id="pgNote" data-role="page">
<header id="pgNoteheader" data-role="header" data-position="fixed">
<h1>Notes</h1>
<a data-role="button" id="pgNoteBack" data-icon="arrow-l" class="ui-btn-left">Back</a>
<a data-role="button" id="pgNoteNew" data-icon="plus" data-theme="b" class="ui-btn-right">New</a>
</header>
<div id="pgNotecontent" data-role="content" class="content">
<ul data-role="listview" data-inset="true" id="pgNoteList" data-autodividers="true" data-filter="true" data-filter-placeholder="Search Notes" data-filter-reveal="false">
<li data-role="list-divider">NoteHdr</li>
<li id="noNote">You have no notes</li>
</ul>
</div>
</div>

The Notes listing screen has a back button, which when pressed allows a user to go back to the Springboard, This is defined with pgNoteBack. Also available here is another button on the header to add a new note, defined by pgNoteNew. I could have defined what these buttons do when clicked within their element definition however I wanted to handle that through javascript code, I will demonstrate this later on though. The disadvantage with handling these simple click events in code is bloating my app with javascript, because they only perform simple functions like moving from one page to another. If however, one needs to perform other functions when a button event is fired, it is best to call a function when that button is clicked.

The end result of this Notes Listing definition is depicted in Figure 2 below. When the web app first starts, it will show there are No Notes up until a user enters some notes and saves them. Each note deleted will also be removed from LocalStorage and the remaining notes listed too.

Figure 2

When an element in the list is selected, a Note Update screen gets shown as will be depicted for one to update the notes details. Some settings of some elements gets done as will be explained in the javascripts.

From the above listing, the listview will have automatic data dividers, meaning that the notes will be grouped by alphabet and these are searchable due to the existence of the data-filter="true" attribute within the listview definition.

What this screen achieves - this lists all available notes that have been stored in LocalStorage. When a user selects a note, they are prompted with it to be able to update it and save it too. This also provides a link to add a new Note.

New Note - html definition and page output

The following screen seeks to explain how the new note screen is designed and the resulting output.

HTML
<div id="pgAddNote" data-role="page">
<header id="pgAddNoteheader" data-role="header" data-position="fixed">
<h1>Add Note</h1>
<a data-role="button" id="pgAddNoteBack" data-icon="arrow-l" class="ui-btn-left">Back</a>
</header>
<div id="pgAddNotecontent" data-role="content" class="content">
<form action="#" method="post" id="pgAddNoteForm" name="pgAddNoteForm">
<div data-role="fieldcontain">
<label for="pgAddNoteTitle" id="lblpgAddNoteTitle">Title<span style='color:red;'>*</span></label>
<input type="text" required="required" title="Enter title here." name="pgAddNoteTitle" id="pgAddNoteTitle" placeholder="Enter title here." autocomplete="off" data-clear-btn="true">
</input>
</div>
<div data-role="fieldcontain">
<label for="pgAddNoteDetail" id="lblpgAddNoteDetail">Details<span style='color:red;'>*</span></label>
<textarea name="pgAddNoteDetail" id="pgAddNoteDetail" placeholder="Enter details here." data-clear-btn="true" required="required"></textarea>
</div>
<div><button type="submit" id="pgAddNoteSave" class="ui-btn ui-corner-all ui-shadow ui-btn-b">Save Note</button>
</div>
</form>
</div>
</div>

The Add Note screen as depicted in Figure 3 below has the above definition. When a user select the New button from the Notes listing page, a new screen prompting the user to create a new note will show. To arrange the form contents nicely accross mobile deviced, the elements are hosted within a field container as depicted by data-role="fieldcontain". All the required fields of the Note are depicted by a red star as per style definition of each element. For each note, the user should type in the Title and Details and click Save Note.

Figure 3

What this screen achieves - this provides functionality for the end user to add a new note and save it to localstorage and also go back to the Notes Listing. The title will show a delete icon to enable quick clearing of the title contents. This uses the data-clear-btn="true" attribute of the text field.

Edit Note - html definition and page output

The edit note screen is the same as the Add Note screen, however it does not add a new note but updates an existing note. When a user selects to update an existing note, due to the title of the note being unique, the title textbox to prompt the user gets set to be read-only. This screen also has a functionality to delete a note on it on the header.

HTML
<div data-url="Title" id="pgEditNote" data-role="page">
<header id="pgEditNoteheader" data-role="header" data-position="fixed">
<h1>Edit Note</h1>
<a data-role="button" id="pgEditNoteBack" data-icon="arrow-l" class="ui-btn-left">Back</a>
<a data-role="button" data-href="ID" id="pgEditNoteDelete" data-icon="delete" data-theme="b" class="ui-btn-right">Delete</a>
</header>
<div id="pgEditNotecontent" data-role="content" class="content">
<form action="#" method="post" id="pgEditNoteForm" name="pgEditNoteForm">
<div data-role="fieldcontain">
<label for="pgEditNoteTitle" id="lblpgEditNoteTitle">Title<span style='color:red;'>*</span></label>
<input readonly="readonly" data-clear-btn="true" autofocus="true" type="text" required="required" title="Enter title here." name="pgEditNoteTitle" id="pgEditNoteTitle" placeholder="Enter title here." autocomplete="off"></input>
</div>
<div data-role="fieldcontain">
<label for="pgEditNoteDetail" id="lblpgEditNoteDetail">Details<span style='color:red;'>*</span></label>
<textarea name="pgEditNoteDetail" id="pgEditNoteDetail" placeholder="Enter details here." data-clear-btn="true" required="required"></textarea>
</div>
<div><button type="submit" id="pgEditNoteUpdate" class="ui-btn ui-corner-all ui-shadow ui-btn-b">Update Note</button>
</div>
</form>
</div>
</div>

The resulting user interface screen for the above definition is depicted in Figure 4 below.

Figure 4

What this screen achieves - this provides functionality for the end user to edit an existing note and also delete it with a prompt.

One other thing I should mention, when a user selects to delete a note, they should be prompted if they want to delete the note and if there is some information that is not correct in the web app, the user needs to be told. To do this, I have defined two pages, one being a MessageBox and another an Alert.

MessageBox - html definition and page output

Using a page element to prompt users has been a little bit tricky for me and I'm still searching for other ways to provide them. I did'nt want to use a plugin for them either but wanted the same functionality the JQuery Mobile provided. There are a few settings that needed to be done to make this work.

HTML
<section data-transition="pop" id="msgbox" data-role="dialog">
<header id="msgboxheader" data-role="header" data-position="fixed">
<h1>Confirm</h1>
</header>
<div id="msgboxcontent" data-role="content" class="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>
</section>

Messageboxes in nature are dynamic depending on what is being fed to them. This being the title, the prompt and the buttons they show. For this example, the above definition has two buttons, a yes and a no. The message box header will change including the message being prompted to the user. The two depicted buttons are sitting inside a grid element. I'm also using data- elements like data-id, data-topage and data-method to pass variables of what the app should do when a yes or a no is clicked depending on these data- attributes.

For example, the data-method will indicate witch data method to execute, the data-id, which note id to process and data-topage, which page to go to after everything is executed.

The buttons have been called msgboxyes and msgboxno because we also have an alert box definition.

Figure 5 below is the output of this messagebox definition when the web app is running.

Figure 5

What this screen achieves - this provides functionality for the end user to prompt them if they want to delete a note or not.

Alert - html definition and page output

HTML
<section data-transition="pop" id="alertbox" data-role="dialog">
<header id="alertboxheader" data-role="header" data-position="fixed">
<h1>Error</h1>
</header>
<div id="alertboxcontent" data-role="content" class="content">
<div id="alertboxtitle">
</div>
<br><div id="alertboxprompt">
<p>An error has been encountered!</p>
</div>
<br><div style="text-align: center;" id="alertboxbuttons" class="ui-grid-solo">
<div class="ui-block-a">
<a data-method="" data-id="" data-topage="" id="alertboxok" data-role="button" data-icon="check" data-theme="b">Ok</a>
</div>
</div>
</div>
</section>

The alert box as runs when the user has entered an incorrect username and password combination for example when they Sign In to the NoteKeeper app. For the purposes of this exercise, this just depicts how one can define and Alert box with an Ok button. The same data elements as explained above with the message box applies. I will explain how to configure the message box prompts below, however please note that the same principles can be applied to this alert box to prompt the user and perform actions later on.

We have completed the definition of the web app html and now we will delve into the glue that makes this work. This will be the javascript and the local storage access.

NoteKeeper - Javascript (this is explained in the sequence of the code in the file)

JavaScript
$(function(){
// define the application
var NoteKeeper = {};
(function(app){
// variable definitions go here
var NoteLi = '<li ><a href="#pgEditNote?Title=LINK">ID</a></li>';
var NoteHdr = '<li data-role="list-divider">NoteHdr</li>';
var noNote = '<li id="noNote">You have no notes</li>';

We are defining a NoteKeeper application with a namespace as defined with var NoteKeeper = {}.

The NoteLi defines each item of the listview within the Notes Listing page. When each note is selected its suppose to open the pgEditNote page to edit the page using the Title of each note. This variable will be used to generate each list item when the notes are read from local storage.

The NoteHdr variable is the header to show on the listview, this has been defined as a divider that will show a header.

noNote - is a variable that defines the text to show when there are no notes.

Next we define bindings for all the events that should fire when the web app is being run.

JavaScript
app.init = function(){
FastClick.attach(document.body);
app.Notesbindings();
app.checkForNotesStorage();

When the app initialized, with FastClick, we ensure that the click delay is not experienced when a button is clicked by assigning the FastClick javascripts to the document of the application. More details about FastClick can be obtained here. There is a 300ms delay between a physcical tap and a click event and this seeks to eliminate that.

Secondly, we bind all notes related events like the listview clicks, back button clicks, new button clicks for new notes, save button clicks etc. Binding the events tells the system that these events are meant to fire and does not necessary fire them. We are just letting the web app be aware of what is expected to happen with an element, e.g. for button save, a click event will happen and what should happen when that click happens.

Thirdly, we want the system to run checkForNotesStorage. This basically says, open up local storage, check if notes are existing, if they exist, load them. I will explain the binding functions later.

Then we define the linkages of the message boxes.

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

When a msgboxyes is clicked, i.e. a yes button on the messagebox, prevent the default operations when links are clicked and dont bubble up events on the page, e.preventDefault and e.stopImmediatePropagation respectively.

JavaScript
var yesmethod = $('#msgboxyes').data('method');

Get the name of the method to execute that has been assigned in the data-method attribute of the msgboxyes element.

JavaScript
var yesid = $('#msgboxyes').data('id');

Get the id of the element to process from the data-id attribute of the msgboxyes element and finally.

JavaScript
app[yesmethod](yesid);

Execute the associated method of the app, passing it the title to process.

We also bind the springboard click event here

JavaScript
$(document).on('click', '#sbItems a', function(e){
e.preventDefault();
e.stopImmediatePropagation();
var href = $(this).attr('href');
$.mobile.changePage(href, {transition: 'slide'});
});

Each springboard item is a list element with a href attribute. Here we basically get the details of the page to get to from the href attribute and slide to that page using jquery's changePage method. This just shows one how they can manipulate listview items click events. The listview name is sbItems and each item is an anchor item in the list.

app.Notesbindings 

The user has selected a note from available notes in the listview

JavaScript
app.Notesbindings = function(){
$(document).on('click', '#pgNoteList a', function(e){
e.preventDefault();
e.stopImmediatePropagation();
var href = $(this)[0].href.match(/\?.*$/)[0];
var Title = href.replace(/^\?Title=/,'');
$.mobile.changePage('#pgEditNote', {transition: 'slide'});
app.editNote(Title);
});

From here, when a note is selected from the notes listing screen, 1. get the href of the selected item, 2. change to the pgEditNote screen, ie. where a user can update the note and 3. execute the app.EditNote(Title) method. The method on 3 basically reads the note from local storage and displays it on the screen by updating the textbox and text area of the Edit Screen.

We want to show existing notes before the Notes Listing page is shown, thus we add this function

Before the Notes Listing page is shown, update the listview with available notes

JavaScript
$(document).on('pagebeforechange', function(e, data){
var toPage = data.toPage[0].id;
if(toPage == 'pgNote'){
// restart the storage check
app.checkForNotesStorage();
}
});

We compare the page that will show after to the page that hosts the notes, this being pgNote, if it will be that we checkForNotesStorage.

On the Notes Listing, when a user clicks the back button

JavaScript
$('#pgNoteBack').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
// move to the add new record screen
$.mobile.changePage('#pgMenu', {transition: 'slide'});
});

When a user presses the back button on the Notes Listing, the web app should show the springboard. As indicated above, this just bloats the code here and could have been easily achieved by adding the following attributes, href='#pgMenu' data-transition='slide' on the button definition of the Back button in the notes listing header, however in case you might want to perform other actions when the back button is clicked besides going to another page, you would write code like this. I will demonstrate this performing other actions with the New button below.

The new button on the header does the same thing as back button, however this goes to the pgAddNote screen. If you note with this button, we wanted to store the name of the page the pgAddNote is accessed from.

On the Notes Listing, when a user clicks the new button

JavaScript
$('#pgNoteNew').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
$('#pgAddNote').data('from', pgNote);
// move to the add new record screen
$.mobile.changePage('#pgAddNote', {transition: 'slide'});
});

This part of the pgNoteNew, the button thats clicked when a new note is being added does that.

JavaScript
$('#pgAddNote').data('from', pgNote);

This tells the app that for the AddNote screen, save a data attribute of 'from' with pgNote, telling the web app to keep this assigned to the page for later processing.

When the user is on the Add Note Page and clicks on Back, the web app should read which page opened the Add Note page and perform accordingly.

On the Add Note page, when a user clicks the Back button

JavaScript
$('#pgAddNoteBack').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
//which page are we coming from, if from sign in go back to it
var pgFrom = $('#pgAddNote').data('from');
switch (pgFrom) {
case "pgSignIn":
$.mobile.changePage('#pgSignIn', {transition: 'slide'});
break;
default:
// go back to the listing screen
$.mobile.changePage('#pgNote', {transition: 'slide'});
}
});

On the Add Note page, when a user clicks the Save Note button

JavaScript
$('#pgAddNoteSave').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
// save the Note
var NoteRec;
NoteRec = pgAddNoteGetRec();
app.addNote(NoteRec);
});

pgAddNoteGetRec() is a function that reads the Title and Details from the screen and assigns it to an object and this object gets passed to app.addNote to save the new note to localstorage.

On Edit Note, when a user clicks the Back button

JavaScript
$('#pgEditNoteBack').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
// go back to the listing screen
$.mobile.changePage('#pgNote', {transition: 'slide'});
});

When a user clicks the back button from the Edit Note screen, the Notes Listing page will be shown.

On Edit Note screen, when a user clicks the Update Note button

JavaScript
// click update when editing a record
$('#pgEditNoteUpdate').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
// save the Note
var NoteRecNew;
NoteRecNew = pgEditNoteGetRec();
app.updateNote(NoteRecNew);
});

When a user clicks the update button, the contents of the title and details are saved in an object that gets passed to app.updateNote to update localstorage with the new details of the Note. The title of the note is readonly and cannot be changed at this time. This ensures that the details of the title are tamper proof as its unique.

This next section demands some further explanation as it deals with the messagebox as defined above.

On Update Note screen, when a user clicks the Delete button

JavaScript
$('#pgEditNoteDelete').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
var Title = $('#pgEditNoteTitle').val();
Title = Title.replace(/-/g,' ');
$('#msgboxheader h1').text('Confirm Delete');
$('#msgboxtitle').text(Title);
$('#msgboxprompt').text('Are you sure that you want to delete this note?');
$('#msgboxyes').data('method', 'deleteNote');
Title = Title.replace(/ /g,'-');
$('#msgboxyes').data('id', Title);
$('#msgboxno').data('topage', 'pgEditNote');
$.mobile.changePage('#msgbox', {transition: 'pop'});
});
};

When a user clicks the Delete button when they edit a note, we want a messagebox to appear that will ask them if they want to delete the note or not. We want our message box to be dynamic as it might be used by other models we add within the notekeeper app.

This is what happens when a user selects delete.

1. The title is read from the screen. The title is unique

JavaScript
var Title = $('#pgEditNoteTitle').val();

2. Any spaces in the title are replaced with -

JavaScript
Title = Title.replace(/-/g,' ');

3. The title of the messagebox becomes Confirm Delete

JavaScript
$('#msgboxheader h1').text('Confirm Delete');

4. We set an title to show is the note to be deleted using the title

JavaScript
$('#msgboxtitle').text(Title);

5. We set the prompt of the message box

JavaScript
$('#msgboxprompt').text('Are you sure that you want to delete this note?');

6. If the user opts to delete the note, we want the web app to run the app.deleteNote method. This is done by assigning the data-method element of the msgboxyes button.

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

7. We set the data-id of the yes button to the title of the note

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

8. If the user selects no in the message box, we go back to the Edit Note page

JavaScript
$('#msgboxno').data('topage', 'pgEditNote');

9. We show the newly updated message box to ther user, making it pop up in the screen

JavaScript
$.mobile.changePage('#msgbox', {transition: 'pop'});

You can relate to Figure 5 above on how the messagbox was defined. That concludes the bindings for the notes. Below are some other useful functions that the web app uses with comments.

JavaScript
//get the record to be saved and put it in a record array
function pgAddNoteGetRec(){
//define the new record
var NoteRec
NoteRec = {};
NoteRec.Title = $('#pgAddNoteTitle').val();
NoteRec.Detail = $('#pgAddNoteDetail').val();
return NoteRec;
}

//get the record to be saved and put it in a record array
function pgEditNoteGetRec(){
//define the new record
var NoteRec
NoteRec = {};
NoteRec.Title = $('#pgEditNoteTitle').val();
NoteRec.Detail = $('#pgEditNoteDetail').val();
return NoteRec;
}

//clear the forms for new data entry
function pgAddNoteClear(){
$('#pgAddNoteTitle').val('');
$('#pgAddNoteDetail').val('');
}

//clear the forms for new data entry
function pgEditNoteClear(){
$('#pgEditNoteTitle').val('');
$('#pgEditNoteDetail').val('');
}

This function gets executed when a user adds a new note. The previously NoteRec read from the screen contents is passed to it for saving to the localstorage. When a new note is saved, the web app reads existing notes from localstorages by executing app.getNotes. This returns a json object. Then the the new note is added to existing notes and saved to localstorage.

The contents of the screen are cleared. The web app will stay on the same page until back is clicked. As a follow up article, we will update the NoteKeeper to have a Sign In and Sign Up screens so that users can protect their notes.

Saving the note to localstorage

JavaScript
app.addNote = function(NoteRec){
// get Note records.
var NotesObj = app.getNotes();
// define a record object to store the current details
var Title = NoteRec.Title;
Title = Title.replace(/ /g,'-');
NotesObj[Title] = NoteRec;
localStorage['notekeeper-notes'] = JSON.stringify(NotesObj);
// clear the form fields
pgAddNoteClear();
//which page are we coming from, if from sign in go back to it
var pgFrom = $('#pgAddNote').data('from');
switch (pgFrom) {
case "pgSignIn":
$.mobile.changePage('#pgSignIn', {transition: 'slide'});
break;
}
};

As previously indicated, when a user selects a note from existing notes in the listview, it gets edited. This function, from the title specified of the note, reads the note from existing notes in localstorage and then updates the title textbox and detail text area with the saved contents of the note.

Showing the Saved Note on screen after reading it from local storage

JavaScript
app.editNote = function(Title){
// get Note records.
var NotesObj = app.getNotes();
// lookup specific Note
Title = Title.replace(/ /g,'-');
var NoteRec = NotesObj[Title];
$('#pgEditNote').data('url', Title);
$('#pgEditNoteDelete').data('href', Title);
$('#pgEditNoteTitle').attr('readonly', 'readonly');
$('#pgEditNoteTitle').attr('data-clear-btn', 'false');
$('#pgEditNoteTitle').val(NoteRec.Title);
$('#pgEditNoteDetail').val(NoteRec.Detail);
};

Updating an existing note and persisting to localstorage

JavaScript
app.updateNote = function(NoteRecNew){
// get Note records.
var NotesObj = app.getNotes();
// lookup specific Note
var Title = NoteRecNew.Title;
Title = Title.replace(/ /g,'-');
var NoteRec = NotesObj[Title];
// assign new values to read record
NoteRec.Title = NoteRecNew.Title;
NoteRec.Detail = NoteRecNew.Detail;
NotesObj[Title] = NoteRec;
localStorage['notekeeper-notes'] = JSON.stringify(NotesObj);
// clear the form fields
pgEditNoteClear();
// show the page to display after a record is deleted
$.mobile.changePage('#pgNote', {transition: 'slide'});
};

The note details have been read from Update Note screen and assigned to an object, this object is passed to the above method. The notes are read from local storage and if the note already exists on local storage its over-written. The contents of the Update Page are cleared and the user is taken to the Notes Listing page.

Deleting a note from Local Storage

JavaScript
app.deleteNote = function(Title){
// clear the set values// get the Note records from localStorage
var NotesObj = app.getNotes();
// delete selected Note
delete NotesObj[Title];
// write it back to localStorage
localStorage['notekeeper-notes'] = JSON.stringify(NotesObj);
// show the page to display after a record is deleted
$.mobile.changePage('#pgNote', {transition: 'slide'});
};

This method above deletes an existing note from local storage using the title of the note as the key. First the notes are read from local storage and if the one having the title exist, it gets deleted by excuting the delete NotesObj[Title]; script.

Below is the method to get all notes from local storage. All notes read are assigned a json object.

Get Notes from Local Storage

app.getNotes = function(){
// get Note records
var NotesObj = localStorage['notekeeper-notes'];
if (!NotesObj){
NotesObj = {};
localStorage['notekeeper-notes'] = JSON.stringify(NotesObj);
} else {
NotesObj = JSON.parse(NotesObj);
}
return NotesObj;
};

The records that store notes are stored in a localstorage key named notekeeper-notes. As we might have other details to store for notekeeper e.g. users and their passwords, it is an obvious choice to separate these elements. If there are no existing notes, a blank object is stored in the storage and returned as blank.

Displaying Notes on Notes listing

The code below, reads all notes available from local storage, loops through each of them and displays the title of each note to the note listing listview. This is the updating of the listview during runtime.

JavaScript
app.displayNotes = function(){
// get Note records.
var NotesObj = app.getNotes();
// create an empty string to contain html
var html = '';
// make sure your iterators are properly scoped
var n;
// loop over notes
for (n in NotesObj){
var nLnk = n.replace(/-/g,' ');
html += NoteLi.replace(/ID/g,nLnk).replace(/LINK/g,n);
}
$('#pgNoteList').html(NoteHdr + html).listview('refresh');
};

This is one of the most important things I wanted to learn. This procedure gets all the notes from the localstorage, and iterate through each creating a new clickable element in the listview. When we started we defined a few parameters including a NoteLi, which was a listitem. Here we use the same item and just update it with the title of each note. The listview item elements are just basic, but one can add a count bubble, side content and a description of an item. In an upcoming article that follows this, we will explorer more on listviews by adding Users to this NoteKeeper.

When all the notes are read, the listview is updated once with the header and the new element details. One of the recommended features of updating elements at run time is not through using the document dot notation of creating elements but by creating elements like this and updating the .html elements of the item at once.

Another important fact is .listview('refresh') method. This ensures that our changes are reflected on the listview.

Figure 6 here shows a listing of a captured note. This list will be dynamic depending on the number of notes added. Typing something on the Search Notes will list all notes meeting your search criteria on your listview.

Figure 6

and Figure 7, when no lists exists

Figure 7

Checking Storage

JavaScript
app.checkForNotesStorage = function(){
var NotesObj = app.getNotes();
// are there existing Note records?
if (!$.isEmptyObject(NotesObj)) {
// yes there are. pass them off to be displayed
app.displayNotes();
} else {
// nope, just show the placeholder
$('#pgNoteList').html(NoteHdr + noNote).listview('refresh');
}
};

We want the web app to check local storage when it starts and show the appropriate notifications to the end user or display existing notes. If the notes exists, they will be displayed by calling the app.DisplayNotes or else the user will be notified that there are no Notes.

App Script End

JavaScript
app.init();
})(NoteKeeper);
});

These last three code lines close off our app definition and our application is ready for execution. You can save all this script in a separate file and reference it from your main html file or include it as part of the file html file itself.

Working Offline

As part of this article within the source code, an index.appcache file exists that enables this app to work offline.

Points of Interest

During developing the NoteKeeper, I learned how to create a behaviour for a messagebox and trap the yes or no buttons depending on users choices and execute code based on those choices. This included storing data items within data-* attributes of elemements. The storarage and retrieval of data from local storage proved to be an eye opener too especially the reading the data into json objects and also writing it back as text.

Screen navigation also was an important factor and deciding on how to create a sliding effect to pages to make a nice user interface. Discovering FastClick also helped in making the web app appear faster in performance speed. Handling the listview by updating it during runtime was marvelous.

For some reason, theming does however pose a serious challenge for me as it does not seem to be working within the emulators, when I changed it to "e" for example, nothing happened. I will find out some more how to achieve that here. I intend to explore PhoneGap in terms of how to store the information on device files and perhaps add functionality to backup and restore the data as this can be used for any other generic tool.

License

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


Written By
Software Developer SITHASO HOLDINGS (PTY) LTD
South Africa South Africa
I'm a Bachelor of Commerce graduate, fell inlove with ICT years back with VB5. Used Pick & System Builder to create a windows app. Very curious, developed my first web database app called Project.Show using ExtJS. Published on Google Play Store, learned JQuery Mobile, a project manager at best. My first intranet app eFas with MySQL.

Fear closes people to a lot of things and we hold ourselves back being held by it. Thus the sooner you believe you can't do something, the sooner everything will work towards that belief. Believe in yourself at all times because you can do anything you set your mind to it!

I have a very beautiful woman and four kids, the best joys in the world. East London, South Africa is currently home.

Awards:

Best Mobile Article of February 2015 (First Prize)
http://www.codeproject.com/Articles/880508/Create-a-CRUD-web-app-using-JQuery-Mobile-and-Loca

Best Mobile Article of May 2015 (Second Prize)
http://www.codeproject.com/Articles/991974/Creating-JQuery-Mobile-CRUD-Apps-using-JQM-Show-Ge

Apps
Bible.Show (Android Store App)
https://www.facebook.com/bibleshow
https://play.google.com/store/apps/details?id=com.b4a.BibleShow

JQM.Show (Android Store App)
https://www.facebook.com/jqmshow
https://play.google.com/store/apps/details?id=com.b4a.JQMShow

CodeProject.Show (An offline CodeProject Article writer)
http://www.codeproject.com/Articles/993453/CodeProject-Show-A-CodeProject-offline-article-wri

Comments and Discussions

 
QuestionDelete isn't working--Any help appreciated Pin
Member 1246662918-Apr-16 8:08
Member 1246662918-Apr-16 8:08 
AnswerRe: Delete isn't working--Any help appreciated Pin
Anele 'Mashy' Mbanga18-May-16 10:21
professionalAnele 'Mashy' Mbanga18-May-16 10:21 
QuestionRe: Delete isn't working--Any help appreciated Pin
Member 1285785428-Nov-16 3:48
Member 1285785428-Nov-16 3:48 
AnswerRe: Delete isn't working--Any help appreciated Pin
Anele 'Mashy' Mbanga29-Nov-16 0:13
professionalAnele 'Mashy' Mbanga29-Nov-16 0:13 
PraiseWonderful explanation and tutorial Pin
Member 1246662917-Apr-16 19:32
Member 1246662917-Apr-16 19:32 
QuestionVery grateful - works for me on iPhone and iPad Pin
jonathan salisbury13-Jul-15 2:36
jonathan salisbury13-Jul-15 2:36 
AnswerIn response to the question: "How I can solicit more explanations from a persons vote?" Pin
Sergey Alexandrovich Kryukov8-Apr-15 16:59
mvaSergey Alexandrovich Kryukov8-Apr-15 16:59 
QuestionNicely done Pin
frankboyd31-Mar-15 20:50
frankboyd31-Mar-15 20:50 
AnswerRe: Nicely done Pin
Anele 'Mashy' Mbanga31-Mar-15 21:41
professionalAnele 'Mashy' Mbanga31-Mar-15 21:41 
I appreciate the feedback, I am keeping it up!! Wink | ;) Actually this was my second try at writing a CodeProject article after the first one I wrote was rejected as not meeting standards. I learned and have been nicely surprised by winning the first prize for Feb 2015.
You can do anything you set your mind to! Believe!
Come on, change somebody's life and vote on their article, tip etc if it was helpful to you.

QuestionAppreciation Pin
Anele 'Mashy' Mbanga24-Mar-15 10:40
professionalAnele 'Mashy' Mbanga24-Mar-15 10:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.