Learning Ionic - Second Edition

By Arvind Ravulavaru
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Angular - A Primer

About this book

Ionic makes it incredibly easy to build beautiful and interactive mobile apps using HTML5, SCSS, and Angular. Ionic also makes app development easier, faster, and more fun.

This hands-on guide will help you understand the Ionic framework and how you can leverage it to create amazing real-time applications. We begin by covering the essential features of Angular 2, and then dive straight into how Ionic fits in today’s world of hybrid app development and give you a better understanding of the mobile hybrid architecture along the way.

Further on, you will learn how to work with Ionic decorators, services, and components, which will allow you to build complex apps using the Ionic framework. We will take a look at theming Ionic apps using the built-in SCSS setup. After that, we will explore Ionic Native, and you will learn how to integrate device-specific features, such as notifications, with the Ionic app. To complete our learning, we will be building a Rider app, using Ionic and Uber API, to book a ride.

Next, you will learn how to unit test, end-to-end test, monkey test, and execute device testing on AWS Device farm. Then, we will take a look at migrating the existing Ionic 1 apps to Ionic 2 and deploy them to the App Store. The final chapter on Ionic 3 wraps up this book by explaining the new features of Ionic 3 at the time of writing this book.

By the end of this book, you will be able to develop, deploy, and manage hybrid mobile applications built with Cordova, Ionic, and Angular.

All the examples in this book are valid for both Ionic 2 and Ionic 3.

Publication date:
April 2017
Publisher
Packt
Pages
378
ISBN
9781786466051

 

Angular - A Primer

When Sir Timothy Berners-Lee invented the Internet, he never anticipated that the Internet would be used to publish selfies, share cat videos, or bomb web page with ads. His main intention (guessing) was to create a web of documents so a user on the Internet can access these hypertexts from anywhere and make use of it.

An interesting article published by Craig Buckler at Sitepoint titled, The Web Runs Out of Disk Space (http://www.sitepoint.com/web-runs-disk-space/), shows how the content on the Internet is spread out:

  • 28.65% pictures of cats
  • 16.80% vain selfies
  • 14.82% pointless social media chatter
  • 12.73% inane vlogger videos
  • 9.76% advertising/clickbait pages
  • 8.70% scams and cons
  • 4.79% articles soliciting spurious statistics
  • 3.79% new JavaScript tools/libraries
  • 0.76% documents for the betterment of human knowledge

You can see, since the invention of the Internet to the present day, how we have evolved. Better evolution needs better frameworks to build and manage such apps that need to be scalable, maintainable, and testable. This is where Angular stepped in back in 2010 to fill the gap and it has been evolving quite well since then.

We are going to start our journey by understanding the new changes to Angular, the importance of TypeScript, and see how Ionic 2 has adapted itself with Angular to help build performance-efficient and modern Mobile Hybrid apps.

In this chapter, we will take a quick peek at new topics added as part of Angular with the help of an example. The main changes that have taken place in Angular (2) are primarily on the lines of performance and componentization, apart from the language update. We will be going through the following topics in this chapter:

  • What is new in Angular?
  • TypeScript and Angular
  • Building a Giphy app
 

What is new in Angular?

Angular 2 is one of the most anticipated and dramatic version upgrades I have seen for any software. Angular 1 was a boon to web/mobile web/hybrid app developers, where managing a lot of things was made easy. Not only did Angular 1 help restructure client-side app development, but it also provided a platform to build applications; not websites, but applications. Though the first release suffered performance issues when dealing with large datasets, the Angular team bounced back quite well with the later releases of Angular 1, that is, Angular 1.4.x and above, and fixed these performance issues by releasing a more stable version in the form of Angular (2).

Some of the new changes that have accompanied with Angular (2) are:

  • Speed and performance improvements.
  • Component based (not the typical MV*).
  • Angular CLI.
  • Simple and expressive syntax.
  • Progressive Web Apps (PWA).
  • Cross-platform app development, which includes desktops, mobile, and web.
  • Cordova-based Hybrid app development.
  • Angular Universal provider for the server side for fast initial views.
  • Upgrades to better animation, internationalization, and accessibility.
  • Angular can be written on ES5, ES6, TypeScript, and Dart are based on the user's comfort with the JavaScript flavor.

With these new updates, developing apps has never been easier, be it on the desktop, mobile, or Mobile Hybrid environments.

Note: The latest version of Angular is going to be called just Angular, not Angular 2, or AngularJS 4, or NG4. So throughout this book, I will refer to Angular version 2 as Angular.

The current latest version of Angular is 4. Do checkout Chapter 11, Ionic 3, to know a bit more about Angular 4 and how it improves Ionic.

You can find more information about Angular here: https://angular.io.

Note: If you are new to Angular, you can refer to these books:

https://www.packtpub.com/web-development/learning-angular-2

https://www.packtpub.com/web-development/mastering-angular-2-components

https://www.packtpub.com/web-development/mastering-angular-2

https://www.packtpub.com/web-development/angular-2-example

Or these videos:

https://www.packtpub.com/web-development/angular-2-projects-video

https://www.packtpub.com/web-development/web-development-angular-2-and-bootstrap-video

https://www.packtpub.com/web-development/angular-2-web-development-TypeScript-video

 

TypeScript primer

Angular uses TypeScript extensively for app development. Hence as part of the Angular primer, we will refresh the necessary TypeScript concepts as well.

If you are new to TypeScript, TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. TypeScript provides static typing, classes, and interfaces and supports almost all features of ES6 and ES7 before they land in the browser.

A TypeScript file is saved with a .ts extension.

The main advantage of adding typings to an untyped language (JavaScript) is to make IDEs understand what we are trying to do and better assist us while coding; in other words, Intellisense.

Having said that, here is what we can do with TypeScript.

Variable typing

In vanilla JavaScript, we would do something like this:

x = 20; 
// after a few meaningful minutes
x = 'nah! It's not a number any more';

But in TypeScript, we cannot do as shown in the preceding code snippet, the TypeScript compiler would complain as we are modifying the variable type at runtime.

Defining types

When we declare variables, we can optionally declare the types of variables. For instance:

name: string = 'Arvind'; 
age: number = 99;
isAlive: boolean = true;
hobbies: string[];
anyType: any;
noType = 50;
noType = 'Random String';

This increases the predictability of what we are trying to do.

Classes

I am a guy who believes that JavaScript is an object-based programming language and not an object-oriented programming language, and I know quite a lot of people who disagree with me.

In vanilla JavaScript, we have functions, which act like a class and exhibit prototype-based inheritance. In TypeScript/ES6, we have the class construct:

class Person { 
name: string;

constructor(personName: string) {
this.name = personName;
}

getName {
return "The Name: " + this.greeting;
}
}
// somewhere else
arvind:Person = new Person('Arvind');

In the preceding example, we have defined a class named Person and we are defining the class constructor, which accepts the name on initialization of the class.

To initialize the class, we will invoke the class with a new keyword and pass in the name to the constructor. The variable that stores the instance of the class -- the object, arvind in the preceding example, can also be typed to the class. This helps in better understanding the possibilities of the arvind object.

Note: The classes in ES6 still follow Prototypal-based Inheritance and not the classical Inheritance model.

Interface

As we start building complex apps, there will be a common need for a certain type of structure to be repeated throughout the app, which follows certain rules. This is where an interface comes into the picture. Interfaces provide structural subtyping or duck typing to check the type and shape of entities.

For instance, if we are working with an app that deals with cars, every car will have a certain common structure that needs to be adhered to when used within the app. Hence we create an interface named ICar. Any class working with cars will implement this interface as follows:

Interface ICar { 
engine : String;
color: String;
price : Number;
}

class CarInfo implements ICar{
engine : String;
color: String;
price : Number;

constructor(){ /* ... */}
}

Modules and imports

In vanilla JavaScript, you must have observed code blocks like this:

(function(){ 
var x = 20;
var y = x * 30;
})(); //IIFE
// x & y are both undefined here.

Modules are achieved in ES6/TS using the imports and exports syntax:

logic.ts
export function process(){
x = 20;
y = x * 30;
}

exec.ts
import { process } from './logic';
process();

These are the bare essentials that we would need to get started with TypeScript. We will look at more such concepts where needed.

With this we wrap up the key concepts needed to get started with TypeScript. Let us get started with Angular.

For more information on TypeScript, check out: https://www.TypeScriptlang.org/docs/tutorial.html. Also check out the TypeScript introduction video: https://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript.
 

Angular

Angular (2) has added a bunch of new features and updated existing features and removed a few over Angular 1.x. In this section, we will go through some of the essential features of Angular.

Components

Angular components are inspired by the Web Components specification. At a very high level, Web Components have four pieces:

  • Custom elements: A user can create their own HTML element.
  • HTML imports: Import one HTML document into another.
  • Templates: HTML definitions of the custom elements.
  • Shadow DOM: A specification to write encapsulated logic of custom elements.

The preceding four specifications explain how a frontend developer can develop their own standalone, isolated, and reusable components, similar to a HTML select box (<select></select>), or a text area (<textarea></textarea>), or an input (<input />).
You can read more about the Web Component specification here: https://www.w3.org/standards/techs/components#w3c_all.

If you would like to dig deeper into the Web Component, check out: http://webcomponents.org/.

As mentioned, Angular is (loosely) built on Web Components, where the preceding four specifications are implemented in an Angular way.

In simple terms, our entire app is a tree of components. For example, if we look at the world's most viewed page, https://www.google.com, it would look something like this:

And if we had to build this page in Angular, we would first split the page into components.

A visual representation of all the components that go into the preceding page would look like this:

Note: Each black box is a (custom) component.

As we can see from the preceding figure, the entire page is a tree of custom components.

A (custom) component would typically consist of three pieces:

  • component.ts: This represents the component logic
  • component.html: This represents the component view (template)
  • component.css: This represents the component specific styles

To build a custom component, we need to use a Component decorator on top of a class. In simple terms, a decorator lets us configure a class with specific metadata on them. This metadata will then be used by Angular to understand the behavior of that class. Decorators start with an @, followed by the name of the decorator.

The component decorator tells Angular that the class being processed needs to exhibit the behavior of an Angular component. A simple decorator would look as follows:

@Component({ 
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
// This is where we write the component logic!
title = 'Hello World!';
}

Some of the properties that go into a component decorator are:

  • selector: CSS selector that identifies this component in a template
  • templateUrl: URL to an external file containing a template for the view
  • styleUrls: List of URLs to style sheets to be applied to this component's view
  • providers : List of providers available to this component and its children
To know more about the Component decorator, refer to the following link: https://angular.io/docs/ts/latest/api/core/index/Component-decorator.html

Zones

Zones are one of the new concepts that have been introduced in Angular. The concept of Zones was migrated from Dart to JavaScript.

The main reason why a lot of developers were attracted towards Angular initially was by its Auto-magic Data Binding among other things. This was achieved using scopes in Angular 1.x. In Angular 2, we are using Zone.js (https://github.com/angular/zone.js) to achieve the same.

Whenever there is a change in the data, Angular updates the appropriate stakeholders (variables, interfaces, providers, and so on) with new data. Angular can track all synchronous activities quite easily. But for change detection in asynchronous code, such as event handling, AJAX calls, or Timers, Angular 2 uses Zone.js.

To know more about zones, how they work, and change detection in Angular, check out Zones in Angular: http://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html and Angular change detection explained: http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html.

Templates

Templates are used to bind the component logic to the HTML. Templates are also used as an interface between the user interaction of the user and app logic.

Templates have changed quite a bit when compared to version 1 of Angular. But there are a few things that still remain the same. For instance, the way we take a value from a component and display it in the user interface remains the same with the double curly brace notation (interpolation syntax).

The following is a sample app.component.ts:

@Component({ 
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
// This is where we write the component logic!
title = 'Hello World!';
}

The app.component.html would look something like this:

<h1>
{{title}} <!-- This value gets bound from app.component.ts -->
</h1>

Templates can also be made inline by passing in the template metadata to the decorator instead of templateUrl. This would look something like this:

 @Component({ 
selector: 'app-root',
template: '<h1>{{title}}</h1>',
styleUrls: ['./app.component.css']
})
export class AppComponent {
// This is where we write the component logic!
title = 'Hello World!';
}
The template metadata takes higher priority over templateUrl. For example, if we have defined both a template and templateUrl metadata, template is picked up and rendered.
We can also write multiline templates using backtick(`) instead of quotes, in both ES6 as well as TypeScript. For more information, refer to Template Literals: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals

In Angular 1.x, we have core/custom directives. But in Angular (2), we have various notations, using which we achieve the same behavior of a directive from Angular 1.

For instance, if we want to add a custom class to an element based on the truthiness of an expression, it would look this:

<div [class.highlight]="shouldHighlight">Hair!</div>

The preceding is a replacement for the famous ng-class Angular 1.x directive.

To handle events, we use the ( ) notation, as in:

<button (click)=pullHair($event)">Pull Hair</button>

And this pullhair() is defined inside the component class.

To keep the data bindings up to date, we use the [( )] notation, as in:

<input type="text" [(ngModel)]="name">

This keeps the name property in the component class in sync with the textbox.

An example of *ngFor, which is a replacement for ng-repeat, is shown here:

<ul> 
<li *ngFor="let todo in todos">{{todo.title}}</li>

</ul>

Note that let in front of todo indicates that it is a local variable in that zone.

These are some of the basic concepts that we need to get started with our hands-on example. I will talk about other Angular (2) concepts as and when they appear in our app.

 

Giphy app

Using the concepts we have learned so far, we are going to build a simple app using Angular and an Open JSON API provider named Giphy.

Giphy (http://giphy.com) is a simple Gif search engine. The guys at Giphy exposed an open REST API that we can consume and do a bunch of things with the data.

The app we are going to build is going to talk to the Giphy JSON API and return the results. Using Angular, we are going to build interfaces for three features in the app:

  • Show a random Gif
  • Show trending Gifs
  • Search a Gif

We will be using Angular CLI (https://cli.angular.io/) and Twitter Bootstrap (http://getbootstrap.com/) with the Cosmos theme (https://bootswatch.com/cosmo/).

Before we start building the app, let's first understand the app structure.

Architecture

The first thing we are going to look at is the architecture of the app. On the client side, we will have a router, from which all things start flowing. The router will have four routes:

  • Home route
  • Browse route
  • Search route
  • Page Not Found route

We will have one service, with three methods that will interact with the Giphy REST API.

Apart from the previously mentioned items, we will have the following components:

  • Nav Component: App Navbar
  • Home Component: Home Page which shows a random gif
  • Trending Component: Show trending gifs
  • Search Component: Search a gif
  • Giphy Component: Template for a gif
  • Page not found Component: To show a page that tells the user that nothing is found

The component tree for this would look as follows:

API

The Giphy API is quite easy to understand and use. You can find the official API documents here: https://github.com/Giphy/GiphyAPI.

The APIs that we are going to consume are:

You can navigate to the preceding links to see the sample data.

At the time of writing, Giphy exposed dc6zaTOxFJmzC as the API key to use.

Angular CLI

To develop our Giphy app, we are going to use Angular CLI. If you are new to the CLI and its features, I recommend checking out this video: Simple Angular 2 App With Angular CLI: https://www.youtube.com/watch?v=QMQbAoTLJX8.
This example is written with Angular CLI version 1.0.0-beta.18.

Installing software

For us to successfully develop the Angular-Giphy App, we need to have Node.js installed (https://nodejs.org/en). We will be using NPM (https://www.npmjs.com) to download the required modules via the Angular CLI.

Once Node.js is installed, open a new command prompt/terminal and run the following:

npm install -g @angular/cli

This will go ahead and install the Angular CLI generator. That is all we would need to start developing our app.

Note: I have used angular-cli version 1.0.0 to build this app.

Text editors

Regarding text editors, you can use any editor to work with Angular as well as Ionic. You can also try Sublime text (http://www.sublimetext.com/3) or Atom editor (https://atom.io/) or Visual Studio Code (https://code.visualstudio.com/) for working with the code.

If you are using Sublime text, you can take a look at: https://github.com/Microsoft/TypeScript-Sublime-Plugin to add TypeScript intelligence to your editor. And for Atom, refer to the following link: https://atom.io/packages/atom-TypeScript.

Scaffolding an Angular 2 app

The first thing we are going to do is scaffold an Angular app using the Angular CLI. Create a new folder named chapter1 and open a command prompt/terminal in that folder and run the following:

ng new giphy-app

Now Angular CLI generator will go ahead and create all the files and folders necessary to work with our Angular app.

As mentioned earlier, you can check out Simple Angular 2 app with Angular CLI: https://www.youtube.com/watch?v=QMQbAoTLJX8, as well to go through Angular CLI docs: https://cli.angular.io/reference.pdf to know more about it.

The scaffolded project structure would look as follows:

. 
├── .angular-cli.json
├── .editorconfig
├── README.md
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ ├── tsconfig.e2e.json
├── karma.conf.js
├── node_modules
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ ├── assets
│ │ ├── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ ├── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── typings.d.ts
├── tsconfig.json
├── tslint.json

We will be spending most of our time inside the src folder. Once the project is completely scaffolded, cd into the giphy-app folder and run the following:

ng serve

This will start the built-in server. Once the build is completed, we can navigate to http://localhost:4200 to view the page. The page should look something like this:

Building the Giphy app

Now that we have all the pieces to get started, we will start off by adding Twitter Bootstrap CSS to the app.

For this example, we will be using a Bootstrap theme from https://bootswatch.com/ named Cosmos. We can find the Cosmos CSS theme on the theme page: https://bootswatch.com/cosmo/, by clicking on the Cosmos dropdown and selecting the bootstrap.min.css option. Or alternatively, we can find it here: https://bootswatch.com/cosmo/bootstrap.min.css.

If you want, you can use any other theme or the vanilla Bootstrap CSS as well.

To add the theme file, navigate to giphy-app/src/styles.css and add the following line inside it:

@import "https://bootswatch.com/cosmo/bootstrap.min.css";

That is it, now our app is powered with Twitter Bootstrap CSS.

Next, we will start working on our app's main page. For that we will be leveraging an example template from Twitter Bootstrap named the Starter Template. The template can be found here: http://getbootstrap.com/examples/starter-template/.

The Starter template consists of a navigation bar and a body section where the content gets displayed.

For the Navbar section, we will be generating a new component named nav-bar and updating the relevant code in it.

To generate a new custom component using Angular CLI, navigate to the giphy-app folder and run the following:

ng generate component nav-bar

Note: You can either kill the current running command or spawn a new command prompt/terminal to run the preceding command.

And you should see something like this:

create src/app/nav-bar/nav-bar.component.css
create src/app/nav-bar/nav-bar.component.html
create src/app/nav-bar/nav-bar.component.spec.ts
create src/app/nav-bar/nav-bar.component.ts
update src/app/app.module.ts

Now open giphy-app/src/app/nav-bar/nav-bar.component.html and update it as follows:

<nav class="navbar navbar-inverse navbar-fixed-top"> 
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" [routerLink]="['/']">Giphy App</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li [routerLinkActive]="['active']"><a [routerLink]="
['/trending']">Trending</a></li>
<li [routerLinkActive]="['active']"><a [routerLink]="
['/search']">Search</a></li>
</ul>
</div>
</div>
</nav>

All we are doing here is creating the header bar with two menu items and the app name, which acts as a link to the home page.

Next, we will update the giphy-app/src/app/app.component.html to load the nav-bar component. Replace the contents of that file with the following:

<nav-bar></nav-bar>

Next, we will start adding routes to the app. As discussed earlier, we are going to have three routes.

To add routing support to the current app, we need to do three things:

  1. Create the routes needed.
  2. Configure @NgModule.
  3. Tell Angular where to load the content of these routes.

At the time of writing, Angular CLI has disabled route generation. Hence we are going to create the same manually. Otherwise we could simply run ng generate route home to generate the home route.

So first, let's define all the routes. Create a new file named app.routes.ts inside the app folder. Update the file as follows:

import { HomeComponent } from './home/home.component'; 
import { TrendingComponent } from './trending/trending.component';
import { SearchComponent } from './search/search.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

export const ROUTES = [
{ path: '', component: HomeComponent },
{ path: 'trending', component: TrendingComponent },
{ path: 'search', component: SearchComponent },
{ path: '**', component: PageNotFoundComponent }
];

All we have done here is exported an array of routes. Do notice the path '**'. This is how we define the other section of the routes.

We will create the required components now. Run the following:

ng generate component home
ng generate component trending
ng generate component search
ng generate component pageNotFound

Next, we will configure the @NgModule. Open giphy-app/src/app/app.module.ts and add the following imports at the top:

import { RouterModule }   from '@angular/router'; 
import { ROUTES } from './app.routes';

Next, update the imports property of the @NgModule decorator as follows:

//.. snipp 
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(ROUTES)
],
//.. snipp

The completed page would look as follows:

import { BrowserModule } from '@angular/platform-browser'; 
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { NavBarComponent } from './nav-bar/nav-bar.component';
import { HomeComponent } from './home/home.component';
import { TrendingComponent } from './trending/trending.component';
import { SearchComponent } from './search/search.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

import { ROUTES } from './app.routes';

@NgModule({
declarations: [
AppComponent,
NavBarComponent,
HomeComponent,
TrendingComponent,
SearchComponent,
PageNotFoundComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(ROUTES)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Now we will update the app component to show the Navbar as well as the current route content.

Update the giphy-app/src/app/app.component.html as follows:

<app-nav-bar></app-nav-bar> 
<router-outlet></router-outlet>

Using the router-outlet, we tell the router to load the current route content at that location.

If you want to know more about routing in Angular, you can check out: Routing in Eleven Dimensions with Component Router by Brian Ford: https://www.youtube.com/watch?v=z1NB-HG0ZH4.

Next, we will update the home component HTML and test the app so far.

Open giphy-app/src/app/home/home.component.html and update it as follows:

<div class="container"> 
<div class="starter-template">
<h1>Giphy App</h1>
<p class="lead">This app uses the JSON API provided by Giphy to Browse and Search Gifs.
<br> To know more checkout : <a href="https://github.com/Giphy/GiphyAPI#trending-gifs-endpoint">Giphy API</a> </p>
</div>
</div>

Once this is done, save the file and run the following:

ng  serve

And we should see the following page:

As we can see, the page looks broken. Let's fix this by adding a couple of styles. Open giphy-app/src/styles.css and add the following:

body {
padding-top: 50px;
padding-bottom: 20px;
}

.starter-template {
padding: 40px 15px;
text-align: center;
}

Now our page will look as expected:

Next, we will start by writing the service to talk to the Giphy API. We will be writing three methods, one to get a random gif, one to get the latest trends, and one to search the Gif API with a keyword.

To get started, we will generate a service. Run the following:

ng generate service giphy

WARNING Service is generated but not provided, it must be provided to be used

As shown in the warning, the service that has been generated has not been marked as a provider. So we need to do that manually.

Open giphy-app/src/app/app.module.ts and import the GiphyService:

import { GiphyService } from './giphy.service';

Next, add the GiphyService as a provider in the @NgModule decorator, providers property:

//.. snipp 
providers: [
GiphyService
],
//..snipp

The complete giphy-app/src/app/app.module.ts would look as follows:

import { BrowserModule } from '@angular/platform-browser'; 
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { NavBarComponent } from './nav-bar/nav-bar.component';
import { HomeComponent } from './home/home.component';
import { TrendingComponent } from './trending/trending.component';
import { SearchComponent } from './search/search.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';

import { ROUTES } from './app.routes';

import { GiphyService } from './giphy.service';

@NgModule({
declarations: [
AppComponent,
NavBarComponent,
HomeComponent,
TrendingComponent,
SearchComponent,
PageNotFoundComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
RouterModule.forRoot(ROUTES)
],
providers: [
GiphyService
],
bootstrap: [AppComponent]
})
export class AppModule { }

Now we will update the giphy-app/src/app/giphy.service.ts with the three methods. Open giphy-app/src/app/giphy.service.ts and update it as follows:

import { Injectable } from '@angular/core'; 
import { Http, Response, Jsonp } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';

@Injectable()
export class GiphyService {
private giphyAPIBase = 'http://api.giphy.com/v1/gifs';
private APIKEY = 'dc6zaTOxFJmzC';

constructor(private http: Http) { }

getRandomGif(): Observable<Response> {
return this.http.get(this.giphyAPIBase +
'/random?api_key=' + this.APIKEY)
.map((res) => res.json());
}

getTrendingGifs(offset, limit): Observable<Response> {
return this.http.get(this.giphyAPIBase +
'/trending?api_key=' + this.APIKEY + '&offset=' + offset +
'&limit=' + limit)
.map((res) => res.json());
}

searchGifs(offset, limit, text): Observable<Response> {
return this.http.get(this.giphyAPIBase + '/search?api_key=' +
this.APIKEY + '&offset=' + offset +
'&limit=' + limit + '&q=' + text)
.map((res) => res.json());
}
}

All we are doing here is making an HTTP GET request to the corresponding Giphy API URLs and returning an Observable.

In RxJS (http://reactivex.io/rxjs/), an Observable is an entity, which can change over a period of time. This is the most basic building block of RxJS. An Observer subscribes to an Observable and reacts to its changes. This pattern is called a Reactive pattern.

Quoting from the documentation:

This pattern facilitates concurrent operations because it does not need to block while waiting for the Observable to emit objects, but instead it creates a sentry in the form of an observer that stands ready to react appropriately at whatever future time the Observable does so.

If you are new to Observables, you can start here: http://reactivex.io/documentation/observable.html followed by: Taking advantage of Observables in Angular: http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html and Angular 2 HTTP requests with Observables: https://scotch.io/tutorials/angular-2-http-requests-with-observables.

Now that the service is completed, we will update the HomeComponent to get a random gif and display it on the home page.

Open giphy-app/src/app/home/home.component.ts and update it as follows:

import { Component, OnInit } from '@angular/core'; 
import { GiphyService } from '../giphy.service';

@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
public gif: string;
public result: any;
public isLoading: boolean = true;

constructor(private giphyService: GiphyService) {
this.getRandomGif();
}

ngOnInit() {
}

getRandomGif() {
this.giphyService.getRandomGif().subscribe(
(data) => {
this.result = data;
this.gif = this.result.data.image_url;
this.isLoading = false;
},
(err) => console.log('Oops!', err),
() => console.log('Response', this.result)
)
}
}

In the preceding code, first off, we have imported GiphyService and added it to the constructor. Next, we have written getRandomGif() and invoked getRandomGif() from the constructor. In getRandomGif(), we have invoked getRandomGif() on giphyService to get a random gif. We are then assigning the gif to a class variable named gif.

Just to see if everything is working fine, we will run the app by executing ng serve and opening developer tools. If everything goes well, we should see the response from the Giphy API:

Now that we have the response, we want to build a component that will display the gif. We want to build a separate component for this because we will be using the same component on other pages as well to display a gif where needed.

Let's go ahead and scaffold the component. Run the following:

ng generate component gif-viewr

Next, open giphy-app/src/app/gif-viewr/gif-viewr.component.html and update it as follows:

<div class="item"> 
<div class="well">
<img src="{{imgUrl}}">
</div>
</div>

Once this is done, we need to tell the component to expect the data from the parent component, as the home component will pass the imgUrl to the gif-viewer component.

Open giphy-app/src/app/gif-viewr/gif-viewr.component.ts. First, update the import statement by adding a reference to the Input decorator:

import { Component, OnInit, Input} from '@angular/core';

Next, add an Input decorator to the imgUrl variable:

@Input() imgUrl: string;

The updated giphy-app/src/app/gif-viewr/gif-viewr.component.ts would look as follows:

import { Component, OnInit, Input} from '@angular/core'; 

@Component({
selector: 'app-gif-viewr',
templateUrl: './gif-viewr.component.html',
styleUrls: ['./gif-viewr.component.css']
})
export class GifViewrComponent implements OnInit {
@Input() imgUrl: string;

constructor() { }

ngOnInit() {
}
}
Note: To define an input for a component, we use the @Input decorator. To know more about the @Input decorator, refer to the Attribute Directives section in Angular docs: https://angular.io/docs/ts/latest/guide/attribute-directives.html.

Save the file and open giphy-app/src/app/home/home.component.html. We will add the app-gif-viewr component inside this page:

<app-gif-viewr class="home" [imgUrl]="gif"></app-gif-viewr>

The complete file would look as follows:

<div class="container"> 
<div class="starter-template">
<h1>Giphy App</h1>
<p class="lead">This app uses the JSON API provided by Giphy to
Browse and Search Gifs.
<br> To know more checkout :
<a href=
"https://github.com/Giphy/GiphyAPI#trending-gifs-endpoint">
Giphy API</a> </p>
</div>

<app-gif-viewr class="home" [imgUrl]="gif"></app-gif-viewr>
</div>

Next, we will update CSS to beautify the page. Open giphy-app/src/styles.css and add the following CSS to the existing styles:

.home .well{ 
width: 70%;
margin: 0 auto;
}

img{
width: 100%;
}

If we go back to the browser and refresh, we should see the following:

And every time we refresh a page, we will see a new gif come up.

Next, we are going to work on the Trending page. This page will show gifs that are trending using the Pintrest layout (or Masonry layout). The Trending REST API supports pagination. We will be making use of this to load 12 gifs at a time. And then provide a Load More button to fetch the next 12 gifs.

First, let's get the data from the Giphy API. Open giphy-app/src/app/trending/trending.component.ts. We will first import the GiphyService:

import { GiphyService } from '../giphy.service';

Now, we will add the same to the constructor and update the constructor to invoke getTrendingGifs():

constructor(private giphyService: GiphyService) { } 
In ngOnInit(), we will call the getTrendingGifs() API:
ngOnInit() {
this.getTrendingGifs(this.offset, this.perPage);
}
Next, we will add the required class variables:
private offset = 0;
private perPage = 12;
public results: any;
public gifs: Array<any> = [];
public isLoading: boolean = true;

offset and perPage will be used to manage pagination.

results will be used to store the response from the server.

gifs is the array consisting of an array of trending gifs that we are exposing to the template.

isLoading is a boolean variable to keep track if a request is in progress or not. Using isLoading, we will show/hide the Load More button.

Next, we will add getTrendingGifs():

getTrendingGifs(offset, limit) { 
this.giphyService.getTrendingGifs(offset, limit).subscribe(
(data) => {
this.results = data;
this.gifs = this.gifs.concat(this.results.data);
this.isLoading = false;
},
(err) => console.log('Oops!', err),
() => console.log('Response', this.results)
)
}
And finally getMore(), which will be invoked by the Load More button:
getMore() {
this.isLoading = true;
this.offset = this.offset + this.perPage;
this.getTrendingGifs(this.offset, this.perPage);
}

To display the gifs retrieved, we will update the trending component template. Open giphy-app/src/app/trending/trending.component.html and update it as follows:

<div class="container"> 
<h1 class="text-center">Trending Gifs</h1>
<div class="wrapper">
<app-gif-viewr [imgUrl]="gif.images.original.url" *ngFor="let gif of gifs"></app-gif-viewr>
</div>
<input type="button" value="Load More" class="btn btn-primary btn-block" *ngIf="!isLoading" (click)="getMore()">
</div>

All we are doing here is setting up app-gif-viewr to take the gif URL by applying an *ngFor directive on it. And at the bottom, a Load More button, so a user can load more gifs.

And finally to achieve the Pintrest/Masonry layout, we will add a couple of CSS rules. Open giphy-app/src/styles.css and add the following styles:

*, *:before, *:after { 
box-sizing: border-box !important;
}

.wrapper {
column-width: 18em;
column-gap: 1em;
}

.item {
display: inline-block;
padding: .25rem;
width: 100%;
}

.well {
position: relative;
display: block;
}

Save all the files and head back to the browser. If we click on the trending menu item in the Navbar, we should see the following:

And if we scroll down completely, we should see a Load More button:

Clicking on the Load More button will load the next set of gifs:

I wasted about 15 minutes clicking Load More and watching the gifs. I think this is why APIs should have a rate limit.

Finally, we will implement searching gif. Open giphy-app/src/app/search/search.component.ts and import GiphyService:

import { GiphyService } from '../giphy.service';

Add giphyService as a class variable in the constructor:

constructor(private giphyService: GiphyService) { }

Next, we will add variables to manage pagination as well as the response:

  private offset = 0; 
private perPage = 12;
public results: any;
public query: string;
public gifs: Array<any> = [];
public isLoading: boolean = true;

Now we will invoke searchGifs, which makes a REST call to get the searched gifs, by passing in the query string:

searchGifs(offset, limit, query) { 
this.giphyService.searchGifs(offset, limit, query).subscribe(
(data) => {
this.results = data;
this.gifs = this.gifs.concat(this.results.data);
this.isLoading = false;
},
(err) => console.log('Oops!', err),
() => console.log('Response', this.results)
)
}

The following is a method to manage the search form submit button:

  search(query) { 
this.query = query;
this.isLoading = true;
this.searchGifs(this.offset, this.perPage, this.query);
}

And finally, getMore() to load more pages of the same query:

getMore() { 
this.isLoading = true;
this.offset = this.offset + this.perPage;
this.searchGifs(this.offset, this.perPage, this.query);
}

The updated giphy-app/src/app/search/search.component.ts would look as follows:

import { Component, OnInit } from '@angular/core'; 
import { GiphyService } from '../giphy.service';

@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
private offset = 0;
private perPage = 12;
public results: any;
public query: string;
public gifs: Array<any> = [];
public isLoading: boolean = true;

constructor(private giphyService: GiphyService) { }

ngOnInit() {
}

searchGifs(offset, limit, query) {
this.giphyService.searchGifs(offset, limit, query).subscribe(
(data) => {
this.results = data;
this.gifs = this.gifs.concat(this.results.data);
this.isLoading = false;
},
(err) => console.log('Oops!', err),
() => console.log('Response', this.results)
)
}

search(query) {
this.query = query;
this.isLoading = true;
this.searchGifs(this.offset, this.perPage, this.query);
}

getMore() {
this.isLoading = true;
this.offset = this.offset + this.perPage;
this.searchGifs(this.offset, this.perPage, this.query);
}
}

Now we will update the giphy-app/src/app/search/search.component.html. Open giphy-app/src/app/search/search.component.html and update it as follows:

<div class="container"> 
<h1 class="text-center">Search Giphy</h1>
<div class="row">
<input class="form-control" type="text" placeholder="Search
something.. Like.. LOL or Space or Wow" #searchText
(keyup.enter)="search(searchText.value)">
</div>
<br>
<div class="wrapper">
<app-gif-viewr [imgUrl]="gif.images.original.url" *ngFor="let
gif of gifs"></app-gif-viewr>
</div>
<input type="button" value="Load More" class="btn btn-primary btn-block" *ngIf="!isLoading" (click)="getMore()">
</div>

This view is the same as the Trending component, except there is a search textbox, which will allow the user to search by entering a string.

If we save all the files, go back to the browser, and navigate to the Search page, we should see an empty page with a search textbox. At this point, the load more button will not be shown. If we enter text and hit the return key, we should see results, as shown in the following screenshot:

With this we have completed the implementation of a Giphy API with an Angular app.

To bring this example to a closure, we will update giphy-app/src/app/page-not-found/page-not-found.component.html as follows:

<div class="container"> 
<div class="starter-template">
<h1>404 Not Found</h1>
<p class="lead">Looks Like We Were Not Able To Find What You Are Looking For.
<br>Back to : <a [routerLink]="['/']">Home</a>? </p>
</div>
</div>

And when we navigate to http://localhost:4200/nopage, we should see the following page:

 

Summary

In this chapter, we have gone through a high level overview of TypeScript and why we use TypeScript. Next we got acquainted with Angular's new syntax and the component structure. Using this knowledge, we have built an app named Giphy, which interfaces with the Giphy REST API to get gifs.

You can read more about Angular here: https://angular.io.

Also, check out Chapter 11, Ionic 3, to know more about the changes to Angular 4.

In the next chapter -- Welcome to Ionic, we will get started with Mobile Hybrid development using Cordova and we will look at how Ionic fits into the bigger scheme of things.

About the Author

  • Arvind Ravulavaru

    Arvind Ravulavaru is a platform architect at Ubiconn IoT Solutions, with over 9 years of experience in software development and 2 years in hardware & product development. For the last 5 years, he has been working extensively on JavaScript, both on the server side and the client side. And for the last couple of years in IoT, building a platform for rapidly developing IoT solutions, named The IoT Suitcase. Prior to that, Arvind worked on big data, cloud computing, and orchestration.

    Browse publications by this author
Learning Ionic - Second Edition
Unlock this book and the full library for $5 a month*
Start now