Welcome to my third entry for my (what was at first) simple PhoneGap RSS reader. If you haven't yet, please be sure to read part 1 and part 2 so you have some context about how this application works. In this part, I'm going to tackle two enhancements suggested to me by my readers:
- First - support rendering the entries if the user is offline.
- Second - clean up the UX a bit so that when a user views an entry, leaves, and then comes back to another entry, they don't see the old text there for a split second.
Let's tackle the offline question first. I spent some time thinking about this and tried a few things that didn't quite work out the way I wanted. The first thing I tried was checking navigator.onLine. (See this Stackoverflow entry for details.) This did not work well for me. When I switched my device to airplane mode it still reported me as online. I then looked into PhoneGap's Connection API. This kinda worked. It didn't seem to grok my Airplane mode at all. It certainly didn't report it as online (it returned a null actually) and I could have handled it, but I then had the issue of how I was going to handle coordinating the deviceready event along with the jQuery Mobile pageinit method.
Then I realized something. I already had an Ajax error handler. And it worked. That's obvious of course, but it occurred to me. Why not use this error handler? It would not only support offline mode, but any error on the remote server as well. At the end of the day, if I can't get the RSS feed, who cares if I'm offline or if the server is down. I could see caring, and if so, you would obviously want to use the PhoneGap Connection value, but I figured, why not go with the simple route.
As for storage - that turned out to be trivial - LocalStorage. Have you guys figured out yet that I really love HTML5 LocalStorage?
So, I decided to get rid of AjaxSetup. I only have one Ajax call in the entire application, so why not tie it to that call. So I switched from a $.get to a $.ajax:
$.ajax({
url:RSS,
success:function(res,code) {
entries = [];
var xml = $(res);
var items = xml.find("item");
$.each(items, function(i, v) {
entry = {
title:$(v).find("title").text(),
link:$(v).find("link").text(),
description:$.trim($(v).find("description").text())
};
entries.push(entry);
});
//store entries
localStorage["entries"] = JSON.stringify(entries);
renderEntries(entries);
},
error:function(jqXHR,status,error) {
//try to use cache
if(localStorage["entries"]) {
$("#status").html("Using cached version...");
entries = JSON.parse(localStorage["entries"])
renderEntries(entries);
} else {
$("#status").html("Sorry, we are unable to get the RSS and there is no cache.");
}
}
})
This is - for the most part, the same code as before, just using the core $.ajax method. You can see where the error function will look into LocalStorage for the cached copy, and where the success function always stores a copy. The renderEntries function is simply an abstracted out version of the display code:
function renderEntries(entries) {
var s = '';
$.each(entries, function(i, v) {
s += '<li><a href="#contentPage" class="contentLink" data-entryid="'+i+'">' + v.title + '</a></li>';
});
$("#linksList").html(s);
$("#linksList").listview("refresh");
}
Woot. That works. Now for the next request. We want to ensure that users don't see the old content when loading in the RSS entry detail page. This turned out to be a bit weird. jQuery Mobile has logic to say, "Do this when a page is hiding, or before it hides", but for the life of me (and please correct me if I'm wrong) there doesn't seem to be a good way to get the page that is leaving. You get passed the page you are going to, but not the current page. I really feel like I'm missing something here, so please note this may get corrected later. For me though I simply added an event listener to the main page. It now sees if a previous page exists, and if so, clears out the text:
$("#mainPage").live("pagebeforeshow", function(event,data) {
if(data.prevPage.length) {
$("h1", data.prevPage).text("");
$("#entryText", data.prevPage).html("");
};
});
And that's it. I've included the entire JavaScript file below (the HTML hasn't changed from the previous entry) and a zip of the entire project may be downloaded for the low cost of free.
//EDIT THESE LINES
//Title of the blog
var TITLE = "ColdFusion Jedi";
//RSS url
var RSS = "http://feedproxy.google.com/RaymondCamdensColdfusionBlog";
//Stores entries
var entries = [];
var selectedEntry = "";
//listen for detail links
$(".contentLink").live("click", function() {
selectedEntry = $(this).data("entryid");
});
function renderEntries(entries) {
var s = '';
$.each(entries, function(i, v) {
s += '<li><a href="#contentPage" class="contentLink" data-entryid="'+i+'">' + v.title + '</a></li>';
});
$("#linksList").html(s);
$("#linksList").listview("refresh");
}
//Listen for main page
$("#mainPage").live("pageinit", function() {
//Set the title
$("h1", this).text(TITLE);
$.ajax({
url:RSS,
success:function(res,code) {
entries = [];
var xml = $(res);
var items = xml.find("item");
$.each(items, function(i, v) {
entry = {
title:$(v).find("title").text(),
link:$(v).find("link").text(),
description:$.trim($(v).find("description").text())
};
entries.push(entry);
});
//store entries
localStorage["entries"] = JSON.stringify(entries);
renderEntries(entries);
},
error:function(jqXHR,status,error) {
//try to use cache
if(localStorage["entries"]) {
$("#status").html("Using cached version...");
entries = JSON.parse(localStorage["entries"])
renderEntries(entries);
} else {
$("#status").html("Sorry, we are unable to get the RSS and there is no cache.");
}
}
});
});
$("#mainPage").live("pagebeforeshow", function(event,data) {
if(data.prevPage.length) {
$("h1", data.prevPage).text("");
$("#entryText", data.prevPage).html("");
};
});
//Listen for the content page to load
$("#contentPage").live("pageshow", function(prepage) {
//Set the title
$("h1", this).text(entries[selectedEntry].title);
var contentHTML = "";
contentHTML += entries[selectedEntry].description;
contentHTML += '<p/><a href="'+entries[selectedEntry].link + '">Read Entry on Site</a>';
$("#entryText",this).html(contentHTML);
});
Archived Comments
By the way, I made one more small change. I'm loading jQuery 1.6.4 instead of 1.7. 1.7 isn't supported in jQuery Mobile yet.
Hey Raymond. Nice work and useful articles. Local storage and PhoneGap - these are some of the things I want to read about.
I wanted to ask you, since you tested lot of Adobe mobile technologies, are you satisfied with jQuery Mobile performances.
Some things work better than in AIR but jQuery transitions take time. They are not instant, takes 1 second to load another page, item renderers work nice and some things look much easier to develop than in mobile AIR. Just my 2 cents.
Cheers,
Ivan
I know page transitions are something the JQM folks are working on improving. But for now, you can totally turn them off:
http://forta.com/blog/index...
Hey! As far as I can tell this doesn't take care of pictures, right? I did follow your suggestions you posted as a comment on the second part. I still needed a todatauri plugin as the javascript implementation on android apparently doesn't have that builtin. It worked on small pictures, but not on larger ones. But even when I overrode the image size and had it all included in the html-stream,the android browser simply refused to show datauri images. S that was the end of that, I guess... Let me know if you attempt to fix this somehow.
@Johannes: You mean in terms of being offline? No - it would not handle that. I think it's ok not to as folks can still see the content.
Hi Raymond
First af all I'm a newbie so my skills aren't that good at all.
I've dowloaded the attached file from above.
I've tried with several feeds, some works, the most of them doesn't. I'm not sure if the not are supported or not. But one thing make me very uncertain and that is why I got this messege "Sorry, we are unable to get the RSS and there is no cache" when I try with the feed you included in the file. (var RSS = "http://feedproxy.google.com...";)
Do you know why? At this moment I've only tried to run this on my PC /Win 7, browser Chrome) or do I HAVE to run it inside Phonegap?
Best regards Niclas
It _has_ to be via PhoneGap. Regular JS apps cannot make a cross domain request. Ie, a file on foo.com cannot load an XML feed from goo.com. There are ways around this (JSON/P, or uses your own server side code to proxy it), but for the most part, the answer is no. :)
Hi Raymond
Thanks a lot. I've got an iPhone and an iPad but no Mac. I really have to get a Mac so I can try Phonegap :)
Do you have any ideas why http://blip.tv/nettuts/rss works? It just a feed I got online, it is not mine or anything.
//N
No idea. It should be impossible. :)
Hey again, you were so helpful in part 2 of course I had to come back for part 3!
Love the app so far but only one complaint. When reading an article (at least on android 4.0.3) and you hit the back button it closes the app vs returning to the home page.
http://wiki.phonegap.com/w/...
It wasn't too difficult to fix but I'm sure others will be looking for this at some point.
Also another thing I ran into was 4.0.3 has issues with phonegap and screen rotation. It pretty much kills the app. Had to modify the AndroidManifest.xml and force portrait to fix this.
<activity
android:label="@string/app_name"
android:name=".AppNameActivity" android:screenOrientation="portrait">
Otherwise its still awesome! v4 to tackle push/pull notifications?
Odd, the back button for me (Android 3.something) works ok, so maybe it's just 4.x. Ditto rotation.
Anyway... sorry to be dense, but what do you mean by push/pull notifications?
http://wiki.phonegap.com/w/...
and
http://developer.android.co...
Adding the notifications when the feed has been updated would make the app a bit more useful.
So for that, you need something that runs in the background. I know apps don't always close when you - well, close them - so it's possible you can "hope" for that. I don't believe, stress, believe, you can set up a PG app so that it can notice something even when not running.
ahh, touche! it'd be unrealistic to expect something like that out of phonegap :-P but I did find this gem
https://github.com/saileshm...
Also, I thought I had the back button issue resolved but apparently I made it worse for some users :-\
http://stackoverflow.com/qu...
It actually creates errors for me so I wasn't able to use this method to "fix" it. Thoughts?
1) My understanding (and again, let me stress I'm mostly talking out my rear here), is that this plugin too will require the app to actually be run by the user first. It's nice like the Twitter app, for example, which will run after you reboot the phone.
2) I'm ashamed to say it, but I didn't know navigator.app.* existed. I just now asked on the forums where this is documented.
Hi Raymond,
Thanks for this code.
I tried it on a Galaxy S2 and the scrolling "jumps" a little bit, it's not smooth, do you also have the same problem ? Do you know where it could come from ?
Thanks,
Hary
Hmmmm, no, I haven't seen that. I know that on Android, sometimes page transitions can be a bit icky. They are working on fixing that asap.
Hey Any ideas why i am unable to images & the full content in the app?
Here's my feed url
http://feeds.feedburner.com...
I am using wordpress on my original site. Any ideas on how to fix this?
What images? What content? You said full content, right? So I assume the titles work, but when you click, you don't see the entry, is that right?
I am sorry for the confusion.
Your feed looks like this
http://feeds.feedburner.com...
And mine looks like this
http://feeds.feedburner.com...
I have enabled full feeds but when you view the xml version only the start description shows up with no image.
I am asking if the app can be modified to read the hidden <[CDATA thing?
OR
How do i optimize my feed so that it shows the complete feed with images.
Any Ideas?
When I view source on your feed, I see HTML, not XML.
i have the same problem. i want to read out an <item> with a field named <content-encoded> that includes the content in a <![CDATA field. ... this code:
entry = { title:$(v).find("title").text(), link:$(v).find("link").text(), description:$.trim($(v).find("content:encoded").text())
doesnt work :(
best regards
stefan
Oh, I ran into this before. Let me whip up a quick demo.
Ok, for me, .text works perfectly fine. Whipping up a quick blog demo and will post the link.
you mean .text() works with content-encoded?
my rss feed looks like this:
<item>
<title>MT...</title>
<pubDate>Thu, 09 Feb 2012 13:24:05 +0100</pubDate>
<link>https://...</link>
<guid>https://...</guid>
<content:encoded><![CDATA[Fachbereich:]]></content:encoded>
</item>
and i want to read out the line with content-encoded?!
Quick example code for you -
http://www.raymondcamden.co...
Ah, you didn't say that (actually, you probably did, and I missed it), I thought the issue was ONLY CDATA. Let me update my example.
Ok, I knew I remembered this. See:
https://plus.google.com/115...
and specifically:
http://www.steveworkman.com...
Hi, this code is working fine on Android 2.3. But on Android 2.2, the only field properly displayed from the feed is PubDate :
pubDate: $(v).find("pubDate").text()
Other fields (title, description) are ignored and not displayed. Any idea of a workaround on Android 2.2?
All I can suggest is some debugging. If you console.log the values, what are you seeing.
Awesome tutorials, all 3. If you're looking for something to add in case there's a part 4, consider adding multiple feeds with a feed menu as the front page. Such as "view news from source 1" and "view news from source 2."
I've tried to set a new main page and set load a url into the RSS variable based on which link the user clicks, but I'm obviously borking it somewhere, so now I'm selfishly asking for help by suggesting it as a blog post :)
Either way, thanks for the tips so far!
At this point, debugging is showing that .ajax() is returning an [Object Element] on Android 2.3 and a [HTML Element] on Android 2.2, with the very same code. On Anroid 2.2, most of the elements in the result are empty (except PubDate), and I didn't manage to understand why.
I ended up using Google Feed API, which is much faster to execute, really easy to use and more reliable across platforms. The only drawback is that if the feed contains HTML, all "class" and "id" properties are removed. Here is the code to insert in your JS file, instead of your $.ajax() call:
var feed = new google.feeds.Feed("http//insert_your_feed_URL_here");
feed.setNumEntries(10);
feed.setResultFormat(google.feeds.Feed.JSON);
feed.load(function(result) {
if (!result.error) {
var entries = [];
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
// Do whatever you want with each entry
entries.push(entry);
}
localStorage["entries"] = JSON.stringify(entries);
} else {
// If nos results in the feed
}
});
and add this in index.html:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("feeds", "1");
</script>
To get the "class" and "id" properties back, I tried switching to a mixed result (JSON + XML) using "google.feeds.Feed.MIXED_FORMAT" parameter. The idea is to set entry.content from JSON with the value of <content:encoded> from the XML part of the result, as XML stores the full HTML without any cleanup.
I tried adding the 2 following lines in the FOR loop above:
entry.content = result.xmlDocument.getElementsByTagName('item')[i].getElementsByTagName("content:encoded")[0].firstChild.nodeValue; // Get <content:encoded> content and replace entry.content with full HTML of the item
entry.xmlNode = null; // To prevent a circular JSON Object
But it throws an error. It seems that <content:encoded> is not found. Console log shows: "Uncaught TypeError: Cannot read property 'firstChild' of undefined"
If somebody finds a way to make this work, it would be fantastic. For the moment, I stick with this solution, as it gives really good results with both Android 2.2 and 2.3. Moreover, response time is much better (maybe my custom server providing the feed is not that fast).
@Raymond: Feel free to use this code for a Part 4 ;) Parts 1,2 and 3 helped me a lot to understand how to parse a feed and to use the results. Thanks!
@Jesse: It won't be available terribly soon, but I'm building something like this for my jQuery Mobile book. In fact, it's pretty much _exactly_ that. Technically you should buy the book, but all the code will be available via Github too.
@Vince: Didn't know about the Google Feed API. It looks like your getting JSON back - that's sweet. All things being considered, you almost _always_ want to work with JSON, not XML.
Hi,
It has been driving my crazy for 3 days and I need to express it here :D So your code works perfectly side alone. However, when I try to link it from another html file, it seems not to work. Meant that I already have my index.html, and your code is feed.html, I try to call feed.html in the list in index.html as below:
<ul data-role="listview" data-inset="true">
<li><a href="feed.html"><img src="images/feed.png" width="25" height="22" class="ui-li-icon" title="Feed">Feed</a></li>
When the link is triggered, it shows only the head and footer. Not the content at all.
Do you have any ideas on this issue?
Hi again,
For the comment above, I have found a way to work around it.
The main.js needs to be included in the index.html not feed.html in order to make it work. I found an useful post that said jquery will create another 'blank' page with the used js and css in its parent.
It leads to another issue that, reanderentry function will not work to create a viewable frame for each entry.
Sorry to ask again, but how do you think about this? :)
Peter,
try using rel="external" on your link
@Ivan,
Thank for your comment, that works! Also found another way to do it is to disable ajax by data-ajax="false" :)
While that works, I'd do what you did first - include the JS code in the first page. Unlike a "web" JQM site, your PhoneGap apps always start off with a root URL.
Hey Ray,
first off, this is awesome. Thanks for posting the RSS series.
Here's the question. I'm looking to present each RSS feed entry in its own container. Much like a pinterest board.
Second, i would also like to include several RSS Feeds under their own tab. How do i do that.
FYI> I am mashing the Foundation HTML5 Responsive Framework with your RSS JQuery Mobile framework. So far so good, just looking for a bit more adaptation.
Thanks a million again. Mike
The first question is easier to answer. Instead of a UL with LIs for each entry, just use <p>entry</p> for each one. Or use a <div> that you style whatever way you want.
Tabs or another issue though. I haven't found a good "tab" solution for mobile. The other issue too is size. What do you o when you have 20 feeds? You don't want 20 tabs at the bottom.
Here's my HTML markup. within the body tag. As of now, all the RSS entries pull in as a list. Can you explain the <p>entry</p> so it works dynamically? Again, thanks.
<!-- Panel -->
<div class="row">
<div class="twelve columns">
<div class="panel">
<!-- RSS Feed -->
<div data-role="content">
<div id="status"></div>
<ul id="linksList" data-role="listview" data-inset="true" data-split-icon="gear" data-split-theme="d">
</div>
<!-- RSS Feed -->
</div>
</div>
</div>
Well, instead of
s += '<li><a href="#contentPage" class="contentLink" data-entryid="'+i+'">' + v.title + '</a></li>';
You would change the li to p. And instead of the ul id="linklist", you can change it to a simple div with an ID of whatever you want. Then you inject it with
$("#someid").html(s)
i should clarify. There are only 5 or 6 feeds. Each with about 20 posts displayed at a time. The feeds are randomly updated throughout the day.
oh, one more thing. How would one display an img within the #mainpage list?
Also, the Foundation Framework works really well for tabs. Check it out.
http://foundation.zurb.com/
re:pictures - This was discussed in the comments in part 2:
http://www.raymondcamden.co...
Like This?
main.js >
$(".contentLink").live("click", function() {
selectedEntry = $(this).data("entryid");
});
function renderEntries(entries) {
var s = '';
$.each(entries, function(i, v) {
s += '<p><a href="#contentPage" class="contentLink" data-entryid="'+i+'">' + v.title + '</a></p>';
});
$("#pinBoard").html(s)
$("#pinBoard").listview("refresh");
}
html >
<!-- Panel -->
<div class="row">
<div class="twelve columns">
<div class="panel">
<!-- RSS Feed -->
<div data-role="content">
<div id="status"></div>
<div id="pinBoard"></div>
<!-- RSS Feed -->
</div>
</div>
</div>
<!-- Panel -->
Yep, but get rid of this:
$("#pinBoard").listview("refresh");
That tells jQuery Mobile to refresh a list view, which you don't have anymore.
Groovy. Thanks Raymond.
Great tutorials, I've been looking for exactly this for quite awhile. However I'm working on a project that breaks for some reason when I add jQuery Mobile. Would it be possible to implement this without jQuery Mobile?
Absolutely. All jQuery Mobile does is make crap pretty. I don't mean that to belittle JQM, I love it, but at the end of the day, it's role is to help design the mobile friendly site. It's not required at all.
Raymond, thanks for the reply. I tried removing jQuery Mobile from your example and it stops working, is there something that I'm missing?
Well, my code makes use of JQM to render the entries. You would need to modify that. You asked if this could be done w/o JQM, and of course, it can, but you can't just remove the library. You would need to modify some of the code too.
Thanks again for the reply. I'll try to figure it out.
Hi Raymond,
First of all, this is awesome. Thanks for posting the RSS series.
I am newbee of jquery. I want to use your code in my application in that such a way:
I wrote an application using phonegap wizard. I want to make a link to your code via a button click such as:
<li><a href="#api-rss">RSS Feed</a></li>
When i copy and paste your code in my div where id's is "api-rss", the code doesn't work because your code include "pageinit" function. How can i adapt your code into a div section? Please help me.
Best regards.
PS: Sorry my bad English, I hope you understand me.
The pageinit event is used by jQuery Mobile. I have no idea what framework, if any, your PhoneGap wizard uses, but if it isn't including it, then it won't work.
To be clear, jQuery Mobile is NOT required for this type of app. I used it to handle the display. You could use my logic in a non-jQuery Mobile app. The changes for that would be a bit too much for the comments section though.
Fantastic rss reader! Thank you so much.
When I try to use jquery mobile's theme roller with your page the theme does not work and the page instead just uses the stock themes that come with jquery mobile. Any advice on fixing that?
Did you download the right bits from themeroller? Can you share some of your code (via pastebin)?
Jason, I deleted your comments. All you need to do is post the URL, not any HTML.
http://pastebin.com/raw.php...
Try that link, sorry.
Looks reasonable to me. Best I can suggest is double checking which swatch you modified and ensure you are using it. You can also open your stuff up in Chrome and use DevTools to inspect the DOM.
Hey Raymond,
I want to make a app who can read 2 RSS feeds.
This is my first RSS feed:
http://www.hapdedag.nl/dagh...
It is a newsfeed who displays all the news correctly with your RSS reader.
Secondly i got a Food Feed:
http://www.hapdedag.nl/exte...
I want to display the restaurant and if people click on it they see the dish of the day and some more information.
How do i get these 2 RSS Feeds working???
The first one, the news feed is working but I do not know how to fix the other RSS Feed correctly???
I can think of a few different ways of doing it.
One would be to simply have your app start with a list of two items - one for RSS feed one, the other for the second.
Clicking that leads to the list of entries just for that feed.
Another way would be to get the RSS feeds for both and merge them together. That would require a bit more work.
Yeah i want to make it simple.
But what is the best way to do it??
Make 2 JS files? Or 1 JS file with more functions??
There is no reason to add additional JS file. You can add a new html page to show your 2 rss feeds and then have them link to the page that will show the proper detail.
I know! I got a second HTML file but the 2 RSS files are totaly different.
Do i need to copy and rename all the variables and functions or can I skip some??
Um well give it a shot man. At this point I've described the process in general. You need to take a stab at the work yourself.
Got it working!
But i got only 1 problem.
It is a app for a meal of the day.
Now the app shows alle the meals from the whole week.
How do i fix this?
Looks at the feed:
http://www.hapdedag.nl/exte...
You can write some code to compare the data in the XML to the current day. Do some quick searching for JavaScript Date functions. It should be easy.
Hmmm... i am a beginner in javascript.
I need the getDate() function and compare with <day>7</day> in XML...
function renderfoodEntries(foodentries) {
var s = '';
var d = new Date();
if(d.getDate == v.day){
$.each(foodentries, function(i, v) {
s += '<li><a href="#contentfoodPage" class="contentfoodLink" data-entryfoodid="'+i+'">' + v.restaurant + '</a></li>';
});
$("#foodList").html(s);
$("#foodList").listview("refresh");
}
}
Something like this???
Err, almost. You want the date comparison INSIDE the each loop, not outside it. Also, getDate is a method, so you would have getDate().
This function also not works:
function renderfoodEntries(foodentries) {
var s = '';
var d = new Date();
var n = d.getDate();
$.each(foodentries, function(i, v) {
if(n == v.day){
s += '<li><a href="#contentfoodPage" class="contentfoodLink" data-entryfoodid="'+i+'">' + v.restaurant + '</a></li>';
}
});
$("#foodList").html(s);
$("#foodList").listview("refresh");
}
Do I need to put more into my JS???
If, inside your loop, you console.log both n and v.day, what do you see?
As I said I am a beginner in Javascript how do i console.log n and v?? console.log(v)???
In webkit enabled browsers and Firefox+Firebug, you can use console.log("...") to send messages to the console. Lots of examples here:
https://www.google.com/sear...
Okay I know Firebug or the inspector from Google Chrome but not what line i have to put into the loop.
$.each(foodentries, function(i, v) {
I'm not quite sure what you mean. As I said, you want to use console to log the value of n, which is the date, and v.day. Compare them.
A Phonegap and Jquery mobile application. The Background/splash image takes too long to transit to the main application and all html,css, JavaScript are packaged with the app. Any solution to reduce the splash screen to about 4 seconds before it transit to the main page?
"to about 4 seconds" - how long are you seeing? I only see a splash screen of a maybe 1-2 seconds.
the time varies sometimes over 20seconds , i dont know if it depends on the network, bt like i said previously all assets a loaded in the app
Please any solution to this problem?
Ohhhh - so your issue is possibly with the amount of time it takes to get and parse the RSS. So you are seeing a loaded page, but with no content, while it gets the RSS. True?
In regards to your last comment Chuba, I respond when I can. It is Saturday so may get not quick responses.
Thanks for the reply. its Json. am seeing the splash screen for too long Before the main page loads even wen it fetches nothing from the internet
So wait - if you try a new PhoneGap project with no code and just simple html, like <h1>Hello</h1>, are you seeing a long amount of time for _that_ to render?
its faster
So yeah then - I think your issue is the network call to get and load the RSS. So maybe the solution is to make it more obvious that it is loading crap when the page is shown?
wow does it have to take that long to load files that is available in the deployed app already? ..cuz it doesnt connect to the internet
Err, well now wait. As you said, when you did a simple example with no remote calls, it loaded faster. Right? When you use my code, which has to use the network to fetch the RSS, it was slower.
That is -expected-.
What may be the issue though is that the app is not being vocal about what is doing when it loads up. We could improve that.
I can say in my testing it ran fast, but obviously, it depends on the RSS feed you are trying to load, network conditions, etc.
Any app that needs the network for it's data is going to have that "problem" - there isn't much you can do about it. (Outside of what I said above - ensuring there is good user feedback and you can employ caching too).
k Thanks appreciate
@STEFAN:
You need only this:
content:$(v).find("[nodeName='content:encoded']").text()
But what i am not understand is why all my <p> Tags do nothing in my Application when i get the full Post via content:$(v).find("[nodeName='content:encoded']").text()
Nice, now i got everthing working quite well.
Only one Question:
How can i put $.mobile.pageLoading(); and $.mobile.pageLoading( true );
into this script ?
Thanks.
Um... put it in? Just do it. :) Maybe you need to clarify more what you mean.
Hey Raymond! I've been using your tutorial for a phonegap app I'm building and it's extremely helpful. Although I already have written android apps in Java, apparently some employers want experience with Phonegap. I've been using your tuts to hop on this bandwagon and with your help I'm almost there.
Cheers!
I see you have gravatars enabled. Hi there!
Glad you like them. If you have any suggestions for other tutorials, let me know.
Hello Raymond,
because of my last question.
All what i want to do is to add a loading cycle for the time the rss news will be synct.
I tryed a lot right now with $.mobile.pageLoading(); and $.mobile.pageLoading( true );
but with no results :(
Would be very nice if you can give me a tip :)
I'd need to see a bit of the code. Basically, you would call pageLoading _before_ the get() call and clear it _after_ it gets loaded.
Hey Raymond,
Although I'm getting further and further with my app, it doesn't function properly when I click a feeditem. It loads mainPage but it doesn't show the feeditem or comes up with gibberish.
I've followed your tutorial step by step (part 1- 3) but I can't find out what I'm doing wrong. If I set up the app with the quickstart guide from phonegap and use your tutorial should it just 'work' or am I still missing something? Could I send you my eclipse project or is that too much to ask?
Cheers!
Could be anything. But sure - go ahead and send your project, I'll try importing it. No promises on how quickly.
Raymond,
nevermind haha. I accidentally added the contentPage div two times to index.html. It loads the feeditem perfect but loads a blank contentPage immediately after it has been loaded.
Above comment can be removed if you want =)
Cheers!
I like leaving mistakes like that around - you won't be the first to make it. :)
Hey Ramond,
The next thing I want to do is to show a twitterfeed in my app. I guess I can use this script pretty much in the same way for this, amiright?
I´ll be using this (for example) http://search.twitter.com/s...
Sure yeah - Twitter has a RSS format. If I'm remembering wrong, then you just have to adjust for the different XML.
What an awesome resource! Thanks so much for posting this.
For my app, I tweaked the code in the main.js file to allow the "Read Entry on Site" to use the Childbrowser plugin in PhoneGap so I can view my blog without launching the web browser, plus I'm stuck using the WebView preference for an iFrame on one of my pages to keep it from wigging out. Since WebView is turned on, sending links to the web browser has not been possible... or at least I don't know how to make it happen. (Rookie at Javascript but learning.)
Anyway, here's my code for the childBrowser plugin:
contentHTML += '<p/><a href="#" onclick=window.plugins.childBrowser.showWebPage("'+entries[selectedEntry].link + '")>Read Entry on Site</a>';
I would like to use the Childbrowser plugin for other links that show up in my feed. Can you tell me how to make this happen?
Thanks in advance - G
You will want to ensure you trap any click events in the list of feed data. That is possible via jQuery (let me know if you need help with that).
Let me start by saying that this tutorial has been great. It's exactly what I've been looking for to complete an app.
There is one interesting problem that I have. Peter in one of the earlier comments said that he was having trouble putting the feed on a different page than index.html. I put the main.js reference in my index file and the feed appears on the page (latest.html), but the links don't do anything when they are clicked.
I tried adding the rel="external" based on another comment, but that didn't work either. Do you have any insight into this?
By links do you mean the titles or do you mean the "Read Entry on Site" link?
Whenever I click the titles. It will light up but then it won't go anywhere. I put a console.log into the script to figure out if it fired. It knows when it's clicked but won't open the page with the text and the link that says "Read Entry on Page".
Not sure then- can you post your code to pastebin please?
I actually finally diagnosed the problem. It's the data-role="pages" attribute. I tried it on my subpage without referencing your code and that doesn't work either, so that would explain why nothing popped up. I'm going to work with this today.
It's not your code -- it's my own lack of knowledge with JQuery and PhoneGap.
Ok cool- just glad you got things working.
I do need help. This is in reference to my post on 05-14-2012 at 11:37 PM. I replied via email, but don't think that worked.
My content is coming from blogger and using feed burner to convert the RSS to RSS2. When my feed comes to my app, any text or pictures that have links tied to them open up in WebView on my iOS device and I don't have a header with a back button to get back to the previous screen. I have to run the app with WebView enabled as I have an iFrame on one of my #pages that launches Safari when the app loads and enabling WebView is the only way I can prevent that from happening.
Here's my feed address: http://feeds.feedburner.com... so you can test.
All my code is on GitHub at https://github.com/jjgleim/....
Hello Raymond,
first of all, thanks a lot for the tutorial, it was really a big help!
I am trying to get the <media:thumbnail> from my RSS-Feed in the App, but unfortunately none of my pitifully attempts has been successful :/
Could you give me some hint how this could work, please?
Thanks a lot in advance!
@G-Man: I think I saw your email - I just haven't been able to go through my queue. Hopefully another person can possibly help out too since it is on Github. I'll try if I can get the time - just not sure I will soon.
@A-Z: You may need to ensure your JS code handles it right - maybe:
title:$(v).find("media:thumbnail").text(),
but I don't know if .text() is right for you - if that XML element has subkeys you will need to use a different way to get to it.
Hello Raymond, and thank you for your rapid reply!
The xml is simple, looks like this:
<item>
<title> </title>
<link> </link>
<description> </description>
<pubDate> </pubDate>
<media:thumbnail height="" url="" width="" />
</item>
and in the .js I tried something like this
media:$(v).find("media:thumbnail").text(),
or this
media:$(v).find("media:thumbnail").attr("url"),
The first one causes absolutely nothing, the second one "undefined"… ?
It is quite frustrating to be honest, because actually it should work! :(
Try this:
var ray = $(v).find("media:thumbnail");
console.log(ray);
See if it shows something.
Ok, I am not sure if I am putting it on the right place, but there is no reaction when it comes after the entries.push(entry); and as soon as I put it up, no matter where in the $("#mainPage").live("pageinit", function() {… or above or under it, then neither my list is displayed…
Here the modified code, perhaps you see something I don't?
...
function renderEntries(entries) {
var s = '';
$.each(entries, function(i, v) {
s += '<li><a href="#contentPage" class="contentLink" data-entryid="'+i+'">' + '<div class="navtitel">' + v.title + '</div>' + '<div class="small-nav">'+ v.pubDate + '</div>' + '</a></li>';
});
$("#linksList").html(s);
$("#linksList").listview("refresh");
}
//Listen for main page
$("#mainPage").live("pageinit", function() {
$.ajax({
url:RSS,
success:function(res,code) {
entries = [];
var xml = $(res);
var items = xml.find("item");
$.each(items, function(i, v) {
entry = {
title:$(v).find("title").text(),
link:$(v).find("link").text(),
pubDate:$(v).find("pubDate").text(),
description:$.trim($(v).find("description").text()),
};
entries.push(entry);
entries.reverse();
var ray = $(v).find("media:thumbnail");
console.log(ray);
});
//store entries
localStorage["entries"] = JSON.stringify(entries);
renderEntries(entries);
},
error:function(jqXHR,status,error) {
//try to use cache
if(localStorage["entries"]) {
$("#status").html("Using cached version...");
entries = JSON.parse(localStorage["entries"])
renderEntries(entries);
} else {
$("#status").html("Sorry, we are unable to get the RSS and there is no cache.");
}
}
});
});
$("#mainPage").live("pagebeforeshow", function(event,data) {
if(data.prevPage.length) {
$("h1", data.prevPage).text("");
$("#entryText", data.prevPage).html("");
};
});
//Listen for the content page to load
$("#contentPage").live("pageshow", function(prepage) {
//Set the title
$("h1", this).text(entries[selectedEntry].title);
var contentHTML = "";
contentHTML += '<div class="bg-txt-s">';
contentHTML += '<h3>'+ entries[selectedEntry].title + '</h3>';
contentHTML += entries[selectedEntry].ray;
contentHTML += '<div class="small">' + entries[selectedEntry].pubDate + '</div>';
contentHTML += entries[selectedEntry].description;
...
In the future, please use pastebin for big code blocks. Makes it easier to read then in here. You need to put it in the EACH block, ie, after this:
$.each(items, function(i, v) {
The line above is run for each of the items in the XML. Now - another thing to consider is that not all of your items may have a thumbnail.
Oh sorry, I didn't think about the readableness of the code, my mistake!
Ok, tried it the other way, still not working unfortunately… I am sure that every entry has got a thumbnail, here the whole construct of the xml and the url in <media:thumbnail url="" > is always filled with an image url:
http://pastebin.com/1ysXTsiZ
And just to make sure, I made not more failures, here also the code of my main.js:
http://pastebin.com/NPKuqe0e
Thanks a lot for your effort!
Ok, me again ;)
I tried changing the <media:thumbnail> in my .rss to <media> and in the main.js I added ray:$(v).find("media").attr("url"),
and now at least the url of the image is displayed! That could been seen as a little progress perhaps, yippie! :)
Sorry, I am an absolute newbie in this field, this is my first app at all...
Do you think I should rather try on this way with the <media> and the .attr("url"), and if so, what would be the wisest way?
It works! I just have to put
contentHTML += '<img src="';
contentHTML += entries[selectedEntry].ray;
contentHTML += ' "/>';
in my main.js, and the pictures are there!
Thanks for your help! :)))
Glad you got it. One thing I'd recommend - to everyone - is to consider the Google Feed API. It allows you to parse XML and return a simple array of objects. It is MUCH easier than working with XML. It even includes a JSON/P service. For the jQuery Mobile book I wrote, I did a RSS example and made use of that instead. A heck of a lot cleaner.
Good morning Raymond, one last question, I hope:
do you see any possibility to use a feed which doesn't end with .rss in the app? Like a Yahoo Pipes feed (ends with ;_render=rss") or a Flickr feed?
It should not matter what the extension is. The JS code requests the URL and uses it. It should just work.
Hey Raymond,
Alright, I got the RSS feed working exactly as I wanted except for one thing. When they tap the entry, I want it to show everything in the description tag. For example, if you look at the source code of my feed ( http://www.lambdachi.org/ca... ), the description tag has the text for the entire post. However, in Firefox and in your RSS feed, it shows "Continue Reading". Any ideas on how to show the full tag?
-Sandy Meers
When I view that RSS, it says continue reading too. That's the content of the RSS feed. So I'm a bit confused. If you want the entire post, the RSS data has to include the entire post. (You can build another way to get the full post of course.)
Look at your blog provider. (I say "your" because I assume this is your feed.) Some blogs provide a 'short' RSS feed and some provide a full entry version.
Figured it out finally! I guess a long weekend finally knocked some sense into me...
I needed to use my RSS2 feed (/feeds/rss2), and that generated the content in the source code. I modified your Ajax call as well. Where you looked for description:$.trim($(v).find("description").text()), I changed the part in quotes to "encoded". RSS adds a <content:encoded> tag around the content and looking for just encoded gave me the near-close result that I was looking for.
Again, thanks for all of your help and for the great tutorial!
Cool. I strongly recommend the Google Feed API still though. I probably need to do a follow up.
Hey Raymond,
With your help my app is running superb. With a lot of tweaking it is now showing the thumbnail and title of a the items (directly from my custom Wordpress feed) on mainPage and featured images, attached images (if any), title and content on page blogContent.
I came up with another idea. What I'd love to have is more pages which only show the items in certain categories (my feed now also shows the category in which the item was posted). Could you help me or point me in the good direction?
Thanks!
There are a few ways to handle this (well, probably many!), here are some basic ideas.
1) How do you do more pages? That is up to the UI framework you use. You could use tabs at the bottom of the screen for categories - but that would be a bit limited width wise. You could have a button that linked to a page that then listed categories. Again, there's different ways of doing this.
2) And of course -your category list is dynamic. Your blog may add the "Raymond Camden" category one day. Do you try to support that or do you get by with hard coded categories? You could simply use the categories from the RSS feed, but that's going to be dependent on your current content. If my last 10 entries are JUST on ColdFusion, then you wouldn't know that I have a jQuery category as well. And heck - even if you cache - people remove categories too. I did in my last redesign.
3) Probably the easiest aspect would be the filtering. Just use XPath. Of course, most blog engines support category specific RSS feeds, so you would simply switch to that URL.
Hey Ray,
I'm using JqueryMobile. I already ran into the problem of limited width. I fixed it with using custom buttons in custom CSS without any JQMobile.
I want three extra pages. The first one, (home (or mainpage)) which show all the items in my feed. The second, third and fourth need to show items which are in the main feed but are rarely posted but interesting enough to have an own page (these categories have a lot of views compared to other categories). My problem is not the feed but more the javascript itself, I don't know where to start. Could you give me a simple example of javascript (main.js) where one page shows category X and one page shows category Y? I'll be able to work it out from there. At the moment I don't know how to let certain pages load certain items of the feed.
Thanks :)
Well, I can talk about it at a high level.
My code works by listening to pageinit for mainPage. Basically, "When this page is created, do this." When you add other pages, you can add your own code for them. You could- if you wanted -almost cut and paste my existing code and simply use a different RSS url (the category specific one).
I hope that helps. If I feel the code makes sense for a blog entry, I will write it, but right now, I don't think I will. As I mentioned, I think my next entry will be an update to the latest PG code base, latest JQM code base, and make use of the simpler Google Feeds API.
I actually did an example of this for my jQuery Mobile book. This code is open source here:
https://github.com/cfjedima...
Of course, for a detailed explanation you need to buy the book:
http://www.amazon.com/dp/18...
Hey Ray, I have nearly finished my first app, but now I am running against a wall… I want to output the entries in reverse order in the list, but when I try entries.reverse(); it just makes a mess of the order. Any idea?
Thanks!
You got me there. Where are you doing the reverse? Can you post to pastebin your code?
I put it directly after the entries.push(entry), here the code:
http://pastebin.com/EbMRYtFh
You put the reverse call in the each() block. That means your calling it every time you add an item. You need to put it immediately _after_ the each block.
I could have sworn that I already tried it out at that position, but then I probably had forgotton the semicolon or made some other ridiculous little neglect...
Thanks a lot, of course it works! :)
Hey Raymond,I am a beginner with phonegap and jquery,and I tried your awesome app but it couldn't work,it just show the title and "Sorry, we are unable to get the RSS and there is no cache."
Here are some questions:why there are no
"<script type="text/javascript" src="cordova-1.8.0.js"></script><script type="text/javascript">"
in the index.html file?
why can't the program work properly for me?Does it because I missed some setting in elipse or jquery?Thanks for your help.
1) why there are no
"<script type="text/javascript" src="cordova-1.8.0.js">
Almost always you need the Cordova JS file to speak to the hardware. But this application doesn't need it. It actually doesn't do anything device specific at all.
2) "why can't the program work properly for me"
No idea. You will need to do some debugging on your side.
Sorry to bother you again.I open the main.js with system editor and it says in line 11
$(".contentLink").live("click", function()
"Object Expected"
Please help me.
By the way,I used Android 2.2 version
You get that error where? You said your system error, but are you testing on the mobile device?
I ran it on the Android Visual Device 2.2,and it just showed the titles...
So the issue is with clicking to view the entries then?
It only shows "ColdFusion Jedi" and "SimpleBlog by Raymond Camden",there are nothing for me to click...
So what do you see in console? Please read this blog entry:
http://www.raymondcamden.co...
After I ran the Console told this:
ActivityManager: Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.camden.rssdemo/.RSSDemoActivity }
And I just read your blog and filter the "Web Console" and "Console",nothing there
Nothing stands out to me - at this point - I'd have to say I don't know what to tell you - and to go further would require some one on one - which would be a paid engagement. Sorry.
That's ok,thank you all the same,I'll try it myself:)
Hi,it's me again:)I got it worked and thank you so very much.
By the way,the jquery-moblie-1.0 has a "flash back" problem,that is when clicking a link it will flash back to the mainpage ,then go to the next page,after update to jquery-moblie-1.1,this problem is solved,hope someone would find this useful:)
Hey Raymond, thanks you for sharing this app for us.But this app couldn't work not only on the emulator but also my device.they all show the title and "Sorry, we are unable to get the RSS and there is no cache."
But when I put the .xml file in to the project file and open it locally, it works fine.
I wonder it may be the cross-domain problem,can you give me some suggests,thanks.
Have you tried adding the RSS URL to the whitelist?
Thanks a lot.It really works!
hi,Ray.I have tested your code in eclipse platform for two days,unfortunately,it did not work.My android version is android 2.2?when I load code,it just displayed "SimpleBlog by Raymond Camden Home" .I am new to js,so I do not know how to debug that code.Can you give me some suggestions? thanks !
Sam,
This is a topic that is quite important to me. As I've blogged more on client side development over the past few years, I've seen a growing number of people like yourself. (That sounds a bit derogatory - please do not take it as such.) There is a serious lack of knowledge in regards to how to debug issues with JavaScript applications and mobile apps as well. Because of this I've begun work on a presentation to cover the basics. The topic is too much for this blog post, but it is sorely needed. For now what I'm recommending is some basic Googling on your part in terms of debugging JavaScript. There -is- a lot of content out there. In the meantime, I can say that when my presentation is done (I'm giving it to two places next month), I'll be giving it online as well so please subscribe to my blog for updates.
I feel like I'm letting you down here a bit - but at the same time - I feel like it is just too much for this entry now and it makes sense to address it more fully in a place of it's own.
Ray,thanks for replying ,I will learn to debug as soon as possible.
hi,Ray,it works,thanks.
Hi, I hava some problem with the attached file.
I think it miss some important files. So Can send an E-mail for me.(yjjxwx@qq.com or yjjxwx@sina.com)
I am a Chinese boy, study in a collage.
I am very poor in English, many information what I don't know how to say.
I am sorry for that.
I just checked the zip and everything is there. If you can tell me something specifically you think is missing, I'll try to find it for you.
Thank you!
I have this working perfectly on my local machine, but when I upload the files to my server and go to index.html, I get the following error: "Sorry, we are unable to get the RSS and there is no cache." I am not sure how to trouble shoot this problem.
Any suggestions would be helpful.
Your uploading the files to your server? This isn't meant to be run as a 'normal' web site, but rather as a PhoneGap application. If you run it as is, you will have the 'Same Domain' issue, where a file on foo.com can't do an XHR to goo.com. There are ways around this of course, but my main concern is with you trying to run it on your server.
For people trying to acces the content between the <![CDATA tags, you should use content:$.trim($(v).find("encoded").text())
Thanks for this tutorial, helped me a lot!
I try to reproduce this, except I'm using a JSON feed instead of regular RSS xml feed.
When feed is ok, use localStorage to store entries.
Please, take a look: http://jsfiddle.net/elektro...
Why I'm i getting only one entry, and this one is near the end of the list. Why only this one?
This line:
entries = [];
is inside your each loop, which means the array is getting destroyed each time. Move it to before the each loop.
Thank you so much Raymond, thanks for all your work by the way. Great tutorial. Now I have my feed OK. Great !
Hi raymond ive try your code and its work in android but not in blackberry app, can you help me?
I don't normally test in Blackberry, sorry. I'd maybe try the latest version of this app (see Part 5). You could also tell me more about how it doesn't work. What do you see - etc.
hi raymond i've try part 5 and deploy to blackberry app and its work, can you give me an example server side that using coldfusion code ? and what do you think is it the right methode to develop mobile who get data from remote site ? can you give me suggestion ?
Thank You
Budi
"can you give me an example server side that using coldfusion code"
Your question doesn't make sense. If you are asking how to handle RSS in general with CF, there is a <cffeed> tag.
"and what do you think is it the right methode to develop mobile who get data from remote site ?"
That is a bit broad. Libraries like jQuery make it relatively easy to load remote data. CF is also simple to use for generating JSON data.
Hai Raymond, i have a question about your rss.
How to show all title of the article? for example title is bla bla bla, not bla bla....
please answer my question raymond. Thanks before :).
Thats jQuery Mobile truncating it. I'd check their docs to see if that can be disabled, and of course if you aren't using jQM then it won't be a problem.
Ok thanks raymond. i will build a aplication about automatic summarization from article in rss, what should I do first ? do you have links to tutorials about it ? I'm sorry for your trouble :(. Thank you so much Raymond if you want help me :(.
Automatic summarization? Like taking an article and making a summary from it? You would need a powerful system to do that. I'm not aware of an API you can use for that. I do know of an API that will do text analysis (keywords, reading level, etc), but it won't summarize it for you.
I'm making a thesis about automatic news summarization. I was a dead end :'(.
yes thats true, so I can not do it? :'( :'(.
I'm not saying you can't do it - just that I don't know of an API you can call to do it for you. You could write the logic yourself I suppose.
ok raymond thank you so much for your answer. :)