Last week I wrote up my initial thoughts on working with NativeScript. After working through the getting started guide, I thought I'd take a stab at building a simple app, a RSS reader. Before going further, note that you should assume my code is crap. It works - but I'm sure I've done things like - well - a noob. Because I am. So I'd think twice before using my code. (Although you are welcome to it - I'll have a link to the code at the end.)

My RSS reader consists of two screens - an initial list based on the entries from an RSS feed and a detail page for the actual blog entry. Here's the initial screen.

Screen shot 1

To be clear, that lovely red header there was me using my design chops. Don't blame NativeScript for that. Anyway, here's the detail view:

Screen shot 2

That's a lot of text (partially because that blog entry really does have a lot of text at first) and not terribly nice looking, but it works. Now let's take a look at the code.

First off, the home page view, which is really just a list.


<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="loaded">

	<Page.actionBar>
		<ActionBar title="{{ title }}" />
	</Page.actionBar>

	<StackLayout orientation="vertical">
		<ListView items="{{ rssList.feedItems }}" itemTap="loadItem">
			<ListView.itemTemplate>
				<Label text="{{ title }}" horizontalAlignment="left" verticalAlignment="center" />
			</ListView.itemTemplate>
		</ListView>
	</StackLayout>
</Page>

Nothing too special here. You've got a list with a tap handler. On top you can see a call to loaded() for when the page loads. Now let's look at the code behind this.


var RssListViewModel = require('../shared/view-models/rss-list-view-model');
var frameModule = require('ui/frame');

var config = require('../shared/config');

var Observable = require('data/observable').Observable;
var viewModule = require('ui/core/view');
var page;

var pageData = new Observable({
	rssList:RssListViewModel,
	title:config.title
});


exports.loaded = function(args) {
	page = args.object;
	page.bindingContext = pageData;
	//RssListViewModel.empty();
	RssListViewModel.load();
};

exports.loadItem = function(args) {
	console.log('tap item',args.index);
	console.log('tap item 2', args.view.bindingContext.title);
	//rssList.viewModel.set('selectedItem', args.view.bindingContext);
	RssListViewModel.viewModel.set('selectedItem', args.view.bindingContext);
	frameModule.topmost().navigate('views/item-page');
}

So for the most part, this too is rather simple. Most of the logic is in a view module. This file basically handles asking the view model to do it's thing and return a list of RSS entries.

I do want to point out one thing. Notice in loadItem() I call a set operation. This is how I handle "I'm leaving this view but want to remember what I clicked." This one thing took me roughly 70% of the development time for this project. Why? At first, I was creating an instance of my view model, not just requiring it. I did this on my detail page too. That meant when I set a value on it on the list page, I lost that when the object was recreated on the detail page. That seems trivial, but it took me forever to get around that.

I also discovered later that you can pass random data to another view via navigate. You can see that described here in the docs. I didn't see that at first because when I went to the API reference in the docs, I was initially on the "Module" for Frame and not the "Class" for it. I honestly don't know the difference (I just asked on Slack though so hopefully I'll get a clue ;).

Now let's look at the detail page.


<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="loaded">

	<Page.actionBar>
		<ActionBar title="{{ title }}" />
	</Page.actionBar>

	<StackLayout orientation="vertical">
		<ScrollView>
			<HtmlView html="{{ text }}" />
		</ScrollView>
	</StackLayout>
</Page>

Again - fairly simple. I first used a TextView and of course that doesn't render HTML. I did find odd performance issues with this control. The first few views worked perfect. Then I saw a noticeable lag in rendering the view. I'd say maybe 2-3 seconds. I'm fairly certain it is probably my code, but I've let me friends on the NativeScript code know about what I encountered. Ok, so now the code behind the view.


var RssListViewModel = require('../shared/view-models/rss-list-view-model');
var Observable = require('data/observable').Observable;

var pageData = new Observable({
	title:"",
	text:""
});

exports.loaded = function(args) {

	page = args.object;
	page.bindingContext = pageData;
	
	console.log('loaded the item page');
	console.log(RssListViewModel.viewModel.get('selectedItem').title);
	pageData.title = RssListViewModel.viewModel.get('selectedItem').title;
	pageData.text = RssListViewModel.viewModel.get('selectedItem').description;

}

Basically I ask for the data I saved in the previous view and update a local observable. I had tried to bind directly to my instance of RssListViewModel, but noticed that content only updated one time. Again - that's possibly my fault.

Finally, let's look at the view model. I used one of the methods I described in my blog post on the topic - Parsing RSS Feeds in JavaScript - Options. Just in case it isn't obvious - yes - I used something in NativeScript that worked perfectly fine for Cordova too. While I may struggle a bit with the UI of NativeScript and other aspects, being able to re-use stuff I've already learned in the hybrid space is a big win. Anyway, the code:


var observable = require('data/observable');
var ObservableArray = require('data/observable-array').ObservableArray;
var fetchModule = require('fetch');
var config = require('../config');

function handleErrors(response) {
	if (!response.ok) {
		console.log(JSON.stringify(response));
		throw Error(response.statusText);
	}
	return response;
}

exports.empty = function() {
	while (feedItems.length) {
		feedItems.pop();
	}
};

exports.load = function name(params) {
	console.log('CALLING LOAD');
	//handle caching
	if(feedItems.length > 0) {
		console.log('leaving early');
		return;
	}

	return fetch('https://query.yahooapis.com/v1/public/yql?q=select%20title%2Clink%2Cdescription%20from%20rss%20where%20url%3D%22'+encodeURIComponent(config.rssURL)+'%22&format=json&diagnostics=true', {
	})
	.then(handleErrors)
	.then(function(response) {
		return response.json();
	}).then(function(data) {
		console.log('number of rss entries: '+data.query.results.item.length);
		data.query.results.item.forEach(function(feedItem) {
			feedItems.push({
						title: feedItem.title,
						link: feedItem.link,
						description: feedItem.description
					}
			);
		
		});
	});

}

var feedItems = new ObservableArray();
exports.feedItems = feedItems;

var viewModel = new observable.Observable();
exports.viewModel = viewModel;

This is - I assume - mostly self-explanatory, but let me know in the comments below if anything isn't clear. There's one more file I didn't show yet - a simple config object I can use to quickly change the title of the app and the RSS feed:


module.exports = {
	rssURL:"http://feeds.feedburner.com/raymondcamdensblog",
	title:"Raymond Camden's Blog"
}

There's two things missing from this app that I'd like to correct. First, a good mobile application should recognize when it is offline. I need to update the app to notice that, let the user know, and possibly start working again once network connectivity is restored. Secondly, many RSS feeds only contain a small portion of the entry text. I'd like to add a button that would open the entry in the native browser for proper reading.

Want the complete code? (And again, remember that it is code being written by a noob. I'd hate to be accused of leading people to bad code.) You can find the complete source here: https://github.com/cfjedimaster/NativeScriptDemos/tree/master/rssTest1.