Book Image

Learning Ionic, Second Edition - Second Edition

By : Arvind Ravulavaru
Book Image

Learning Ionic, Second Edition - Second Edition

By: Arvind Ravulavaru

Overview of 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.
Table of Contents (13 chapters)

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: