Ionic Single View Project
In this article, we will create a "Single View" application, and we will show the list of stories with title and time; and after clicking on any story, it will redirect to the complete story. For the raw data, we will consider the “Reddit” website because we can retrieve data from Reddit without any prior authentication.
We will consider the following topics.
- Create Single View
- List of Items
- Infinite Scroll
- Pull to refresh
- Thumbnail, title and details of stories
- Create AngularJS Controller
- Load JSON data using $http service
- Add Cordova Plugin
- Add JavaScript Library: Moment.js
- Add Side Menu for NEWS categories
- Add Tab for NEWS type.
Before start working on the application, let’s first visit Reddit.
Reddit is a social stories aggregation, web content rating, and discussion website. Reddit's registered community members can submit content, such as text posts or direct links. Wikipedia.
You can select any subreddits, such as games, IOS, Hindi, or pics.
Funny Subreddit
To get data for any subreddit, just append the “.json” at the end of URL. It will return the data for subreddit in JSON format.
Now, we can use the data for our application. Let’s start working on this project. First of all, create a blank layout application and name it “redditApp”.
After installation, open the project in any IDE; I am using Visual Studio. After opening the project in any IDE click on the Index.html page. This is the first and most important page of the project.
First of all change header title from “Ionic Blank Starter” to “Reddit App”.
Create controller and get data from Reddit API.
Now we create a controller and using the “$http” service we retrieve the data from Reddit Api.
Controller
- var appMod = angular.module('starter', ['ionic']);
- appMod.controller('redditCtrl', function ($scope,$http) {
- $scope.STORIESContent = [];
- $http({
- method: 'GET',
- url: 'https://www.reddit.com/.json'
- }).then(function successCallback(response) {
- console.log(response);
- }, function errorCallback(response) {
- console.log(response);
- });
-
- });
Output
In the above screen, we called the $http service and retrieved the data from Reddit in JSON format, this data contains all required information like URl, Author Name, thumbnail, and title. Now, we will show this data in View.
Controller
- var appMod = angular.module('starter', ['ionic']);
- appMod.controller('redditCtrl', function ($scope,$http) {
- $scope.STORIESContent = [];
- $http({
- method: 'GET',
- url: 'https://www.reddit.com/.json'
- }).then(function successCallback(response) {
- angular.forEach(response.data.data.children, function (child) {
- $scope.STORIESContent.push(child.data);
- });
-
- }, function errorCallback(response) {
- console.log(response);
- });
-
- });
View(Index.html)
- <body ng-app="starter" ng-controller="redditCtrl">
-
- <ion-pane>
- <ion-header-bar class="bar-stable">
- <h1 class="title">Reddit App</h1>
- </ion-header-bar>
- <ion-content>
- <div ng-repeat="data in STORIESContent">
- <a ng-href="{{data.url}}" target="_blank">{{data.title}}</a>
- ({{data.domain}})
- <span>{{data.num_comments}} Comments</span>
- <span>Submitted By: {{data.author}} </span>
- <span>Score: {{data.score}} </span>
- </div>
- </ion-content>
- </ion-pane>
- </body>
Output
In the above code, we retrieve the data from Reddit API and show the title, author name, URL, comments, and score of the stories. Now, we add some CSS components to improve the look of our app.
First of all, we add a header. To change the style of header, we add the “bar-balanced” class for the header.
We will use the below style of the list.
We modify our previous code to get the above look for the application.
Code
- <body ng-app="starter" ng-controller="redditCtrl">
-
- <ion-pane>
- <ion-header-bar class="bar-balanced">
- <h1 class="title">Reddit App</h1>
- </ion-header-bar>
- <ion-content>
- <div ng-repeat="data in STORIESContent" class="list" style="margin-bottom: 2px;">
- <a class="item item-thumbnail-left item-text-wrap" ng-href="{{data.url}}" target="_blank" >
-
- <img src="{{data.thumbnail}}" ng-if="data.thumbnail.startsWith('http')"/>
- <p> <h2 class="positive story-title">{{data.title}}</h2>-({{data.domain}})</p>
-
- <p>Submitted By: {{data.author}} </p>
-
- <p>{{data.num_comments}} Comments; Score: {{data.score}}</p>
-
- </a>
-
- </div>
- </ion-content>
- </ion-pane>
- </body>
After using the above code now our application will look like below.
Add JavaScript Library
Now, we want to show how long ago the story was published, for this we can use created_utc property that will show the time in seconds.
You can see that it represents the number of seconds since Jan 1 1970, now we want to show this time similar to how the Reddit web site does it.
There are two methods to do this. The first one is that we create a custom code that will convert these seconds into a proper time format that we want or we can use any pre-built JavaScript library. I think the second method is best. So now we will learn how to install a JavaScript library in our project. For our application we us the “monment.js” library. For this go to the “Momentjs.com” website.
This JavaScript library is used to convert a time count into a proper time format.
GitHub also provides Moment.JS directive for Angular.JS, that will be easy for us to use in out application. Now we go to GitHub and download the required files for Moment.JS directives.
After successful installation add the below code into the index.html page.
- <script src="lib/moment/moment.js"></script>
- <script src="lib/angular-moment/angular-moment.js"></script>
Add “angularMoment” dependency in Module.
Add the below code in Index.html page.
Now time will appear in the below format.
Adding Infinite Scroll
Now we are getting only 25 stories at a time, but what if we want to see the older stories? So now we add an infinite scroll; using that we can read the older stories Let’s read the documentation of Reddit API.
If you read the API documentation of Reddit then you will find that we can load after or before stories by specifying the name of stories. Let’s check this statement.
Load new stories published after this story. To get new stories you can use pass before the parameter and to get the old stories you can use pass after the parameter value. Here we pass https://www.reddit.com/.json?before=t3_5afcrc that will return new stories published after stories with the name “t3_5afcrc”. We pass the “after” parameter value in our application to get the older stories.
Use Ionic Infinite Scroll API
Now we use the Ionic infinite scroll API, you can get more information about Ionic infinite scroll
here. Now we add the required components in Html page and controller section as defined in documentation.
Html Code
- <body ng-app="starter" ng-controller="redditCtrl">
-
- <ion-pane>
- <ion-header-bar class="bar-balanced">
- <h1 class="title">Reddit App</h1>
- </ion-header-bar>
- <ion-content>
- <div ng-repeat="data in NEWSContent track by data.id" class="list" style="margin-bottom: 2px;">
- <a class="item item-thumbnail-left item-text-wrap" ng-href="{{data.url}}" target="_blank" >
-
- <img src="{{data.thumbnail}}" ng-if="data.thumbnail.startsWith('http')"/>
- <p> <h2 class="positive story-title">{{data.title}}</h2>-({{data.domain}})</p>
-
- <p>Submitted <span am-time-ago="data.created_utc| amFromUnix"
- am-preprocess="unix"></span> by: {{data.author}} </p>
-
- <p>{{data.num_comments}} Comments; Score: {{data.score}}</p>
-
- </a>
-
- </div>
- <ion-infinite-scroll on-infinite="loadMore()"
- distance="1%">
- </ion-infinite-scroll>
- </ion-content>
- </ion-pane>
- </body>
Controller
- var appMod = angular.module('starter', ['ionic', 'angularMoment']);
- appMod.controller('redditCtrl', function ($scope,$http) {
- $scope.NEWSContent = [];
- $scope.loadMore = function () {
- var param = {};
- if ($scope.NEWSContent.length > 0) {
- param['after'] = $scope.NEWSContent[$scope.NEWSContent.length - 1].name;
- }
- $http({
- method: 'GET',
- url: ' https://www.reddit.com/r/all/',
- params:param
- }).then(function successCallback(response) {
- angular.forEach(response.data.data.children, function (child) {
- $scope.NEWSContent.push(child.data);
- });
- $scope.$broadcast('scroll.infiniteScrollComplete');
- }, function errorCallback(response) {
- console.log(response);
- });
- };
-
-
- });
Above html and JavaScript code provide a functionality of infinite scroll, when you reach the bottom of the page it will fetch 25 stories that were published before the last story.
Add Ionic Refresher
Now we add a refresher in our app, using this refresher we will get new stories. For more information about the refresher read the documentation of Ionic refresher.
Now we add <ion-referesher> element in our code.
Html
- <body ng-app="starter" ng-controller="redditCtrl">
-
- <ion-pane>
- <ion-header-bar class="bar-balanced">
- <h1 class="title">Reddit App</h1>
- </ion-header-bar>
- <ion-content>
- <ion-refresher pulling-text="Pull to refresh..."
- on-refresh="doRefresh()">
- </ion-refresher>
- <div ng-repeat="data in NEWSContent track by data.id" class="list" style="margin-bottom: 2px;">
- <a class="item item-thumbnail-left item-text-wrap" ng-href="{{data.url}}" target="_blank" >
-
- <img src="{{data.thumbnail}}" ng-if="data.thumbnail.startsWith('http')"/>
- <p> <h2 class="positive story-title">{{data.title}}</h2>-({{data.domain}})</p>
-
- <p>Submitted <span am-time-ago="data.created_utc| amFromUnix"
- am-preprocess="unix"></span> by: {{data.author}} </p>
-
- <p>{{data.num_comments}} Comments; Score: {{data.score}}</p>
-
- </a>
-
- </div>
- <ion-infinite-scroll on-infinite="loadMore()"
- distance="1%">
- </ion-infinite-scroll>
- </ion-content>
- </ion-pane>
- </body>
Controller- $scope.NEWSContent = [];
- $scope.doRefresh = function () {
- var param = {};
- param['before'] = $scope.NEWSContent[0].name;
- var oldStory = [];
- oldStory = $scope.NEWSContent;
- var newStory = [];
- $http({
- method: 'GET',
- url: 'https://www.reddit.com/.json',
- params: param
- }).then(function successCallback(response) {
- angular.forEach(response.data.data.children, function (child) {
- newStory.push(child.data);
- });
- $scope.NEWSContent = [];
- angular.forEach(newStory, function (data) {
- $scope.NEWSContent.push(data);
- });
- angular.forEach(oldStory, function (data) {
- $scope.NEWSContent.push(data);
- });
-
- $scope.$broadcast('scroll.refreshComplete');
- }, function errorCallback(response) {
- console.log(response);
- });
- };
After adding the above code if we refresh(Pull Down) the screen then it will send a request to the Reddit API for the latest stories and show you on the screen.
In the above image we pull down the screen to get the latest stories, in the result form we get two new stories showing on the top.
So far our application is working properly but if we click on any story then it will open the story in a new tab. It can work in a general web browser but when we build this application for Android or IOS then it will cause an error because Android and IOS use the WebView that is a kind of browser for mobile but it refused to open any external link. To resolve this issue we use the “inappbrowser” Cordova plugin.
Run “Ionic plugin add cordova-plugin-inappbrowser” command to add the “inappbrowser” plugin.
After adding the Plugin now we make a change in index.html and app.js .
In index.html remove href from anchor tag and add the ng-click directive as below.
App.js
Add openPage function in App.js
And make some modification in run method
In the above code we changed the window.open for a mobile device, when we run this app in a mobile device it will open a link in “InAppBrowser” and in case of regular web browser it will not insert in “if” section and open the link in a new tab.
Let’s build the application and check if all these modifications work or not.
When we click on any link it will open that story in InAppBrowser.
Add Side Menu
So far we are getting stories only for “ALL” category, how much better would it be if we could get stories for other categories like Funny, Pics etc. Now we add side menu in our application and insert categories in this menu like below.
To use side menus add <ion-side-menus> that will encompass all pages that have a side menu, <ion-side-menu-content> for the center content, and one or more <ion-side-menu> directives for each side menu(left/right) that you wish to place. For side menu element add your element in <ion-side-menu> like below.
Html Code
- <body ng-app="starter" ng-controller="redditCtrl">
- <ion-side-menus>
- <ion-side-menu-content>
-
- <ion-nav-bar class="bar-stable">
- <ion-nav-buttons side="left">
- <button menu-toggle="left" class="button button-icon button-clear ion-navicon ">
-
- </button>
- <div class="title title-center header-item" >{{title}}</div>
- </ion-nav-buttons>
-
- </ion-nav-bar>
- <ion-pane>
- <ion-header-bar class="bar-balanced">
-
- </ion-header-bar>
- <ion-content>
- <ion-refresher pulling-text="Pull to refresh..."
- on-refresh="doRefresh()">
- </ion-refresher>
- <div ng-repeat="data in NEWSContent track by data.id" class="list" style="margin-bottom: 2px;">
- <a class="item item-thumbnail-left item-text-wrap" ng-click="openPage(data.url)">
-
- <img src="{{data.thumbnail}}" ng-if="data.thumbnail.startsWith('http')" />
- <p> <h2 class="positive story-title">{{data.title}}</h2>-({{data.domain}})</p>
-
- <p>
- Submitted <span am-time-ago="data.created_utc| amFromUnix"
- am-preprocess="unix"></span> by: {{data.author}}
- </p>
-
- <p>{{data.num_comments}} Comments; Score: {{data.score}}</p>
-
- </a>
-
- </div>
- <ion-infinite-scroll on-infinite="loadMore()"
- distance="1%">
- </ion-infinite-scroll>
- </ion-content>
- </ion-pane>
- </ion-side-menu-content>
- <ion-side-menu side="left">
- <ion-content>
- <div class="list">
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/all/.json')">
- <i class="icon ion-clipboard"></i>ALL</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/funny/.json')">
- <i class="icon ion-social-foursquare-outline"></i>FUNNY</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/pics/.json')">
- <i class="icon ion-images"></i>PICS</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/angularjs/.json')">
- <i class="icon ion-social-angular-outline"></i>Angularjs</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/node/.json')">
- <i class="icon ion-social-nodejs"></i>NodeJ </a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/blog/.json')">
- <i class="icon ion-ios-compose"></i>BLOG</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/books/.json')">
- <i class="icon ion-ios-book-outline"></i>BOOKS</a>
- <a class="item item-icon-left" ng-click="Load_SubReddits('https://www.reddit.com/r/food/.json')">
- <i class="icon ion-fork"></i>FOOD</a>
- </div>
- </ion-content>
- </ion-side-menu>
- </ion-side-menus>
-
- </body>
After all these changes now we need to make changes in controller; replace controller code with below code.
- appMod.controller('redditCtrl', function ($scope, $http) {
- $scope.title="ALL";
- $scope.NEWSContent = [];
- $scope.Load_SubReddits = function (url_) {
- $scope.NEWSContent = [];
- $scope.title = url_.split('/')[4].toUpperCase();
- $http({
- method: 'GET',
- url: url_
- }).then(function successCallback(response) {
-
- angular.forEach(response.data.data.children, function (child) {
- if (!child.data.thumbnail|| child.data.thumbnail === 'self' || child.data.thumbnail === 'default') {
- child.data.thumbnail = 'http://www.redditstatic.com/icon.png';
- }
- $scope.NEWSContent.push(child.data);
- });
- $scope.$broadcast('scroll.infiniteScrollComplete');
- }, function errorCallback(response) {
-
- console.log(response);
- $scope.$broadcast('scroll.infiniteScrollComplete');
- });
- };
- $scope.openPage = function (url) {
- window.open(url, '_blank');
- }
- $scope.doRefresh = function () {
- var param = {};
- param['before'] = $scope.NEWSContent[0].name;
- var oldStory = [];
- oldStory = $scope.NEWSContent;
- var newStory = [];
- $http({
- method: 'GET',
- url: 'https://www.reddit.com/r/' + $scope.title + '/.json',
- params: param
- }).then(function successCallback(response) {
- angular.forEach(response.data.data.children, function (child) {
- newStory.push(child.data);
- });
- $scope.NEWSContent = [];
- angular.forEach(newStory, function (data) {
- $scope.NEWSContent.push(data);
- });
- angular.forEach(oldStory, function (data) {
- $scope.NEWSContent.push(data);
- });
-
- $scope.$broadcast('scroll.refreshComplete');
- }, function errorCallback(response) {
- console.log(response);
- });
- };
- $scope.loadMore = function () {
- var param = {};
- if ($scope.NEWSContent.length > 0) {
- param['after'] = $scope.NEWSContent[$scope.NEWSContent.length - 1].name;
- }
-
- $http({
- method: 'GET',
- url: 'https://www.reddit.com/r/' + $scope.title + '/.json',
- params:param
- }).then(function successCallback(response) {
-
- angular.forEach(response.data.data.children, function (child) {
- if (child.data.thumbnail === 'self' || child.data.thumbnail === 'default') {
- child.data.thumbnail = 'http://www.redditstatic.com/icon.png';
- }
- $scope.NEWSContent.push(child.data);
- });
- $scope.$broadcast('scroll.infiniteScrollComplete');
- }, function errorCallback(response) {
-
- console.log(response);
- $scope.$broadcast('scroll.infiniteScrollComplete');
- });
- };
-
-
- });
In this code we add Load_SubReddits function , in this function we have a Url_ parameter that contains the URL of the category. Using this parameter value we get the data from Reddit.
According to the selected menu items stories will be fetched and content of the lists will be changed.
Add Tab for sub categories
If you go to Reddit 's official website then you will see that NEWS related to a single category is also divided into further sub categories like hot, new, rising, top etc.
Now we add a tab into our app and use this tab to get the NEWS for subcategories. We add only four items (HOT, TOP, NEW, RISING) into our tab section.
You can read Ionic tab documentation for more details about the Ionic tab.
Now add four variables in your controller for the subcategory.
- $scope.hotURL = '';
- $scope.topURL = '';
- $scope.newURL = '';
- $scope.risingURL = '';
After making the above changes now Update the Load_SubReddits method as below.
- $scope.Load_SubReddits = function (url_) {
- $scope.NEWSContent = [];
- $scope.title = url_.split('/')[4].toUpperCase();
- $http({
- method: 'GET',
- url: url_
- }).then(function successCallback(response) {
-
- angular.forEach(response.data.data.children, function (child) {
- if (!child.data.thumbnail|| child.data.thumbnail === 'self' || child.data.thumbnail === 'default') {
- child.data.thumbnail = 'http://www.redditstatic.com/icon.png';
- }
- $scope.NEWSContent.push(child.data);
- });
- $scope.$broadcast('scroll.infiniteScrollComplete');
- $scope.hotURL = 'https://www.reddit.com/r/' + $scope.title + '/hot/.json';
- $scope.topURL = 'https://www.reddit.com/r/' + $scope.title + '/top/.json';
- $scope.newURL = 'https://www.reddit.com/r/' + $scope.title + '/new/.json';
- $scope.risingURL = 'https://www.reddit.com/r/' + $scope.title + '/rising/.json';
- }, function errorCallback(response) {
-
- console.log(response);
- $scope.$broadcast('scroll.infiniteScrollComplete');
- });
- };
In the above code we are generating the URL for the sub categories as any main category will be changed; then values of subcategories also change.
Index.html
Add below code in your index.html page below the “ion-nav-button”.
- <div class="tabs tabs-icon-top">
- <a class="tab-item" ng-click="category(hotURL)">
- <i class="icon ion-contrast"></i>
- HOT
- </a>
- <a class="tab-item" ng-click="category(topURL)">
- <i class="icon ion-star"></i>
- TOP
- </a>
- <a class="tab-item" ng-click="category(newURL)">
- <i class="icon ion-ios-flame-outline"></i>
- NEW
- </a>
- <a class="tab-item" ng-click="category(risingURL)">
- <i class="icon ion-ios-speedometer-outline"></i>
- RISING
- </a>
- </div>
After making the above changes now our app will look like below.
Now you can see that in our application’s tab we have four buttons. These buttons work as sub categories of a main category. For each subcategory we added a category function on click event and in this function we are passing the absolute url of the NEWS topics. For example when we click on “Top” subcategory for AngularJS then the URL will be
https://www.reddit.com/r/angularJS/hot/.json.
Now we add the category function in our js file and the code of the category function is below.
- $scope.category = function (url_) {
- $http({
- method: 'GET',
- url: url_
- }).then(function successCallback(response) {
- $scope.NEWSContent = [];
- angular.forEach(response.data.data.children, function (child) {
- if (!child.data.thumbnail || child.data.thumbnail === 'self' || child.data.thumbnail === 'default') {
- child.data.thumbnail = 'http://www.redditstatic.com/icon.png';
- }
- $scope.NEWSContent.push(child.data);
- });
-
- }, function errorCallback(response) {
-
- console.log(response);
-
- });
- }
In this example, we are getting the content for subcategory of any topic and showing it in the list. Congrats! this is the last step to complete this application.
Conclusion
In this article, first of all we created a single view, and after that we fetched out the data from Reddit API and showed in a list. In the next step we added an infinite scroll that allowed us to load infinite data. In further steps we added a refresh button, and using this button we can refresh our application screen and load the latest NEWS. We also added a side menu and in this side menu we inserted some categories of news and at last we added a tab and in this tab we added four sub-categories for a main-category.
I hope you liked this article. If you have any doubt or questions, then write in comment sections. Thanks for reading the article.