How to pass information between the Notes List page and the Note Editor page using URLs and query strings

How to load a note in the Note Editor page

How to save a new or edited note

Let’s begin with a few changes that will make the application easier to maintain and test.

Refactoring

Our data context module directly uses a reference to the jQuery library when calling the jStorage plugin. We will change our code so we pass the jQuery reference as a parameter when we invoke the dataContext module, like so:

Among other things, this change will allow us to test the dataContext module with a different key than the one we use in the application itself. Let’s modify our AppSpec.js file to support this change:

In the loop that creates the list items representing the notes, we keep track of the date the notes were taken, inserting a new list item with the attribute data-role=”list-divider” whenever the date value changes. This gives us a nice list divider: In addition, notice how the list items contain a link whose query string has a reference to the id of the note. We will use this information when we load a selected note into the Note Editor page. Let’s start working on this page now.

Creating the Note Editor Page

The Note Editor page consists of a div element with the data-role=”page” attribute. We will add this div to the index.html file like so:

This page has a header area with Save and Cancel buttons, a form with the elements that allow us to edit a note, and a footer that hosts the Delete button. As you can see, jQuery Mobile has taken care of nicely styling and laying out the form elements.

We will give the Save and Delete button special treatment, binding to their tap events to trigger the routines that save or delete the note loaded in the Note Editor page.

The role of the Cancel button is to take us back to the Notes List page. This is why we use a link to the #notes-list-page bookmark.

Loading a Note in the Editor

Let’s take a moment to think about what needs to happen to load a note into the editor. For existing notes, when the user taps the li element representing a note in the Notes List page, we will perform the following steps:

Look up the note in the notesList array, based on the note’s id contained in the li element’s link.

Set the values of the title and narrative form elements in the Note Editor page to those of the selected note’s title and narrative.

Make the Note Editor page active.

For new notes, when the user taps the New button in the Notes List page, we will perform these steps:

Create a new, blank note.

Set the values of the title and narrative form elements in the Note Editor page to those of the new note’s title and narrative.

Make the Note Editor page active.

These routines are similar. The only difference is that in the first case we have to look up and load an existing note, while in the second case we need to create and load a new note.

Let’s begin with the first set of steps. The first thing we need in our controller is a reference to the Note Editor. We will define a selector for it in the declarations section of the controller module:

var noteEditorPageId = "note-editor-page";

Next, we need to return to the onPageChange() function and handle the case when we are switching to the editor page:

The handler is a bit more complex now. We added the fromPageId variable, which will store the id of the page we’re navingating from. We will call this page Source page from now on. We will call the page we’re navigating to Target page.

The value of fromPageId will help us determine if we need to load a note into the editor:

if (fromPageId === notesListPageId) {
renderSelectedNote(data);
}

If the source page is the notes list, we load the note by calling the renderSelectedNote() function, which we will implement in a minute. Note that we added the check for the fromPage parameter because the pagechange event is also triggered after the application launches, when there isn’t a source page yet. It doesn’t make sense to acquire the id of the source page when the source page itself doesn’t exist:

To acquire the URL’s hash we use the $.mobile.path.parseUrl() function, which parses a URL into an object that facilitates accessing the URL’s components.

Once we’re certain the source page is the Notes List, we create an object containing the query string parameters passed from the source page. The queryStringToObject() helper function performs this task:

The queryStringToObject() function takes the value of the data.options.queryString property as a parameter:

var queryStringObj = queryStringToObject(data.options.queryString);

The problem is that the data.options object does not have a native queryString property. However, we can create it if we find a place, or rather a time before the page transition occurs, where we can acquire the value of the query string.

It turns out that this is possible if we define a handler for jQuery Mobile’s pagebeforechange event. Let’s revisit the init() function, and add the following line:

Here we use $.mobile.path.parseUrl() to acquire the query string and add it to the data.options object. As the onPageBeforeChange handler is invoked before the onPageChange handler, we’ve found an approach to inject the query string defined in the source page into the events chain, and propagate it to the target pages, where it can be used.

Back in renderSelectedNote(), we can finally take care of loading the selected note, or resetting the title and narrative fields, like so:

Observe how we’re keeping a reference to the selected note in the currentNote variable. This will later allow us to save or delete the note without having to perform a lookup on the notesList array.

This is what it takes to load a note. Let’s make sure things are working as expected. Fire up your favorite browser and confirm that tapping a note in the Notes List page loads the note into the Note Editor page:

Similarly, tapping the New button should load a new note into the editor.

Saving a Note

As the Save Note workflow is initiated when a user taps the Save button, the controller module needs to define a handler for the button’s tap event. We will use this handler to invoke a saveNote() function that we will create in the dataContext module. Let’s work on the tap handler first.

We need an identifier for the Save button, which we can create at the top of the controller module like so:

var saveNoteButtonSel = "#save-note-button";

Next, we need to bind the tap handler in the controller’s init() function:

The first interesting thing that happens in this function is the creation of a temporary note, and the call to the NoteModel’s isValid method. This is the method that will allow us to validate a note before making it permanent.

A simple check on the note’s title is enough to validate the note. Nothing complicated.

Back in onSaveButtonTapped, we find out if we’re editing an existing note by observing the value of currentNote. If currentNote points to an existing note, we transfer the title and narrative of the temporary note to it. If currentNote is not poiting to an existing note, we make it point to the temporary note’s reference.

Then, we save the note by calling the dataContext module’s saveNote function. We haven’t created this function yet. Let’s define a behavior test for it in the AppSpec file:

In this spec we first empty the local storage container we will use. Then, we save a dummy note by calling saveNote on the dataContext module. Last, we retrieve the value from local storage and assert that it is in effect an instance of the NoteModel class.

This function is pretty straightforward. We start by iterating over the array of existing notes. If we find the id of the edited note, we “edit” the existing note through an in-place replacement with the passed note. If don’t find the id, we simply place the passed note at the beginning of the array.

Finally, we call the private function saveNotesToLocalStorage, which we also need to add to the dataContext module. This is where we save the modified array to local storage:

This is a convenience function that we will call every time we need to return to the Notes List page.

There is one last step we need to take in order for the Edit Note and Save Note workflows to work correctly. We need to make sure we reset the currentNote reference after a note is saved. We can do this from the onPageChange function, within its switch statement:

Now, fire up the emulator. You should be able to create and edit notes.

Nice! We just built the ability to create and edit notes into our application.

I’m sure you noticed that we still have a loose end in the onSaveNoteButtonTapped function, as we have not handled the invalid note scenario. We will leave this step for the next chapter, along with deleting notes.