A reader asked me earlier this week if I could show an example of search, with paged results, under the jQuery Mobile framework. I whipped up a simple demo in five or so minutes. This is not a testament to my coding ability, but rather to just how fracking cool jQuery Mobile is. Before I begin though so important disclaimers. This was built using jQuery Mobile Alpha 3. If you are currently wearing a jetpack then you are reading this in the future and should expect that the code I show here may not quite work the same in the final version of jQuery Mobile. Also - I've been using jQuery Mobile for all of one month. Take my "solution" with a huge grain of salt. Make that two grains just to be safe. Ok, enough with the disclaimers, let's get to work.
I began by creating a simple home page for my demo. (Note - I'm not going to cover every little detail of how jQuery Mobile works here - for that, please consult the docs.)
<div data-role="page" data-theme="e"> <div data-role="header">
<h1>Art Search</h1>
</div> <div data-role="content">
<div data-inline="true">
<form action="search.cfm" method="post">
<input type="search" name="search" data-inline="true"> <input type="submit" value="Search" data-inline="true">
</form>
</div>
</div> </div> </body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Search Art</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.css" />
<script src="http://code.jquery.com/jquery-1.5.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.js"></script>
</head>
<body>
You can see I include the relevant libraries on top and my content is really just a simple form. The search form type isn't supported by all clients, but jQuery Mobile (jqm from now on) has unicorn magic and can make it work pretty much anywhere:
So - notice the form posts to search.cfm. Let's look at that.
<cfparam name="url.search" default="">
<cfparam name="form.search" default="#url.search#">
<cfparam name="url.start" default="1">
<cfset perpage = 10>
<cfif len(trim(form.search))>
<cfset search = "%" & trim(form.search) & "%">
<cfquery name="getart">
select artname, price, description
from art
where artname like <cfqueryparam cfsqltype="cf_sql_varchar" value="#search#">
or description like <cfqueryparam cfsqltype="cf_sql_varchar" value="#search#">
</cfquery>
<cfelse>
<cfset noSearch = true>
</cfif>
<cfif not isNumeric(url.start) or url.start lte 0 or round(url.start) neq url.start>
<cfset url.start = 1>
</cfif> <div data-role="page" data-theme="e"> <div data-role="header">
<a href="" data-rel="back">Back</a>
<h1>Search Results</h1>
<a href="index.cfm" data-theme="b">Home</a>
</div> <div data-role="content">
<cfif structKeyExists(variables,"noSearch")>
<p>
Please <a href="index.cfm">enter a search</a> term.
</p>
<cfelseif getart.recordCount is 0>
<cfoutput>
<p>
Sorry, there were no results for #form.search#.
</p>
</cfoutput>
<cfelse>
<cfloop query="getart" startrow="#url.start#" endrow="#url.start+perpage-1#">
<cfoutput>
<p>
#currentrow# <b>#artname#</b>
#description#
</p>
</cfoutput>
</cfloop> <div data-inline="true">
<cfoutput>
<cfif url.start gt 1>
<a href="search.cfm?search=#urlEncodedFormat(form.search)#&start=#max(1,url.start-perpage)#" data-role="button" data-inline="true">Previous</a>
<cfelse>
<!--- Didn't work... <a href="" data-role="button" disabled data-inline="true">Previous</a>--->
</cfif>
<cfif url.start+perpage-1 lt getart.recordCount>
<a href="search.cfm?search=#urlEncodedFormat(form.search)#&start=#url.start+perpage#" data-role="button" data-theme="b" data-inline="true">Next</a>
<cfelse>
<!--- See above...
<a href="" data-role="button" data-theme="b" disabled data-inline="true">Next</a>
--->
</cfif>
</cfoutput>
</div> </cfif>
</div> </div>
Ok, we've got a bit more going on here then before. The top portion handles my search. I'm using ColdFusion (of course), but any server side language would suffice. Scroll on down to inside the div with the data-role content. My first two IF blocks handle no search or no results. The final block handles outputting the results. For my pagination I used the same old code I've used in the past. The only difference is that I made use of JQM's ability to automatically turn links into buttons. For the most part this works really well. What did not work for me, and you can see it commented out above, was passing a "disabled" along. I probably could have simply used a 'grey' theme for my buttons. But for now I simply hid them. Here is an example:
Not bad, right? You can demo this here:
For the most part, I think you see that nothing special was done to make this work. JQM does so much of the work for you that I literally just had to use the right markup to get it to look pretty. If you test this in your browser, mobile or not, you will see my form post, and navigation, is all being done via Ajax. Do you see any JavaScript in my code? Any? Nope? That's right. jQuery Mobile is so powerful it could even defeat Chuck Norris probably. I decided to kick things up a notch though and work on a slightly sexier version...
This version was done using simple UL tags. You can view source to see it yourself. It's an incredibly small modification. The detail page you see took an additional 2 minutes of work.
Archived Comments
Great that you're spreading the word about jQM. I'm loving it, too!
One small caveat is that jQuery Mobile Alpha 3 does not work in Internet Explorer (any version that I've tested), but I have read that they intend to support IE 8+ and IE Mobile on jQM Beta +
You should clarify "not working" Aaron. John Resig gave a presentation at Adobe MAX during which he explains their browser ranking system (A, B, C). It's all based around whether the browser supports CSS media queries.
The code in IE is functional, but might not "look" as good as more modern browsers due to the lack of support for media queries. That's the benefit of the progressive enhancement model that jQuery Mobile has, and IMO makes it better than Sencha since it's all pure HTML.
You have a recommended search that will return multiple values? Thanks!
@Randy: I'm sorry - but your question makes no sense. My search example returns multiple values.
Sorry not to be clearer... I had tried a couple of "real word" searches and hadn't found anything that would return a result so I could see something in action... Just tried a single letter and it work great.
Thanks for your examples... always something valuable.
@Andy - in my testing, jQuery Mobile Alpha 3 returns a blank page in IE 8 & 9 when using jQuery 1.5, where 1.5.1 returns a pretty good version of jQM in IE 9. Are you not seeing the same?
Not at all. That's the whole point of progressive enhancement. Depending on how you're delivering the content, it's just HTML. Our mobile site, http://m.goba.mobi, even works on Blackberrys that can only serve plain text.
Do you have a link that you can share?
Thanks very much Ray. Gave me a great start for something I wanted to try.
Do you have the a code snippet for the UL tags in the second, 'sexier', demo? I'm new to jQM and would like to check out how you ramped it up THAT much with just 'two minutes' more work!!
@andrew...
One of the only drawbacks of jQuery Mobile is that it can be tough to view source. This is because jQm is loading subsequent pages using AJAX, then replacing the contents of the current viewport with the new page.
If you use Firefox to view Ray's demo, then check the Net tab, you'll be able to see the HTML being sent over. Here's a single row of his results:
<ul data-role="listview">
<li>
<img src="artgallery/aiden01.jpg" />
<h3><a href="detail.cfm?id=1">charles1</a></h3>
<p>Pastels/Charcoal</p>
</li>
</ul>
@andy, Thanks for your reply. That helps!
First let me say this is my first post but not my first time reading your Blog, posts and conferences about Coldfusion. I'm in love with JQM and Coldfusion and right now I'm making my first JQM+CFM tests. I'm not an expert and perhaps this is easy but I'm not find why this happens.
If I create a simple JQM page with a data-role="navbar" in the header of the page, when I move to another page the bar extend to the bottom of the screen until the next page is display. I forgot to tell this is if the page is submitted as a .cfm page. The page is a pure html without any cfm in it to discard an y other problem. The same page work if not processed by coldfusion.
Any one know why is this happens and how to avoid this.
Thanks
PD
I try to include an example here but the system flagged my comment as spam.
Sorry my spam checker blocked the link. Can you try posting the link via bit.ly instead? To be clear, ColdFusion outputs HTML. Period. CF can't "break" jqm. But - it's possible something else is being output in your request by mistake. If you use a tool like Firebug, do you see something 'extra' being sent? Maybe CF debugging? That could impact your app.
Thanks for answer my post.
I know that coldfusion outputs HTML and that is the strange thing. When I open the source processed by CFM I do not see anything wrong.
If I open this same page as a .cfm I get the problem but the same page as .htm not.
I test this in firefox, safari and iphone and get the same.
This is a Bit.ly link to the .cfm page http://bit.ly/f0PnuJ
Thanks
This is a link at the source as a text file
OOPS. :-)
this is the link as a text file http://bit.ly/hQXDEo
Ah, I see it. The bar does go big. And you say if you use the same with .html it works perfectly?
(As a warning, I'm on vacation and am about to be away from the machine for a while.)
I see something bad. This is on top of the page
<title>- MobilPR Test -</title>
It is before the doctype which should be first. You need to stop outputting that.
Wow thanks I see this file and did not see that perhaps because I was so used to set that in the Application.cfm file.
Well YES that was the problem, I do not really know why but THANKS. You do not have an idea of how many times I pass through this source. LOL
Thanks for the help and happy vacations.
Hi,
thanks for your great articles about jQuery Mobile. I've started to work on a mobile version of our site, so far so good!
I encountered a problem and I could not find anything online to give me a clue. Basically I have a listview of tags:
<ul data-role="listview"><li><a href="url-to-server-for-the-tag1">Tag 1</a></li>...(more tags)...</ul>
each tag has a url to get a list of corresponding articles from the server.
If I get as a result of this url an html mobile page there is no problem it works but what I would like is to get a JSON from our API and then use jQuery Template plugin to display them in a new page. I've been struggling with this for hours and could not find a clean solution yet. I could not find a clear answer on the net on the best way to do this. I can see jqm receiving the json from my server but then it is trying to add it as a page and I have no idea how to interrupt this...
Do you have any solution?
Thanks
So what you are saying is that you want the result of clicking a list item to be:
json info is loaded
result is printed on the page
I assume _below_ the list view, right?
yep.
click on the link
json info is loaded
(jquery listview template is fill up and insert)
result is shown in a new page (not the same to get a back later)
Wait - you want to show the results on a new page? Then why are you bothering with json? Remember that JQM will load pages in via Ajax anyway to make things a bit zipper. I'm confused as to why you want to change the default behavior here as I don't see a gain.
Yes I understand that all links are loaded through ajax but I have a REST API already available, I don't want to create new views only for the mobile version, I rather get the json and display it on the browser through jquery template plugin, it makes more sense to me and keep clear the separation.
Meanwhile I manage to make things work except that for the data-url part to make the history working properly. I am still trying to understand how jqm generate the data-url or I will simply create empty pages that I will feed with my data from the json, I think it will be easier.
sorry it's a bit confusing ;-)
Anyway, back to the ajax part, it looks strange to me to not being able to get json instead of html from jqm directly. I understand that html is usually good but when you want directly to use a json api, it does not work anymore.
So what I think you would want to do is simply register your own click events for those links. It should take priority over JQM.
So I just tested a list with 2 links.
<ul data-role="listview">
<li><a href="#page2">Page Two</a></li>
<li><a href="" id="testLink" rel="external">Test Link</a></li>
</ul>
and then I did:
<script>
$(document).ready(function() {
$("#testLink").click(function(e) {
alert("You clicked a link.");
e.preventDefault();
}
);
});
</script>
and I was able to correctly "take over" the link and do with it what I wanted.
Just now checking out your "paged search" code..
- and of course mine never works first-time!
- even though I copied everything exactly..
After trying several iterations, here is what I have currently
- http://cerberus.clearwave.c...
- When I click the search button (on my index.cfm page) I get the yellow "Error Loading Page" and the search.cfm page never displays at all?
- I tried to look at the Chrome Developer tools, but it gives back nothing valuable (that I can tell?)
- http://cerberus.clearwave.c...
What does the network tab tell you? Load the XHR request and look at the response.
It shows the following:
http://cerberus.clearwave.c...
Ok, but dude - click on it. :) If you click on it, you can see the response. It is an error (you can tell by the 500) in your CF code. Once you click you will see the error.
Ok, digging a bit deeper in the Network/Preview section:
- http://cerberus.clearwave.c...
The error is pretty clear. Your code references tblcust.name1. I assume you changed the code since the earlier link. If that was supposed to be the query then you should ensure the query name is tblcust.
Ray, you are correct.. and I also found this, where i failed to change your "getart" to my actual query name (getCust):
- <cfif url.start+perpage-1 lt getart.recordCount>
I changed it to:
- <cfif url.start+perpage-1 lt getCust.recordCount>
and it works correctly!
Once again, Thanks for the excellent code, & for pointing me to the Network/Preview screen.. I now know where to look to debug..
ps: Is there a missing "datasource=" in your code above (line 7): <cfquery name="getart">
In CF9, you can set datasource in the Application.cfc file. It applies to all queries in your application and saves you from typing it.
ah.. we are in process of turning up our new CF9 VM, but this week I only have our older CF7 to play with.. thanks for the tip..
Ray, do you have a code download for the second demo? I am particularly interested in the styling of search results page.
You want the front end code for that? You can just view source. :) If you want the ColdFusion side stuff, I can get you that.
I would like to have a look at the server side code, especially the formatting for the listview.
I have done something similar but my list was not formatting as shown in your example. I had a look at the source code to check which classes are used in the list tags (as you know some classes here are added dynamically by the jquery mobile library). Found out the class 'ui-btn-inner' was there along with other classes. I notice your source could only has ui-btn-inner. I am just curious to see why this is happening on my side.
Thanks
N
You can download the code here: http://www.raymondcamden.co....
Hi Ray. The link is giving a 404.
Change "downloads" to "enclosures" - sorry.
Ray, I notice that your Demo "saves" the original "search text" when I click the JQM back button.. or if I click the browsers Back button, but mine does not?
- I'm using a JQM back button to go back to the search page
- but mine "clears" out the original search text box?
Any ideas?
Sorry - no ideas. Is it online where I can see?
Ray, I'm looking to add "extra URL variables" to your search results page but getting zero results.. What needs to be changed in your original code to allow me to pass additional search criteria including the original search box?
- Here is what I have so far:
- Notice I have passed in 3 additional url form variables:
- I have also confirmed the 3 variables are passing to the page:
- I tried changing the OR's to AND's but still zero results:
--------------------------------------------------------
<cfparam name="url.CallType" default="">
<cfparam name="url.releaseReason" default="">
<cfparam name="url.connectedOnly" default="">
<cfparam name="url.search" default="">
<cfparam name="form.search" default="#url.search#">
<cfparam name="url.start" default="1">
<cfparam name="getMSCDR.recordcount" default="0">
<cfset perpage = 10>
<cfif len(trim(form.search))>
<cfset search = "%" & trim(form.search) & "%">
<cfquery name="getMSCDR" datasource="metaswitch_cdr">
SELECT CallType AS ct, ReleaseReason AS rr, CallingPartyAddr AS cpa, BusinessGroupName AS bgn, RequestedAddr AS ra, connected AS cc,
FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y %r') as rt, FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y') as rt2, FROM_UNIXTIME((ConnectTime/1000), '%a %m-%d-%Y %r') as cct, TO_DAYS(FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y %r'))-TO_DAYS(FROM_UNIXTIME((ConnectTime/1000), '%a %m-%d-%Y %r')) AS cl
FROM mscdr
WHERE
<cfloop index="x" from="1" to="#listLen(form.search, " ")#">
<cfset word = listGetAt(form.search, x, " ")>
<cfset word = "%" & ucase(word) & "%">
<cfif x neq 1>
and
</cfif>
(
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.CallType#">
or
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.releaseReason#">
or
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.connectedOnly#">
)
</cfloop>
</cfquery>
<cfelse>
<cfset noSearch = true>
</cfif>
<cfif not isNumeric(url.start) or url.start lte 0 or round(url.start) neq url.start>
<cfset url.start = 1>
</cfif>
fixed my typo: still zero results:
----------------------------------------
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.CallType#">
or
ucase(ReleaseReason) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.releaseReason#">
or
ucase(connected) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.connectedOnly#">
Honestly I have no idea. Remember that the generated SQL is available if you turn on CF debugging. Try that - maybe it isn't what you think it is.
The download link for the source code is broken, can you re upload
Change "downloads" in the URL to "enclosures" and that will work.
thanks, i downloaded. pls hel me check if you can help here http://stackoverflow.com/qu...
I responded.
How would you accomplish this with multiple form fields (without having to pass them each individually in the url)?
You can store it in the Session scope.
Sorry for the delay - your email got buried. I believe I can send you a zip of the old code. If you want that, email me via the contact form so I have your info.