Twitter: cfjedimaster


Address: Lafayette, LA, USA

jQuery Mobile - adding Local Storage

07-13-2011 31,121 views Mobile, jQuery, JavaScript, ColdFusion 45 Comments

So about a week or so ago I had an idea about a simple jQuery Mobile application that would make use of Local Storage. That was a week ago. Turns out - the "simple" application turned out to be a royal pain in the rear. Not because of Local Storage, but because of some misconceptions and lack of knowledge on my part in jQuery Mobile. What followed was a couple painful days (and more than a few curse words) but after all of that, I feel like I've got a better understanding of jQuery Mobile and got to play with some new features. So with that being said, let's get to the app.

My idea was a rather simple one. Given a collection of art, allow the user to browse categories and view individual pieces of art. I've done this before as a jQuery Mobile example. But what I thought would be interesting is to add a simple "Favorites" system. As you browse through the art you can select a piece you like, add it to your favorites, and then later have a quicker way to access them. To make things even more interesting, I thought I'd make use of Local Storage. Local Storage is an HTML5 feature, and unfortunately, it isn't quite as sexy as Canvas so it doesn't get as many cool demos. But it's one of those - you know - useful things that is actually pretty well supported. Local Storage is basically a key system of data. You can store, on the browse, a key and a value. Like name="Raymond". Unlike cookies, this data is not sent to the server on every request. Rather, it just sits there on the client ready to be used by JavaScript. You've got access to both a permanent (localStorage) and session based (sessionStorage) API. The excellent DiveIntoHTML5 talks about Local Storage here. I won't talk any more about the API as it's rather quite simple and the Dive site explains it more than well enough. Before getting into this version though, let's quickly look at the initial, simpler version.

My application consists of three HTML files, all powered by ColdFusion. The home page will list categories, the category page will list art, and the detail page will show just an art piece. Let's start with the index page.

view plain print about
1<cfset categories = application.artservice.getMediaTypes()>
2
3<!DOCTYPE html>
4<html>
5    <head>
6    <title>Art Browser</title>
7    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
8    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
9    <script src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
10</head>
11<body>
12
13<div data-role="page">
14
15    <div data-role="header">
16        <h1>Art Browser</h1>
17    </div>
18
19    <div data-role="content">    
20        <ul data-role="listview" data-inset="true">
21            <cfoutput query="categories">
22                <li><a href="category.cfm?id=#mediaid#&media=#urlEncodedFormat(mediatype)#">#mediatype#</a> <span class="ui-li-count">#total#</span></li>
23            </cfoutput>
24 </ul>
25    </div>
26
27</div>
28
29</body>
30</html>

Note that I begin by asking for media types. Our database categories art by a media type and I'll be considering that my categories. The getMediaTypes method returns a query which means I can simply loop over it in my content.

Next up we have the category page - which is really just a slightly different version of the last one. Note though the use of the Home icon.

view plain print about
1<cfparam name="url.media" default="">
2<cfparam name="url.id" default="">
3<cfset art = application.artservice.getArt(mediatype=url.id)>
4
5<!DOCTYPE html>
6<html>
7    <head>
8    <cfoutput><title>Art Category - #url.media#</title></cfoutput>
9    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
10    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
11    <script src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
12</head>
13<body>
14
15<div data-role="page">
16
17    <cfoutput>
18    <div data-role="header">
19        <a href="index.cfm" data-icon="home" data-iconpos="notext">Home</a>
20        <h1>#url.media#</h1>
21    </div>
22    </cfoutput>
23
24    <div data-role="content">    
25        <cfif art.recordCount>
26            <ul data-role="listview" data-inset="true">
27                <cfoutput query="art">
28                    <li><a href="art.cfm?id=#artid#">#artname#</a></li>
29                </cfoutput>
30     </ul>
31        <cfelse>
32            Sorry, no art in this category.
33        </cfif>
34    </div>
35
36
37</div>
38
39</body>
40</html>

And finally, let's look at our detail page.

view plain print about
1<cfparam name="url.id">
2<cfset art = application.artservice.getArtPiece(url.id)>
3
4<!DOCTYPE html>
5<html>
6    <head>
7    <cfoutput><title>Art - #art.name#</title></cfoutput>
8    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
9    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
10    <script src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
11</head>
12<body>
13
14<div data-role="page">
15
16    <cfoutput>
17    <div data-role="header">
18        <a href="index.cfm" data-icon="home" data-iconpos="notext">Home</a>
19        <h1>#art.name#</h1>
20    </div>
21    </cfoutput>
22
23    <cfoutput>
24    <div data-role="content">    
25        <b>Artist: </b> #art.artist#<br/>
26        <b>Price: </b> #dollarFormat(art.price)#<br/>
27        #art.description#
28        <p/>
29        <img src="#art.image#">
30    </div>
31    </cfoutput>
32
33</div>
34
35</body>
36</html>

This page is even simpler. We just get the art detail and render it within the page. Nothing fancy at all - not yet anyway.

You can demo this here: http://www.coldfusionjedi.com/demos/artbrowser/v1/

Ok, ready to go crazy? I decided on two main changes to my application. First, art pieces would have a new button, Add to Favorites (or Remove from Favorites). Once clicked, I'd use a jQuery Mobile dialog to prompt the user if they were sure. (Normally I hate crap like that. Don't second guess me. But I wanted to try dialogs in jQuery Mobile.) If the user confirms the action, I then simply update local storage to store the value. Since you can only store simple values, I used built in JSON features to store complex data about the art piece (really just the ID and name).

On the home page, I had, what I thought, was a simple thing to do. When the page loads, simply fill out a dynamic list of the user favorites. Here's where things really took a turn for the worst for me. I want to give a huge shout out to user aaraonpadoshek who helped me out on the jQuery Mobile forums. I'll show the new home page and explain what changed.

view plain print about
1<cfset categories = application.artservice.getMediaTypes()>
2
3<!DOCTYPE html>
4<html>
5    <head>
6    <meta name="viewport" content="width=device-width, initial-scale=1">
7    <title>Art Browser</title>
8    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
9    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
10    <script src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
11    <script>
12    //Credit: http://diveintohtml5.org/storage.html
13    function supports_html5_storage() {
14     try {
15     return 'localStorage' in window && window['localStorage'] !== null;
16     } catch (e) {
17     return false;
18     }
19    }
20    function supports_json() {
21     try {
22     return 'JSON' in window && window['JSON'] !== null;
23     } catch (e) {
24     return false;
25     }
26    }
27    
28    $(document).ready(function() {
29
30        //only bother if we support storage
31        if (supports_html5_storage() && supports_json()) {
32
33            //when art detail pages load, show button
34            $('div.artDetail').live('pageshow', function(event, ui){
35                //which do we show?
36                var id = $(this).data("artid");
37                if (!hasInStorage(id)) {
38                    $(".addToFavoritesDiv").show();
39                    $(".removeFromFavoritesDiv").hide();
40                }
41                else {
42                    $(".addToFavoritesDiv").hide();
43                    $(".removeFromFavoritesDiv").show();
44                }
45            });
46
47            //When clicking the link in details pages to add to fav
48            $(".addToFavoritesDiv a").live('vclick', function(event) {
49                var id=$(this).data("artid");
50                $.mobile.changePage("addtofav.cfm", {role:"dialog",data:{"id":id}});
51            });
52
53            //When clicking the link in details pages to add to fav
54            $(".removeFromFavoritesDiv a").live('vclick', function(event) {
55                var id=$(this).data("artid");
56                $.mobile.changePage("removefromfav.cfm", {role:"dialog",data:{"id":id}});
57            });
58
59            //When confirming the add to fav
60            $('.addToFavoritesButton').live('vclick', function(event, ui){
61                var id=$(this).data("artid");
62                var label=$(this).data("artname");
63                addToStorage(id,label);
64                $("#addToFavoritesDialog").dialog("close");
65            });
66
67            //When confirming the remove from fav
68            $('.removeFromFavoritesButton').live('vclick', function(event, ui){
69                var id=$(this).data("artid");
70                var label=$(this).data("artname");
71                removeFromStorage(id,label);
72                $("#removeFromFavoritesDialog").dialog("close");
73            });
74            
75
76            $('#homePage').live('pagebeforeshow', function(event, ui){
77                //get our favs
78                var favs = getStorage();
79                var $favoritesList = $("#favoritesList");
80                if (!$.isEmptyObject(favs)) {
81                    if ($favoritesList.size() == 0) {
82                        $favoritesList = $('<ul id="favoritesList" data-inset="true"></ul>');
83                        
84                        var s = "<li data-role=\"list-divider\">Favorites</li>";
85                        for (var key in favs) {
86                            s+= "<li><a href=\"art.cfm?id="+key+"\">"+favs[key]+"</a></li>";
87                        }
88                        $favoritesList.append(s);
89                        $("#homePageContent").append($favoritesList);
90                        $favoritesList.listview();
91                    } else {
92                        $favoritesList.empty();
93                        var s = "<li data-role=\"list-divider\">Favorites</li>";
94                        for (var key in favs) {
95                            s+= "<li><a href=\"art.cfm?id="+key+"\">"+favs[key]+"</a></li>";
96                        }
97                        $favoritesList.append(s);
98                        $favoritesList.listview("refresh");
99                    }
100                } else {
101                    // remove list if it exists and there are no favs
102                    if($favoritesList.size() >
0) $favoritesList.remove();
103                }
104            });        
105                    
106            //Adding to storage
107            function addToStorage(id,label){
108                if (!hasInStorage(id)) {
109                    var data = getStorage();
110                    data[id] = label;
111                    saveStorage(data);
112                }
113            }    
114            
115            //loading from storage
116            function getStorage(){
117                var current = localStorage["favorites"];
118                var data = {};
119                if(typeof current != "undefined") data=window.JSON.parse(current);
120                return data;
121            }
122            
123            //Checking storage
124            function hasInStorage(id){
125                return (id in getStorage());
126            }        
127
128            //Adding to storage
129            function removeFromStorage(id,label){
130                if (hasInStorage(id)) {
131                    var data = getStorage();
132                    delete data[id];
133                    console.log('removed '+id);
134                    saveStorage(data);
135                }
136            }    
137            
138            //save storage
139            function saveStorage(data){
140                console.log("To store...");
141                console.dir(data);
142                localStorage["favorites"] = window.JSON.stringify(data);
143            }
144    
145        }        
146
147    });
148    </script>
149
150</head>
151<body>
152
153<div data-role="page" id="homePage">
154
155    <div data-role="header">
156        <h1>Art Browser</h1>
157    </div>
158
159    <div data-role="content" id="homePageContent">    
160        <ul data-role="listview" data-inset="true">
161            <cfoutput query="categories">
162                <li><a href="category.cfm?id=#mediaid#&media=#urlEncodedFormat(mediatype)#">#mediatype#</a> <span class="ui-li-count">#total#</span></li>
163            </cfoutput>
164 </ul>
165        
166    </div>
167
168</div>
169
170</body>
171</html>

Ok - a bit more going on here. I'll take it step by step. On top I've got two utility functions taken based on code from the DiveIntoHTML5 site. One checks for local storage support and one for JSON. It's probably overkill for mobile, but it doesn't hurt. Notice that I check both of these functions before I do anything else. It occurs to me that I wrapped up a lot of code in that IF and I should have simply exited the document.ready event handler instead.

I begin by using the "pageshow" event for my art detail page to decide if I should show the "Add to" or "Remove from" buttons. The hasInStorage function is defined later on and is just a utility I wrote for my code to quickly see if a particular art piece is favorited. I'll show that art page in a bit so you can see the HTML differences.

The next two functions listen for clicks on the new buttons. Notice the "vclick" listener. This is not - as far as I know - actually documented. At least 5 of my gray hairs this week came from this. Apparently this is the new way to listen in for click events on multiple devices. It's in the jQuery Mobile blog, but again, it isn't documented. When I went live and tested my code, it had worked fine in Chrome but not at all in iOS or Android. Apparently this is why. Very frustrating! Notice - when you click, I use the built in changePage utility to load a page. But this is the cool thing - I can turn this into a dialog by adding a role attribute. So basically - addtofav.cfm and removefromfav.cfm are normal pages - but because of how I tell jQuery Mobile to load them, turn turn into dialogs. Sweet.

Moving down - the next two event handlers are for the actual confirmations. Nothing special there. They call my utility functions defined later on to change local storage values.

Ok - so here is the part I really struggled with and where aaraonpadoshek helped. I needed a way to say, "When the page loads, write out the list." Unfortunately, the pageshow event, which runs every time, also runs before the page initializes. Read that again - it runs every time the page shows and also before it's even fully drawn. There's a pageinit method which does run after the page initializes but only runs once. So when I used pageshow and tried to change my list, I got an error because jQuery Mobile hadn't added the magical unicorn dust yet to make it pretty. When I used pageinit it worked... once. Here's where Aaron's code helped. Notice we have pagebeforeshow being listened for now. It now detects in the list exists in the DOM. If it doesn't, we create it and initialize it ourselves as a list view. If it does exist, we update it using refresh. I'll be honest and say this still is a bit... fuzzy... in my mind. But it works! And that's good enough for me. I've got a bit of DRY going on there with the display but I'll fix that later.

Moving down - you can now see my functions for working with local storage. To be honest, it's all pretty trivial. I've got a function to add and remove, to check for existence, and to get and persist. I added wrappers for them because I'm using JSON to store the data. Now let's look at the update to art.cfm:

view plain print about
1<div class="addToFavoritesDiv" style="display:none"><a href="" data-role="button" data-artid="#art.id#">Add to Favorites</a></div>
2<div class="removeFromFavoritesDiv" style="display:none"><a href="" data-role="button" data-artid="#art.id#">Remove from Favorites</a></div>

That's the two buttons. Notice they are both hidden by default. Also note the use of data-artid to store in the primary key I'll use later. Now let's look at addtofav.cfm. I won't bother with the remove as it's pretty much the same.

view plain print about
1<cfparam name="url.id">
2<cfset art = application.artservice.getArtPiece(url.id)>
3
4<!DOCTYPE html>
5<html>
6    <head>
7    <title>Add to Favorites?</title>
8    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
9    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
10    <script src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>
11</head>
12<body>
13
14<div data-role="page" id="addToFavoritesDialog">
15
16    <div data-role="header">
17        <h1>Add to Favorites?</h1>
18    </div>
19
20    <div data-role="content">    
21        <p>
22        <cfoutput>
23        <a href="" data-role="button" data-theme="b" data-artid="#url.id#" data-artname="#art.name#" class="addToFavoritesButton">Yes!</a>
24        <a href="art.cfc?id=#url.id#" data-rel="back" data-role="button">No thank you</a>
25        </cfoutput>
26        </p>    
27    </div>
28
29</div>
30
31</body>
32</html>

Nothing fancy here either. Just simple content with some buttons. Here's a shot of the art view:

And here's a shot of the dialog.

And finally - the new home page:

Whew! Done. By the way, I'll also point out another issue I had. When I first tested on a mobile device, the text was incredibly small. I got a nice tweet from @jquerymobile pointing out that in beta1, you need to include a new meta tag in your page templates:

view plain print about
1<meta name="viewport" content="width=device-width, initial-scale=1">

Adding that helped right away. Ok - that's it. I've included a zip below and you can play with this yourself via the uber Demo button. Enjoy.

Download attached file

45 Comments

  • Commented on 07-14-2011 at 12:42 AM
    I've made a few changes to your JS:
    https://gist.github.com/1081987/f6203727eb979c715f...
    I thought there was a way to do a diff on gists.github.com but forgot how (if it existed)
  • Commented on 07-14-2011 at 5:31 AM
    It looks like - mainly - a bit tighter in the event handlers, right?

    FYI (to all) - about to go on a 9 day vacation so may be a bit slow to respond.
  • SteveOfSP #
    Commented on 07-14-2011 at 10:28 AM
    Running this online (FF) hangs when you drill down to the actual art. But still very cool. Thanx.
  • Commented on 07-24-2011 at 9:47 AM
    Could be my console.log usage. Are you not running Firebug? Does it work fine in Chrome?
  • Sholto Maud #
    Commented on 09-21-2011 at 10:11 PM
    the $('#homePage').live('pagebeforeshow', function(event, ui){ ...
    doesn't work for me when I first visit the page, the favourites list doesn't render. If I go back to the home page from an internal page, the favourites list does render and works beautifully from then onwards. Is there a trick to pagebeforeshow when first landing on the page?
  • Sholto Maud #
    Commented on 09-22-2011 at 1:20 AM
    I'm using jgm beta3 ... and I've found that it works when I remove $(document).ready(function()
  • Commented on 09-22-2011 at 5:48 AM
    I can see no reason why this would fail to work in b3, but obviously I've not tested it there. At most, document.ready may be firing before JQM has finished marking stuff up, BUT, the live call on the div should work whenever.
  • Henry #
    Commented on 10-15-2011 at 2:01 PM
    i'm so disapointed,that you didnt used simple javascript and for example webdb or indexdb.Couse i making a simular app and i fail :(.Anyway i keep searching to get mine running.

    Good work anyway
  • Commented on 10-16-2011 at 6:14 AM
    I disagree completely with your comment Henry.

    First off - "simple JS" - my solution does use JavaScript. So that doesn't make sense.

    Secondly - "webdb/indexdb" - first off - indexdb/webdb isn't supported as well as local storage. Secondly, the feature doesn't make sense for what I'm doing. You would use indexdb for storing lots of data that needs a SQL-style search interface. My need was to store a few things that I'd always fetch directly. I'm not saying you _couldn't_ use indexdb here, but I really believe local storage makes more sense.
  • Donn #
    Commented on 01-06-2012 at 11:25 PM
    This is amazing. Keep posting dude!
  • Jason Haag #
    Commented on 01-18-2012 at 3:24 PM
    Excellent tutorial and thanks for sharing! I'm interested in doing the same thing (add to favorites), except don't have a need for cold fusion. I totally agree with you about using local storage for this type of capability. This is an excellent example that many can learn from! Without knowing the syntax for cold fusion I would like to create something similar with just HTML5, CSS, and JavaScript. Rather than using the dynamic variables you created (e.g. data-artid="#url.id#") could I just use static variables instead and it should work, right? But I would have to create a unique dialog for each html page using data-artname and data-artid, rather than it being dynamic for each art item. Great stuff!
  • Commented on 01-18-2012 at 3:55 PM
    Yep, you can use static HTML.

    To your last point about needing N pages for the detail. Technically, you do not. See: http://jquerymobile.com/demos/1.0/docs/pages/page-...
  • Jason Haag #
    Commented on 01-19-2012 at 4:01 PM
    Thanks Raymond. Are you referring to the section at the bottom of the link to the jqm documentation, "Passing parameters between pages?"
  • Commented on 01-19-2012 at 4:06 PM
    No, that entire page. It talks about how to create virtual dynamic pages.
  • Jason Haag #
    Commented on 01-19-2012 at 4:10 PM
    In other words, would one of these plug-ins they mentioned be required?
  • Commented on 01-19-2012 at 4:18 PM
    Crap, brain fart on my part. You want this URL:

    http://jquerymobile.com/demos/1.0/docs/pages/page-...

    It's the page before the one I sent. Sorry.
  • Jason Haag #
    Commented on 01-19-2012 at 4:47 PM
    That's it! Thanks again!
  • Jason Haag #
    Commented on 01-20-2012 at 4:20 PM
    Raymond, were you able to get this to work in Firefox? I'm seeing the following error using Firebug:

    invalid 'in' operand getStorage()
    [Break On This Error]    

    return (id in getStorage());

    Not sure if my install of firefox may have an issue or if you are able to replicate this.
  • Jason Haag #
    Commented on 01-20-2012 at 5:11 PM
    This seems to prevent the favorites feature from working in firefox or IE. I'm trying to look to see if there is another way to return the id from getStorage() using jquery.
  • Commented on 01-21-2012 at 8:36 AM
    What version of Firefox?

    You can probably change:

    function hasInStorage(id){
    return (id in getStorage());
    }

    to this:


    function hasInStorage(id){
    var current = getStorage();
    return current.hasOwnProperty((id);
    }

    I'm writing this just in the comment field so it may not be exactly right.
  • Jason Haag #
    Commented on 01-23-2012 at 1:07 PM
    I'm using FF 9.0.1

    Tried that, but says current is NULL. I'm seeing the same issue when trying your demo in firefox.

    I started down this path because the "Add to Favorites" and "Remove" buttons don't render so it led me to: hasInStorage(id).

    Are you able to see something different? Does the demo work in firefox on your end? It works perfectly in chrome and safari on my end.
  • Commented on 01-23-2012 at 1:51 PM
    Nope, it does not work. I assumed that in operator worked in FF. Let me dig a bit.
  • Commented on 01-23-2012 at 1:52 PM
    When you tried my mod, did you correct the typo I had above?
  • Commented on 01-23-2012 at 1:57 PM
    Nevermind, it's a bit deeper. Fixing now.
  • Commented on 01-23-2012 at 2:00 PM
    Ok, I modified both hasInStorage and getStorage. I think this version is better. Just view source here:

    http://www.coldfusionjedi.com/demos/artbrowser/v5
  • Jason Haag #
    Commented on 01-23-2012 at 2:35 PM
    Hey Ramond, that works in FF now. Was it hanging up on current not being undefined (double negative)? I'm new to jquery so the syntax still throws me a bit sometimes.

    Just found this plug-in too that helps for checking if your keys are stored: https://addons.mozilla.org/en-US/firefox/addon/fir...
  • Commented on 01-23-2012 at 3:22 PM
    Yep to your first comment.

    As an FYI, there is a Chrome extension for LocalStorage viewing too. One I worked on. :)

    https://chrome.google.com/webstore/detail/bpidlidm...
  • Jason Haag #
    Commented on 01-24-2012 at 7:27 AM
    Thanks Ramond! Nice job! Will check out your chrome extension!

    Hey, have you tested this on IE? The fav buttons render and I can see that localStorage and JSON are supported in the latest version of IE, but IE does a full page refresh when navigating from the addtofav dialog screen to the categories. I'm guessing it handles jQuery mobile a bit differently and this can most likely be alleviated with some type of $.mobile.changePage() update.
  • Commented on 01-24-2012 at 8:30 AM
    Nope, didn't bother. But note that I was using jQM Beta 1 for this demo. It may work better with the final release.
  • Jason Haag #
    Commented on 01-24-2012 at 1:01 PM
    I'm using the final release and it's still the same. There must be a way to deal with it using jQM. I'll have to create some test cases and submit as a ticket probably. It has to be something with jquery syntax that IE doesn't like...my guess anyway.
  • Jason Haag #
    Commented on 03-15-2012 at 7:37 PM
    Hey CFJedi, I just noticed that this doesn't work in Windows Phone. Thinking it doesn't like something with the jquery syntax.

    I adapted your favorites example and implemented in an app I'm building for a conference: http://gt12.adlnet.mobi

    If you go the the schedule and drill down to the sessions you'll see where I incorporated it. Pretty sweet! Do you have an inkling of why it might not work on Windows Phone? The lates OS supports localStorage.
  • Commented on 03-15-2012 at 8:06 PM
    Hmm. So the best I can recommend is to remove stuff until it works and see if you can pinpoint the line where it breaks. That's what I do when things break for me. You should be able to narrow it down.
  • BRIAN FERNANDES #
    Commented on 05-24-2012 at 1:55 PM
    Hi Jason,

    Do you have a sample code for the application.
  • Marcio #
    Commented on 10-26-2012 at 9:03 AM
    Hello Raymond,

    Nice tutorial! I have a doubt. Is there a way to copy from Android clipboard to localStorage using jQuery ou native javascript?

    Thanks
  • Commented on 10-26-2012 at 9:21 AM
    No idea. I'd assume not... that seems like it would be a security issue.
  • Johan #
    Commented on 04-10-2013 at 5:13 PM
    Hi, great work, this is what I have been looking for!
    I just new at this and I'm working on an new app
    and I wonder where you can change to categoryname to something else?
    For example "Jewelry", "Painting" and so on...

    I didn't found It anywhere in your sample cfm files. Where is those categories saved?
    And how do I change it?
    I would appreciate it if you could light me in this matter. :)
  • Commented on 04-10-2013 at 7:34 PM
    Sorry - I'm not quite sure I get you. Are you asking how to change the data? Cuz the data itself is just sample data.
  • Johan #
    Commented on 04-11-2013 at 2:47 AM
    When I open the file index.cfm in dreamviewer I don't see the categories: functional, Jewelry, Painting and so on, how do I change this data/text? For example to Fruit, Vegetables and so on...
  • Commented on 04-11-2013 at 6:55 AM
    Dreamweaver is a code editor. You understand how ColdFusion works, right?
  • Johan #
    Commented on 04-11-2013 at 2:11 PM
    I started to look at it last night, is it there you program the categories?
    not sure how it works together with jqm
    jqm i know a bit more about
    I wanted to make favorites on my mobile app and i need it to be local in the phone
    ive been searching the web for a good solution and yours were the closest to my idea
    i simply have learn how this works
  • Commented on 04-11-2013 at 9:00 PM
    Ah, well, then you are in for a journey. :) Note that you can use any server-side language. I'm primarily a ColdFusion person, but I'm doing more with Node.js lately.
  • Johan #
    Commented on 04-12-2013 at 2:07 PM
    Yes programming is fun so that road is the way to go :)
    any chance you could share this favorites files to cf ?
    if yes would you please send to my mail?
    I would be deeply grateful and maybe I can put thanks in the app info with your name if thats ok ? :)
  • Commented on 04-13-2013 at 11:42 AM
    The CF code is included in the download.
  • Johan #
    Commented on 04-13-2013 at 4:59 PM
    ok, im confused lol I just started to look at CF but I cant import the files into coldfusion, or am I getting this wrong, if I just load them into cf then I cant find/change the favoritenames, etc. Functional, jewel, painting..and so on
  • Commented on 04-13-2013 at 6:07 PM
    The code is using a database that ships with CF as sample data. If you don't know about databases, well, um, best I recommend is picking up a CF book (like the one I helped write ;)

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead.

Leave this field empty