Code

The final release of Ionic 2 is out and building modern hybrid mobile apps has become easier and more powerful. Ionic 2 is a complete re-design and re-implementation of the great Ionic framework and is now based on Angular 2. With that new version the Ionic team has improved performance, reduced the complexity of code you need to write, and made it possible to build completely bespoke apps with your existing web development skill set.

In this tutorial you’ll walk through a practical sample and build a real-world Ionic 2 mobile app with external API access.

What We’re Going To Build

The mobile Ionic 2 app we’re going to build in this tutorial can be seen in the following:

From the start page of the app you ca use the tab navigation menu at the bottom to switch to Profiles page and Contact page. If you click on the Profiles link you’ll see the following screen:

Enter a GitHub user name and click on button Search to retrieve profile information from the GitHub web service.

A new section becomes visible showing some profile details. You can click on the link Public Repos to display a list of repositories which belong to the current GitHub user profile.

In the list of repositories you can simply tap on an item to change to the details page:

To retrieve user profile and repository data the app uses GitHub REST web services. For sending HTTP request to the web service endpoint we’ll make use of the Angular 2 Http service.

Setting Up The Project

The first step is to initiate a new Ionic 2 project. This can be done by using the Ionic CLI in the following way:

$ ionic start iongithub —v2

If you have not installed Ionic CLI you need to perform the installation first by using the Node.js Package Manager NPM:

$ npm install -g ionic cordova

If the Ionic 2 project has been ionitialized successfully, you can enter the new project directory iongithub and start the development server by typing in:

$ ionic serve

After the development web server has been started the Ionic 2 application is loaded in the browser:

Sebastian Include screenshot of default tabs application

Adding A Service For Accessing The GitHub API

As our application should retrieve profile and repository data from the GitHub API, the best idea is to provide a service for that task which contains the code which is needed to communicate with the external API.

Generating a Service

A new service class is generated by using Ionic CLI once again in the following way:

$ ionic g provider github-service

This command generates a new file github-service.ts in folder src/providers.

Implementing The Service

Let’s use the service class template in github-service.ts to implement the GitHub API access.

import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; /* Generated class for the GithubService provider. See https://angular.io/docs/ts/latest/guide/dependency-injection.html for more info on providers and Angular 2 DI. */ @Injectable() export class GithubService { baseUrl: String; constructor(public http: Http) { this.baseUrl = 'https://api.github.com/users/' } getProfile(username){ return this.http.get(this.baseUrl + username) .map(res => res.json()); } getRepos(username){ return this.http.get(this.baseUrl + username + "/repos") .map(res => res.json()); } }

The first thing you may notice is that we’re importing the Angular 2 Http service by adding the corresponding import statement. Having imported Http makes it possible to inject this service into the class constructor. We’re using the public keyword to create a http member variable at the same time, so that we’re able to access this service instance throughout the class.

The base URL of the GitHub API is https://api.github.com/users/. This URL is stored in the class member baseURL.

To access the GitHub API two methods are implemented:

getProfiles(username) : This method sends a HTTP GET request to the service endpoint for retrieving user profile data.

: This method sends a HTTP GET request to the service endpoint for retrieving user profile data. getRepos(username): This method sends a HTTP GET request to the service endpoint for rerieving repositories for a given user profile.

The GET request is initiated by calling the get method of the Http object and passing in the service endpoint as a parameter. The get methods an Observable which makes it easy to deal with the asynchronous response from the server. By using the map operator from the RxJS library we’re able to transform the server response object into JSON format:

.map(res => res.json());

Calling the json method on the response object parses the result of the GET request into JSON format.

Adding The Service to AppComponent

Next, we need to make the service available to the pages and components of our app. To do so open the implementation of AppComponent in file src/app/app.component.ts. Add the following import statement on top of the file first:

import { GithubService } from '../providers/github-service';

Next we’re able to add GithubService to the array which needs to be assigned to the providers property. This property is added to the @Component decorator which is attached to class AppComponent:

@Component({ templateUrl: 'app.html', providers: [GithubService] })

By adding GithubService to the providers array we make sure that the component injector becomes aware of this provider. Now the injector is able to resolve GithubService when we’re injecting our service class into a component constructor. As we’re configuring the injector on AppComponent level, we’re making sure that GithubService can be injected to all sub components.

Implementing Profiles Page

In the next step let’s add and implement ProfilesPage. ProfilePage will fullfill the following requirements:

Search for GitHub user profiles

Display details for GitHub user profile

Display list of user profile repositories

First add this new page by using Ionic CLI in the following way:

$ ionic g page profiles

This command creates three new files in src/pages/profiles:

profiles.html

profiles.ts

profiles.scss

If you open files profiles.html and profiles.ts you’ll find a default Ionic 2 page implementation. Let’s extend that default implementation with the following code in profiles.ts

import { Component } from '@angular/core'; import { NavController, NavParams } from 'ionic-angular'; import { GithubService } from '../../providers/github-service'; import { RepoDetailsPage } from '../repo-details/repo-details'; /* Generated class for the Profiles page. See http://ionicframework.com/docs/v2/components/#navigation for more info on Ionic pages and navigation. */ @Component({ selector: 'page-profiles', templateUrl: 'profiles.html' }) export class ProfilesPage { profiles: any; repos: any; github_user = ""; constructor(public navCtrl: NavController, public navParams: NavParams, private githubService: GithubService) {} ionViewDidLoad() { } onSubmit () { this.getProfile(this.github_user); this.github_user = ''; this.repos = ''; } showRepos (github_user) { console.log("Show Repos : " + github_user); this.getRepos(github_user); } reset() { this.profiles = ''; this.repos = ''; this.github_user = ''; } getProfile(username) { this.githubService.getProfile(username).subscribe(response => { this.profiles = response; console.log(this.profiles); }); } getRepos(username) { this.githubService.getRepos(username).subscribe(response => { this.repos = response; console.log(this.repos); }) } repoTapped(event, repo) { this.navCtrl.push(RepoDetailsPage, { repo: repo }); } }

First we’re adding an import statement for GithubService. With the import of GithubService available we’re able to inject GithubService into the class constructor. Again, by using the private keyword we’re assigning the GithubService object to the class variable githubService at the same time.

On class level the following variables are defined:

profiles

repos

github_user

We’ll use profiles to store the current profiles object in JSON format which is retrieved from the GitHub-API. repos will be used to store the repositories of the current user profile and the variable github_user will contain the user name of the current profile.

The GithubService is used within methods getProfile(username) and getRepos(username) . Both methods takes the user name as a parameter and then makes use of the GithubService by calling the service methods getProfile and getRepos. As both methods return an Observable we’re able to use the subscribe method to attach an arrow function which processes the asynchronous response.

Furthermore the implementation of the ProfilePage class contains three event handler methods:

onSubmit

repoTapped

show Repos

In the next step, when we’re implementing the template code for ProfilesPage, we’ll connect those event handler methods to the corresponding UI events.

Let’s take a look at the template code which needs to be inserted into file profiles.html:

<!-- Generated template for the Profiles page. See http://ionicframework.com/docs/v2/components/#navigation for more info on Ionic pages and navigation. --> <ion-header> <ion-navbar> <ion-title>GitHub Profiles</ion-title> </ion-navbar> </ion-header> <ion-content padding> <ion-card> <form (ngSubmit)="onSubmit()"> <ion-item> <ion-label>GitHub User:</ion-label> <ion-input type="text" [(ngModel)]="github_user" name="github_user"></ion-input> </ion-item> <br/> <button ion-button type="submit" clear small>Search</button> </form> <button ion-button clear small (click)="reset()">Reset</button> </ion-card> <ion-card *ngIf="profiles"> <ion-item> <ion-avatar item-left> <img src="{{profiles.avatar_url}}"> </ion-avatar> <h2>{{profiles.name}}</h2> <p>{{profiles.location}}</p> </ion-item> <ion-card-content> <p>{{profiles.bio}}</p> </ion-card-content> <ion-row> <ion-col> <button ion-button icon-left clear small> <ion-icon name="person"></ion-icon> <div>{{profiles.followers}} Followers</div> </button> </ion-col> <ion-col> <button ion-button icon-left clear small (click)="showRepos(profiles.login)"> <ion-icon name="git-branch"></ion-icon> <div>{{profiles.public_repos}} Public Repos</div> </button> </ion-col> <ion-col center text-center> <ion-note> {{profiles.company}} </ion-note> </ion-col> </ion-row> </ion-card> <ion-card *ngIf="repos"> <ion-list> <ion-item *ngFor="let repo of repos" (click)="repoTapped($event, repo)"> <h2>{{repo.name}}</h2> <p>{{repo.description}}</p> </ion-item> </ion-list> </ion-card> </ion-content>

Here you can see that we’re using various Ionic 2 components to build up the user interface. Three <ion-card> elements are used to structure the main content area of the page. The first sections contains the code which is needed to display a form to the user. This form consists of one input element ( <ion-input> ) which is bound to the github_user class member. The data binding is implemented by using the ngModel directive. The submit event of the form is connected to the onSubmit event handler method.

The second ion-card section contains the code which is needed to display the detail information of the GitHub user profile. To make sure that this section is only included in the HTML output if a user profile has been retrieved from the GitHub API before we’re using the ngIf directive in the following way:

<ion-card *ngIf="profiles">

Within the section various details of the current profile are printed out. The output also included the following button

<button ion-button icon-left clear small (click)="showRepos(profiles.login)"> <ion-icon name="git-branch"></ion-icon> <div>{{profiles.public_repos}} Public Repos</div> </button>

As you can see, the click event of this button is bound to the showReports event handler method. After having clicked on this button this method perfomes another API call and retrieves the repository data for the current user profile.

The third ion-card sections prints out a list of repositories. This section is only included in the output if repository data is available in the repos variable.

<ion-card *ngIf="repos">

Each repository of the current user account is displayed as a list item:

<ion-item *ngFor="let repo of repos" (click)="repoTapped($event, repo)"> <h2>{{repo.name}}</h2> <p>{{repo.description}}</p> </ion-item>

The ngFor directive is used to iterate over all repositories within the repos object. The click event type of the <ion-item> element is bound to the event handler method repoTapped. This method takes the event object and the current repository as parameters and navigates the user to the RepoDetailsPage.

Implementing RepoDetailsPage

On the profiles page the user can tap on a repo entry to navigate to the RepoDetailsPage. As this page is not yet available we need to implement RepoDetailsPage in the next step. First, let’s use Ionic CLI to generate the page:

$ ionic g page repo-details

The corresponding files are created in src/pages/repo-details/:

repo-details.html

repo-details.ts

repo-details.scss

Again, let’s start in repo-details.ts:

import { Component } from '@angular/core'; import { NavController, NavParams } from 'ionic-angular'; /* Generated class for the RepoDetails page. See http://ionicframework.com/docs/v2/components/#navigation for more info on Ionic pages and navigation. */ @Component({ selector: 'page-repo-details', templateUrl: 'repo-details.html' }) export class RepoDetailsPage { selectedRepo: any; constructor(public navCtrl: NavController, public navParams: NavParams) { this.selectedRepo = navParams.get('repo'); } ionViewDidLoad() { console.log('ionViewDidLoad RepoDetailsPage'); } }

The selectedRepo member is declared on class level and initialized in the constructor with the current repository object. The current repository is taken from the navigation paramater repo by using the get method of the navParams object. Just remember, the repo navigation parameter has been filled when initiating the navigation in method repoTapped in ProfilesPage.

repoTapped(event, repo) { this.navCtrl.push(RepoDetailsPage, { repo: repo }); }

Now, that the current repository object is available in selectedRepo, we can make use of this object and include the various properties in the template to generate the output for the details page:

<!-- Generated template for the RepoDetails page. See http://ionicframework.com/docs/v2/components/#navigation for more info on Ionic pages and navigation. --> <ion-header> <ion-navbar> <ion-title>repo-details</ion-title> </ion-navbar> </ion-header> <ion-content padding> <ion-card> <ion-item> <ion-icon name="cube"></ion-icon> <strong>Repository Details</strong> <h1>{{selectedRepo.name}}</h1> <h3>by {{selectedRepo.owner.login}}</h3> </ion-item> <hr /> <ion-card-content> <p><strong>Description: </strong>{{selectedRepo.description}}</p> <p><strong>Language: </strong>{{selectedRepo.language}}</p> <p><strong>Watchers: </strong>{{selectedRepo.watchers_count}}</p> <p><strong>Stars: </strong>{{selectedRepo.stargazers_count}}</p> <p><strong>Forks: </strong>{{selectedRepo.forks_count}}</p> </ion-card-content> <hr /> <ion-card-content> <a href="{{selectedRepo.html_url}}" ion-button icon-left> <ion-icon name="git-network"></ion-icon> <div>Go To Repo Website</div> </a> </ion-card-content> </ion-card> </ion-content>

Updating Tabs Navigation

The last step is to update the tabs navigation, so that the user is able to access the Profiles page from the navigation menu. The implementation of the tabs navigation is available in files src/tabs/tabs.html and src/tabs/tabs.ts. Change the implementation in tabs.ts to the following:

import { Component } from '@angular/core'; import { HomePage } from '../home/home'; import { ProfilesPage } from '../profiles/profiles'; import { ContactPage } from '../contact/contact'; @Component({ templateUrl: 'tabs.html' }) export class TabsPage { // this tells the tabs component which Pages // should be each tab's root Page tab1Root: any = HomePage; tab2Root: any = ProfilesPage; tab3Root: any = ContactPage; constructor() { } }

Here we’re importing ProfilesPage and assigning ProfilesPage to the class member tabs2Root. Now we need to update the tabTitle attribute of the second <ion-tab> element in tabs.html as well:

<ion-tabs> <ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="home"></ion-tab> <ion-tab [root]="tab2Root" tabTitle="Profiles" tabIcon="git-network"></ion-tab> <ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab> </ion-tabs>