You are on page 1of 559

Contents

1 Introduction 10

Welcome! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Updates & Errata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Conventions Used in This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Changelog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

New Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

ECMAScript 6 (ES6) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

TypeScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Transpiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Web Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2 Ionic 2 Basics 25

Lesson 1: Generating an Ionic 2 Application . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Installing Ionic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Generating Your First Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Adding Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Running the Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

Updating Your Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Lesson 2: Anatomy of An Ionic 2 Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Important Files & Folders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

The Less Important Stu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Lesson 3: Ionic CLI Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

1
Lesson 4: Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

Common Decorators in Ionic 2 Applications . . . . . . . . . . . . . . . . . . . . . . . . 47

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Lesson 5: Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

What is a Class? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

Classes in Ionic 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

Creating a Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

Creating a Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

Creating a Directive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Creating a Pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Creating an Injectable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

Lesson 6: Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

The * Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Ionic 2 Template Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

Lesson 7: Styling & Theming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Introduction to Theming in Ionic 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Methods for Theming an Ionic 2 Application . . . . . . . . . . . . . . . . . . . . . . . . 89

Lesson 8: Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

Pushing and Popping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

Basic Navigation in Ionic 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

Passing Data Between Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Navigation Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

Tabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

Lesson 9: User Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

Two Way Data Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Form Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

Lesson 10: Saving Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

2
Local Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

SQLite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Lesson 11: Fetching Data, Observables and Promises . . . . . . . . . . . . . . . . . . . . . 118

Mapping and Filtering Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Observables and Promises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

Using Http to Fetch Data from a Server . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Fetching Data from your Own Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

Lesson 12: Native Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

Using Cordova Plugins in Ionic 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

3 Quick Lists 131

Lesson 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

About Quick Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

Lesson Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

Lesson 2: Getting Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Generate a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Create the Required Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

Add Required Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

Add Required Cordova Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

Set up Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

Lesson 3: Basic Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

The Home Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

The Checklist Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Lesson 4: Data Models and Observables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

Creating a Data Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

Adding an Observable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Lesson 5: Creating Checklists and Checklist Items . . . . . . . . . . . . . . . . . . . . . . . 167

Checklists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

3
Checklist Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Lesson 6: Saving and Loading Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188

Saving Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

Loading Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195

Lesson 7: Creating an Introduction Slider & Theming . . . . . . . . . . . . . . . . . . . . . . 196

Slider Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

Theming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

What next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

4 Giist 214

Lesson 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

About Giist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215

Lesson 2: Getting Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

Generate a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220

Create the Required Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

Add Required Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

Add Required Cordova Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223

Set up Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

Lesson 3: The List Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226

The Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226

The Class Denition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Using an Observable to Control Searching . . . . . . . . . . . . . . . . . . . . . . . . . 237

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

Lesson 4: The Reddit API and HTML5 Video . . . . . . . . . . . . . . . . . . . . . . . . . . 242

HTML5 Video Behaviour on iOS and Android . . . . . . . . . . . . . . . . . . . . . . . 242

4
Fetching Data from Reddit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

Playing our GIFs (videos) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253

Launching Comments in the In App Browser . . . . . . . . . . . . . . . . . . . . . . . . 254

Loading More GIFS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254

Changing Subreddits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256

Lesson 5: Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

Creating the Settings Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

Opening the Settings Page as a Modal . . . . . . . . . . . . . . . . . . . . . . . . . . . 262

Saving Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267

Lesson 6: Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278

What next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278

5 Snapaday 279

Lesson 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

About Snapaday . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280

Lesson 2: Getting Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

Generate a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284

Create the Required Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

Add Required Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287

Add Required Cordova Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287

Set up Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290

Lesson 3: The Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

The Home Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291

The Slideshow Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

5
Lesson 4: Taking Photos with the Camera . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302

Creating a Photo Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302

Creating a Simple Alert Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304

Taking a Photo with the Camera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307

Moving the Photo to Permanent Storage . . . . . . . . . . . . . . . . . . . . . . . . . . 313

Updating the Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319

Lesson 5: Saving and Loading Photos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

Implementing the Data Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326

Lesson 6: Creating a Custom Pipe and Flipbook of all Photos . . . . . . . . . . . . . . . . . . 327

Creating a Custom Pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

Creating a Slideshow of All Photos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332

Lesson 7: Integrating Local Notications & Social Sharing . . . . . . . . . . . . . . . . . . . 333

Local Notications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333

Social Sharing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338

Lesson 8: Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347

What next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347

6 Camper Mate 349

Lesson 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350

Lesson 2: Getting Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354

Generate a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354

Create the Required Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356

Add Required Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357

Add Required Cordova Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357

Set up Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

6
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

Lesson 3: Creating a Tabs Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361

Lesson 4: User Input and Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374

Lesson 5: Implementing Google Maps and Geolocation . . . . . . . . . . . . . . . . . . . . . 382

Connectivity Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383

Google Maps Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385

Implementing Google Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397

Lesson 6: Saving and Retrieving Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403

Lesson 7: Reusing Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413

Lesson 8: Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425

What next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425

7 Camper Chat 426

Lesson 1: Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427

Lesson 2: Getting Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

Generate a new application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431

Create the Required Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433

Create the Required Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433

Add Required Platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434

Install PouchDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434

Add Required Cordova Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435

Set up Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437

Lesson 3: Login Page and Sliding Menu Layout . . . . . . . . . . . . . . . . . . . . . . . . . 438

Lesson 4: Using Facebook for Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . 452

Setting up a Facebook App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452

Installing the Facebook Connect Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . 456

Setting up Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

Lesson 5: Creating Messages & Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . 465

7
Adding Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465

Lesson 6: Local and Remote Backend with PouchDB and Cloudant . . . . . . . . . . . . . . 471

Creating a Cloudant Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472

Integrating PouchDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474

Lesson 7: Styling & Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483

Basic Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483

Creating Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490

Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494

What next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494

8 Testing & Debugging 495

Testing & Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

Browser Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

iOS Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496

Android Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498

Tips & Common Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500

Installing your Application with GapDebug . . . . . . . . . . . . . . . . . . . . . . . . . 503

9 Building & Submitting 505

Preparing Assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506

Enable Production Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506

Generate Icons and Splash Screens . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506

Set the Bundle ID and App Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508

Set Cordova Preferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508

Minify Assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509

Signing iOS Applications on a Mac or PC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510

Signing iOS Applications on a Mac . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510

Signing iOS Applications on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . 519

Signing Android Applications on a Mac or PC . . . . . . . . . . . . . . . . . . . . . . . . . . 526

Signing an Android Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526

Building for iOS & Android Using PhoneGap Build (without MAC) . . . . . . . . . . . . . . . . 528

8
Building with PhoneGap Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529

Submitting to the Apple App Store . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537

Creating an App Store Listing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537

Uploading the Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541

Submit for Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547

Submitting to Google Play . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549

Creating a Build for Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549

Submitting Your Application to Google Play . . . . . . . . . . . . . . . . . . . . . . . . 550

Uploading Multiple APKs with Crosswalk . . . . . . . . . . . . . . . . . . . . . . . . . 554

Updating on the App Stores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556

Thank you! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558

9
Chapter 1

Introduction

Welcome!

Hello and welcome to Building Mobile Apps with Ionic 2! This book will teach you everything you need

to know about Ionic 2, from the basics right through to building an application for iOS and Android and

submitting it to app stores.

People will have varying degrees of experience when reading this book, many of you will already be familiar

with Ionic 1, some may have some experience with Ionic 2, and some may have no experience with either.

Whatever your skill level is, it should not matter too much. All of the lessons in this book are thoroughly

explained and make no assumption of experience with Ionic.

This book does not contain an introduction to HTML, CSS, and JavaScript though. You should have a

reasonable amount of experience with these technologies before starting this book. If you need to brush

up on your skills with these technologies Id recommend taking a look at the following:

Learn HTML & CSS

Learn Javascript

This book has many dierent sections, but there are three distinct areas. We start o with the basics,

we then progress onto some application walkthroughs and then we cover building and submitting

10
applications.

All of the example applications included in this course are completely standalone. Although in general, the

applications increase in complexity a little bit as you go along, I make no assumption that you have read

the previous walkthroughs and will explain everything thoroughly in each example. If there are concepts

that need to be explained in more than one walkthrough, I have copied information into both rather than

referring to the other walkthrough.

Updates & Errata

Ionic 2 is still in development, so that means that it is still changing. It is reasonably stable now, so most of

what you read in this book wont change, but there will still most likely be some changes until the release

version is reached. I will be frequently updating this book to reect any changes that are made to the

framework, and you will receive these updates for free. Any time I update the book you should receive

an email notication with a new download link.

Ill be keeping a close eye on changes and making sure everything works, but its a big book so if you think

you have found an error please email me and Ill get an update out as soon as I can.

Conventions Used in This Book

The layout used in this book doesnt require much explaining, however you should look out for:

> Blocks of text that look like this

As they are actions you have to perform. For example, these blocks of text might tell you to create a le

or make some code change. You will mostly nd these in the application walk throughs. This syntax is

useful because it helps distinguish between code changes I want you to make to your application, and just

blocks of code that I am showing for demonstration purposes.

NOTE: You will also come across blocks of text like this. These will contain little bits of information that

are related to what you are currently doing.

11
IMPORTANT: You will also see a few of these. These are important Gotchas you should pay careful

attention to.

Ok, enough chat! Lets get started. Good luck and have fun!

12
Changelog

Version 8 (this version) - updated for beta.11

Changed implementation of overlay components

Updated forms to use formGroup and formControlName

Reformatted entire book to a more versatile format

Added a .mobi and .ePub version of the book

Various typo and formatting xes

Version 7 - updated for beta.10

Changed code to use <ion-header> and <ion-footer>

Removed references to *navbar

Switched to importing PouchDB instead of requiring, added a note on installing typings for Camper-

Chat

Formatting improvements

Other minor typo and bug xes

Version 6 - updated for beta.9

Version 5 - updated for beta.8

Replaced @Page and @App with @Component

Implemented ionicBootstrap

For more details on changes in beta.8 see the Ionic 2 Changelog

Version 4 - updated for beta.7

Updated to use TypeScript instead of ES6

13
Updated to use @ViewChild instead of getComponent

Updated syntax to match latest Angular RC

Version 3 - updated for beta.5 / beta.6

Removed NgZone as it is no longer required

Removed es6-shim import from app.js les

Added changelog

Added table of contents

Fixed various typos

Version 2 - updated for beta.4

Version 1 - Initial release

14
New Concepts

Ionic 1 was built on top of Angular 1, which is a framework for building complex and scaleable Javascript

applications. What Ionic does on top of Angular is that it provides a bunch of functionality to make making

mobile apps with Angular easier. Then along came Angular 2 which is the next iteration of the Angular

framework, which comes with a bunch of changes and improvements. In order for Ionic to make use of

Angular 2 a new framework was required on their end as well, which is how Ionic 2 came about. In short,

by using Ionic 2 & Angular 2 we will be able to make apps that perform even better on mobile, adhere to

the latest web standards, are scalable, reusable, modular and so on.

With the introduction of Angular 2, there has been a lot of changes to how you develop an application.

There are massive conceptual changes, and there have also been a few changes to things like template

syntax as well.

In Ionic 2, your templates will look something like this:

<ion-menu [content]="content">

<ion-toolbar>
<ion-title>Pages</ion-title>
</ion-toolbar>

<ion-content>
<ion-list>
<button ion-item *ngFor="let p of pages" (click)="openPage(p)"></button>
</ion-list>
</ion-content>

</ion-menu>

<ion-nav id="nav" [root]="rootPage" #content></ion-nav>

15
which isnt too dierent to Ionic 1, and your Javascript will look something like this:

import {Component} from '@angular/core';


import {Platform} from 'ionic-angular';
import {HomePage} from './pages/home/home';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {

rootPage: any = HomePage;

constructor(platform: Platform) {

platform.ready().then(() => {

});
}
}

which is very dierent to Ionic 1. If youre already familiar with ECMAScript 6 or TypeScript then a lot of

this probably wont be too hard of a change for you, but if these are completely new concepts to you

(and for most people it will be) the transition might be a little more dicult. To help put your mind at ease

somewhat, ES6 and TypeScript was all completely new to me when the Ionic 2 alpha rst came out, and

within a pretty short time period, I started to feel very comfortable with it. Now I am way more comfortable

with the new syntax and structure than I ever was with Ionic 1.

In this lesson we are going to broadly cover some of the new concepts and syntax in Ionic 2 & Angular 2.

The intention is just to give you a bit of a background, we will get into specics later.

16
ECMAScript 6 (ES6)

Before we talk about ECMAScript 6, we should probably talk about what ECMAScript even is. Theres

quite a bit of history involved which we wont dive into, but for the most part: ECMAScript is a standard,

Javascript is an implementation of that standard. ECMAScript denes the standard and browsers imple-

ment it. In a similar way, HTML specications (most recently HTML5) are dened by the organising body

and are implemented by the browser vendors. Dierent browsers implement specications in dierent

ways, and there are varying amounts of support for dierent features, which is why some things work

dierently in dierent browsers.

The HTML5 specication was a bit of a game changer, and in a similar way so is the ECMAScript 6 spec-

ication. It will bring about some pretty drastic changes to the way you will code with JavaScript and in

general, will make Javascript a much more mature language that is capable of more easily creating large

and complex applications (which JavaScript was never really originally intended to do).

Were not going to go too much into ES6 here, because you will learn what you need to know throughout

the book, but I will give a few examples to give you a sense of what it actually is. Some features ES6

introduced to Javascript are:

Classes

class Shape {
constructor (id, x, y) {
this.id = id
this.move(x, y)
}
move (x, y) {
this.x = x
this.y = y
}
}

17
This is a big one, and something you would be familiar with if you have experience with more traditional

programming languages like Java and C#. People have been using class-like structures in Javascript for

a long time through the use of functions, but there has never been a way to create a real class. Now there

is. If you dont know what a class is, dont worry, there is an entire lesson dedicated to it later.

Modules

// lib/math.js
export function sum (x, y) { return x + y }
export var pi = 3.141593

// someApp.js
import * as math from "lib/math"
console.log("2PI = " + math.sum(math.pi, math.pi))

// otherApp.js
import { sum, pi } from "lib/math"
console.log("2PI = " + sum(pi, pi))

Modules allow you to modularise your code into packages that can be imported anywhere you need in

your application, this is something that is going to be heavily used in Ionic. We will get into this more later,

but essentially any components we create in our application we export so that we can import them

elsewhere.

Promises

Promises are something that have been made available by services like ngCordova previously, but now

they are natively supported, meaning you can do something like this:

doSomething().then((response) => {
console.log(response);
});

18
Block Scoping

Currently, if you dene a variable in Javascript it is available anywhere within the function that it was dened

in. The new block scoping features in ES6 allow you to use the let keyword to dene a variable only within

a single block of code like this:

for (let i = 0; i < a.length; i++) {


let x = a[i];
}

If I were to try and access the x variable outside of the for loop, it would not be dened.

Fat Arrow Functions

One of my favourite new additions is fat arrow functions, which allow you to do something like this:

someFunction((response) => {
console.log(response);
});

rather than:

someFunction(function(response){
console.log(response);
});

At a glance, it might not seem all that great, but what this allows you to do is maintain the parents scope.

In the top example if I were to access the this keyword it would reference the parent, but in the bottom

example I would need to do something like:

var me = this;

someFunction(function(response){
console.log(me.someVariable);

19
});

to achieve the same result. With the new syntax, there is no need to create a static reference to this you

can just use this directly.

This is by no means an exhaustive list of new ES6 features so for some more examples take a look at

es6-features.org.

TypeScript

Another concept we should cover o on is TypeScript which is used in Ionic 2. Its important to point out

that although Ionic 2 uses TypeScript, you dont have to use it yourself to build Ionic 2 applications - you

can just use plain ES6. That said though, TypeScript provides additional features and makes some things

(dependency injection in particular) a lot easier, and it will soon become the default for Ionic 2 so it doesnt

make much sense not to use it.

We will be using TypeScript in this book, so lets talk a little bit more about what it is and how it is dierent

to plain ES6. TypeScripts own website denes it as:

a typed superset of JavaScript that compiles to plain JavaScript

If youre anything like me then you still wouldnt know what TypeScript is from that description (it seems

easy to understand denitions are a big no-no in the tech world). In fact, a StackOverow post did a much

better job at explaining what TypeScript is basically, TypeScript adds typing, classes and interfaces to

JavaScript.

Using TypeScript allows you to program in the way you would for stricter, object oriented languages like

Java or C#. Javascript wasnt originally intended to be used for designing complex applications so the

language wasnt designed that way. It certainly is possible already to use JavaScript in an object oriented

manner by using functions as classes as we discussed before but its not quite as clean as it could be.

But I mentioned before that ES6 is already adding the ability to create classes so why do we still need

TypeScript? I saw one Redditor put it quite simply:

20
Its called TypeScript not ClassScript

TypeScript still provides the ability to use static typing in JavaScript (which means it is evaluated at compile

time, opposed to dynamic typing which is evaluated at run time). Using typing in TypeScript will look a

little like this:

function add(x: number, y :number):number {


return x + y;
}
add('a', 'b'); // compiler error

The code above states that x should be a number (x: number), y should be a number (y: number),

and that the add function should return a value that is a number (add(): number). So in this example,

we will receive an error because were trying to supply characters to a function that expects only numbers.

This can be very useful when creating complex applications, and adds an extra layer of checks that will

prevent bugs in your application.

If you take a look at the Ionic 2 code from before:

export class MyApp {

rootPage: any = HomePage;

constructor(platform: Platform) {

platform.ready().then(() => {

});
}
}

You can see some TypeScript action going on. The code above is saying that rootPage can be the any

type, which is a special type which basically just means it can be anything at all, and platform has a type

21
of Platform. As you will see later, the ability to give things types comes in very handy for an important

concept called dependency injection.

Since the default option for Ionic 2 is TypeScript, and it is what most people are using, this book focuses

on using TypeScript. For the most part, ES6 and TypeScript projects look pretty much the same, and

converting between the two is a reasonably straight forward task.

Transpiling

Transpiling means converting from one language to another language. Why is this important to us? Ba-

sically, ECMAScript 6 gives us all of this cool new stu to use, but ES6 is just a standard and it is not

completely supported by browsers yet. We use a transpiler to convert our ES6 code into ES5 code (i.e. the

Javascript youre using today) that is compatible with browsers.

In the context of Ionic applications, heres how the process works:

You use ionic serve to run the application

All the code inside of the app folder is transpiled into valid ES5 code

A single bundled Javascript le is created and run

You dont need to worry about this process as it is all automatically handled by Ionic.

Web Components

Web Components are kind of the big thing in Angular 2, and they werent really feasible to use in Angular

1. Web Components are not specic to Angular, they are becoming a new standard on the web to create

modular, self contained, pieces of code that can easily be inserted into a web page (kind of like Widgets

in WordPress).

In a nutshell, they allow us to bundle markup and styles into custom HTML elements. - Rob Dodson

Rob Dodson wrote a great post on Web Components where he explains how they work and the concepts

22
behind it. He also provides a really great example, and I think it really drives the point home of why Web

Components are useful.

Basically, if you wanted to add an image slider as a web component, the HTML for that might look like this:

<img-slider>
<img src="images/sunset.jpg" alt="a dramatic sunset">
<img src="images/arch.jpg" alt="a rock arch">
<img src="images/grooves.jpg" alt="some neat grooves">
<img src="images/rock.jpg" alt="an interesting rock">
</img-slider>

instead of (without web components) this:

<div id="slider">
<input checked="" type="radio" name="slider" id="slide1" selected="false">
<input type="radio" name="slider" id="slide2" selected="false">
<input type="radio" name="slider" id="slide3" selected="false">
<input type="radio" name="slider" id="slide4" selected="false">
<div id="slides">
<div id="overflow">
<div class="inner">
<img src="images//rock.jpg">
<img src="images/grooves.jpg">
<img src="images/arch.jpg">
<img src="images/sunset.jpg">
</div>
</div>
</div>
<label for="slide1"></label>
<label for="slide2"></label>
<label for="slide3"></label>

23
<label for="slide4"></label>
</div>

In the future, rather than downloading some jQuery plugin and then copying and pasting a bunch of HTML

into your document, you could just import the web component and add something simple like the image

slider code shown above to get it working.

Web Components are super interesting, so if you want to learn more about how they work (e.g. The Shadow

Dom and Shadow Boundaries) then I highly recommend reading Rob Dodsons post on Web Components.

24
Chapter 2

Ionic 2 Basics

25
Lesson 1: Generating an Ionic 2 Application

Weve covered quite a bit of context already, so you should have a reasonable idea of what Ionic 2 is all

about and why some of the changes have been made. With that in mind, were ready to jump in and start

learning how to actually use Ionic 2.

Installing Ionic

Before we can start building an application with Ionic 2 we need to get everything set up on our computer

rst. It doesnt matter if you have a Mac or PC, you will still be able to nish this book and produce both

an iOS and Android application that is ready to be submitted to app stores.

IMPORTANT: If you already have Ionic 1 set up on your machine then you can skip straight to the next

section. All you will need to do is run npm install -g ionic@beta or sudo npm install -g

ionic@beta to get everything needed for Ionic 2 set up. Dont worry if you want to keep using Ionic 1 as
well, after you update you will be able to create both Ionic 1 and Ionic 2 projects.

First you will need to install Node.js on your machine. Node.js is a platform for building fast, scalable

network applications and it can be used to do a lot of dierent things. Dont worry if youre not familiar

with it though, we wont really be using it much at all - we need it installed for Ionic to run properly and to

install some packages but we barely have to do anything with it.

> Visit the following website to install Node.js:

https://nodejs.org/

Once you have Node.js installed, you will be able to access the node package manager or npm through

your command terminal.

> Install Ionic and Cordova by running the following command in your terminal:

npm install -g ionic@beta cordova

26
or

sudo npm install -g ionic@beta cordova

You should also set up the Android SDK on your machine by following one of these guides:

Installation for Mac

Installation for Windows

If you are on a Mac computer then you should also install XCode which will allow you to build and sign

applications. You dont have to worry about setting up the iOS SDK as if you have a Mac this will be

handled by XCode and if you dont have a Mac then you cant set it up on your computer anyway (well

talk more about how you can build iOS applications without a Mac later).

You should now have everything you need set up and ready to use on your machine! To verify that the

Ionic CLI (Command Line Interface) is in fact installed on your computer, run the following command:

ionic -v

You can also get some detailed information about your current installation by running the following com-

mand from within an Ionic project:

ionic info

It should spit out some info about your current environment, heres mine at the time of writing this:

27
NOTE: The Ionic Framework and Ionic CLI (Command Line Interface) are two separate things. The CLI

is what we just installed, and it provides a bunch of tools through the command line to help create and

manage your Ionic projects. The Ionic CLI will handle downloading the actual Ionic Framework onto your

machine for each project you create.

Generating Your First Project

Once Ionic is installed, generating applications is really easy. You can simply run the ionic start

command to create a new application with all of the boilerplate code and les you need.

> Run the following command to generate a new Ionic application:

ionic start MyFirstApp blank --v2

To generate a new application called MyFirstApp that uses the blank template. Ionic comes with some

28
templates built in, in the example above we are using the blank template, but you could also use:

ionic start MyFirstApp sidemenu --v2

or

ionic start MyFirstApp tutorial --v2

or you could just run the default command:

ionic start MyFirstApp --v2

to use the default starter which is a tabs application. Notice that every time we are supplying the v2 ag.

If you leave this ag o it will just create a normal Ionic 1 application (handy for those of you who still need

to use V1 as well, but make sure you dont forget it when building Ionic 2 apps!).

NOTE: All Ionic 2 projects use TypeScript by default now. Since TypeScript is an extension of ES6, ES6

code will still work in TypeScript projects if you want to use it, but all Javascript les should have the .ts

extension, not .js.

Were just going to stick with a boring blank template for now. Once your application has been generated

you will want to make it your current directory so we can do some more stu to it.

> Run the following command to change to the directory of your new Ionic project

cd MyFirstApp

If using the command prompt or terminal is new to you, you might want to read this tutorial for a little more

in depth explanation - the content is specically for Ionic 1 but it should give you a general sense of how

the command line interface works.

Adding Platforms

Eventually we will be building our application with Cordova (in fact the application that the Ionic CLI gen-

erates is a Cordova application), and to do that we need to add the platforms we are building for. To add

29
the Android platform you can run the following command:

ionic platform add android

and to add the iOS platform you can run:

ionic platform add ios

If you are building for both platforms then you should run both commands. This will set up your application

so that it can be built for these platforms, but it wont really have any eect on how you build the application.

As I will explain shortly, most of our coding will be done inside of the app folder, but you will also nd

another folder in your project called platforms - this is where all of the conguration for specic platforms

live. Were going to talk about all that stu way later though.

Running the Application

The beauty of HTML5 mobile applications is that you can run them right in your browser whilst you are

developing them. But if you try just opening up your project in a browser by going to the index.html le

location you wont have a very good time.

An Ionic project needs to run on a web server - this means you cant just run it by accessing the le directly,

but it doesnt mean that you actually need to run it on a server on the Internet, you can deploy a completely

self contained Ionic app to the app stores (which we will be doing). Fortunately, Ionic provides an easy

way to view the application through a local web server whilst developing.

> To view your application through the web browser run the following command:

ionic serve

This will open up a new browser with your application open in it and running on a local web server. Right

now, it should look something like this:

30
Not only will this let you view your application but it will also update live with any code changes. If you edit

and save any les, the change will be reected in the browser without having to reload the application by

refreshing the page.

31
To stop this process just hit:

Ctrl + C

when you have your command terminal open. Also keep in mind that you cant run normal Ionic CLI

commands whilst ionic serve is running, so you will need to press Control + C before running any

commands.

Updating Your Application

There may come a time when you want to update to a later version of Ionic, especially during the beta

period where changes will be happening more frequently. The easiest way to update the version of Ionic

that your application is using is to rst update the Ionic CLI by running:

npm install -g ionic@beta

or

sudo npm install -g ionic@beta

again, and then updating the package.json le of your project. You should see something like this in that

le:

"dependencies": {
"@angular/common": "^2.0.0-rc.1",
"@angular/compiler": "^2.0.0-rc.1",
"@angular/core": "^2.0.0-rc.1",
"@angular/http": "^2.0.0-rc.1",
"@angular/platform-browser": "^2.0.0-rc.1",
"@angular/platform-browser-dynamic": "^2.0.0-rc.1",
"@angular/router": "^2.0.0-rc.1",
"es6-shim": "^0.35.0",
"ionic-angular": "2.0.0-beta.7",

32
"ionic-native": "^1.1.0",
"ionicons": "3.0.0",
"pouchdb": "^5.3.2",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"zone.js": "^0.6.12"
},

Simply change the ionic-angular version number to the latest version, and then run:

npm install

inside of your project directory. This will grab the latest version of the framework and add it to your project.

IMPORTANT: Keep in mind that there may be other dependencies in package.json that need to be up-

dated, as well as the Ionic library.

Make sure to read the changelogs to check for any breaking changes when a new version is released,

which may mean that you have to update parts of your code as well.

Often it is easiest to just create a fresh new project after updating the Ionic CLI, and porting your code over.

If that is not an option, just make sure you read the changelog carefully, and update your dependencies

and code accordingly. As Ionic 2 becomes more and more stable, this becomes less of a problem as the

changes are not as drastic.

33
Lesson 2: Anatomy of An Ionic 2 Project

Now that weve covered how to get Ionic 2 installed and how to generate a project, I want to cover what

the various les and folders contained within your newly generated project do. When you create a blank

Ionic 2 application, your folder structure will look like this:

34
At rst glance, theres an intimidating amount of stu there - but theres really not that much you need to
35
worry about, and it will all make total sense with a little explanation. Were going to discuss what everything

does, but I will cover the important stu rst in more detail (mainly the les and folders that you will be

modifying) and then cover the less important stu.

Important Files & Folders

These les and folders are ones that you will be using on a frequent basis, so its important that you

understand their role. Fortunately, theres not too many of them!

app

This is where most of the action occurs. In a default application, your app folder will contain:

A pages folder

A theme folder

An app.ts le

The pages folder will contain all of the page components for your application. If you look inside of the

pages folder you should see another folder called home. This is a component, and is made up of a class

denition (home.ts), a template (home.html), and style denitions (home.scss). For every page in your

application you will add another folder here (we can actually get the Ionic CLI to do this for us automatically),

so as well as the home folder you may also have login, intro, checkout, about and many more.

The theme folder will contain all of the .scss les which dene application wide styles for the application.

There are iOS, Android and Windows specic les, a core shared le, and another that contains variables

you can override. There is a whole section later in this book dedicated to theming Ionic 2 applications so

well discuss this in more detail there.

The app.ts le denes the root component for your application. Again, we discuss what the root compo-

nent is and what it does in detail in other parts of this book so I wont cover much right now, but essentially

its the starting point for your application.

36
As well as the default folders that your app will contain, as you start building your application you will likely

see a few more folders in here as well, but well get to that later.

IMPORTANT: This is a really important concept to understand so make sure you read this over a couple

of times if you dont get it initially. In the introduction section of this book we talked about webpack,

transpiling and a bunch of other fancy ES6 stu - the important thing to remember is that the ES6 and

TypeScript features we are using arent actually supported by browsers yet, so we need to transpile

them into valid ES5 code. When you run or build your application, Ionic will bundle up everything in this

app folder, perform all the magic it needs to perform, and then spits it out into the build folder inside of

www.

When you are viewing your application through the browser you are actually viewing the bundled version

inside of the www folder, not the code you created in the app folder. Likewise, when you build your

application for release on iOS and Android (which we will discuss later) it is this www folder that will be

used, not the app folder. DO NOT EDIT CODE IN THE WWW FOLDER, EXCEPT FOR INDEX.HTML -

any changes you make in there (which is a bad idea anyway) will be overwritten when your app is rebuilt.

Although you should not edit code in this folder, you can however add other static resources to this folder

like images, JSON les and so on.

Using Angular 2 and the new ES6 syntax means the project structure and build process is quite a bit more

complicated, but fortunately for us Ionic handles basically all of it for us. So dont worry too much about

it, this is just something important to keep in mind so that you know what is going on.

www

Weve already touched on this above, but the www folder is the web root folder, and will contain the

compiled code for your application that is ready to be run through the browser or on devices.

The most important thing to remember about this folder is that although in general you dont need to touch

it, you will need to add some static resources here that your app will use. In most cases that will be images,

but it also might be things like JSON les that you want to bundle with your application to provide some

data.

37
If you wanted to use an image in your application for example, you would add an <img> tag to one of your

components in the app folder. The path you would use would look something like this:

<img src="images/cutedog.jpg" />

Given that path, you might assume that your image should also live inside of the app folder as well. But

since your app gets bundled into a app.bundle.js le that will then be moved inside the www folder

automatically, thats actually where your image will need to live. So given the path above, your image

should be placed at:

www/images/cutedog.jpg

and if you were referring to the image in one of your .scss les the path should be:

../../images/cutedog.jpg

because the compiled CSS les will be located in their own css folder in the build directory (inside of

www). All of this isnt exactly obvious and is a little confusing, but it works - just something to remember!

cong.xml

You wont need to edit this le very often, but it is a very important le. The cong.xml le is basically

used as a way to tell Cordova how to build your application for iOS and Android. Youll have to supply

some important conguration information in here which we will discuss later, but you can also dene some

preferences in here (like whether the splash screen should auto hide, whether the app should be portrait

only, and a bunch more). You will usually only really worry about this le when youre starting to look at

getting your app on real devices.

The Less Important Stu

Obviously everything in your Ionic project plays a role, otherwise it wouldnt be there. But some of it is for

more advanced use cases you might not need to worry about, and some of it is just pure conguration

stu that youll never have to touch.

38
Conguration Files

If you take a look in the root folder of your generated project you will see a bunch of conguration les.

Aside from the cong.xml le which we discussed above, you can pretty safely just ignore all of these.

The only le you may want to edit is the package.json le to update the version of Ionic you are using.

resources

This folder simply contains the splash screens and icons that will be used for iOS and Android when you

build your application. Ill show you a really easy way to create these resources with the Ionic CLI later.

hooks

Hooks are used as part of your applications build process, and you can add custom scripts in here to

hook into various parts of the build process. At a beginner level you likely will not need to touch this at

all, but if you wanted to start developing more complicated (but useful) workows, related to versioning or

deployment of your application for example, you could create some hooks here.

node_modules

This is another folder that you wont have to touch at all, but its where all the goodies are stored. If you

take a look in this folder you will nd things like ionic-angular, angular2, and ionicons. This is where all

of the source les for the various libraries that your application depends on are stored (including the Ionic

framework itself).

39
Lesson 3: Ionic CLI Commands

The Ionic CLI is a super powerful tool - weve already gone through how to use it to generate a new project

and display your application in the browser, but theres a bunch more commands you should know about

too, so lets go through some of them. This is by no means an exhaustive list, but it will cover all of the

commands you should be using frequently.

Im going to list out a few commands now and what they do, and since some commands have multiple

dierent arguments that you can supply to them I will be using the following syntax:

ionic command [option1|option2]

in place of:

ionic command option1

and

ionic command option2

for the sake of keeping things neat and tidy. Lets get into it!

ionic serve -l

Youve already seen ionic serve which will launch your application in the browser with live reloading,

but this command will launch Ionic Lab, which looks like this:

40
It will allow you to quickly see side by side what your application looks like on both iOS and Android.

ionic platform add [ios|android]

This will allow you to add platforms that you plan on building for.

ionic plugin add [plugin]

This will allow you to add Cordova plugins to your project, simply supply the plugin name.

ionic build [ios|android]

When you are ready to build your application for iOS and Android you can simply run these commands to

41
build your project. Theres a little bit more to it than this to actually get it on your device and then submit

it to App Stores, but we will discuss all that later.

ionic run [ios|android]

If you want to test your application on a real device you can use this command to deploy the app directly to

your device. For more information on this, please see the Testing & Debugging section later in the book.

ionic emulate [ios|android]

Instead of deploying your application to a real device, this will launch an emulator on your machine and

run it there instead.

ionic g [page|component|directive|pipe|provider|tabs]

This is the generate command and it is one of my favourites, its a real time saver. As I mentioned before,

your Ionic project will contain a bunch of components like the home page. To create more components you

can manually create a new folder and add all the required les, or you can just run the ionic generate

command to do it automatically for you, with some handy boilerplate code in place. As well as pages, this

command can also generate generic components, directives, pipes, providers and tabs. Id recommend

using it for any new component you are adding to your application.

ionic plugin list

This will allow you to see a list of all the plugins you have installed in your project.

ionic plugin rm [plugin]

This will allow you to remove any plugin from your project by supplying the plugin name.

ionic platform rm [android|ios]

This will allow you to remove a platform that you have previously added.

As I mentioned, this is not an exhaustive list but it does cover the most commonly used commands. I

rarely use any commands outside of these.

42
Lesson 4: Decorators

Each class (which we will talk about in the next section) you see in an Ionic 2 application will have a

decorator. A decorator looks like this:

@Component({
someThing: 'somevalue',
someOtherThing: [Some, Other, Values]
})

They denitely look a little weird, but they play an important role. Their role in an Ionic 2 application is

to provide some metadata about the class you are dening, and they always sit directly above your class

denition (again, well get to that shortly) like this:

@Decorator({
/*meta data goes here*/
})
export class MyClass {
/*class stuff goes here*/
}

This is the only place you will see a decorator, they are used purely to add some extra information to a

class (i.e. they decorate the class). So lets talk about exactly how and why we would want to use these

decorators in an Ionic 2 application.

The decorator name itself is quite useful, heres a few you might see in an Ionic 2 application:

@Component

@Pipe

@Directive

We can supply an object to the decorator to provide even more information on what we want. Heres the

most common example youll see in your applications:

43
@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

Now this class knows where it needs to fetch its template from, which will determine what the user will

actually see on the screen (well be getting into that later as well). If youve got a super simple template,

maybe you dont even want to have an external template le, and instead dene your template like this:

@Component({
template: `<p>Howdy!</p>`
})
export class HowdyPage {

Some people even like to dene large templates using template. Since ES6 supports using backticks

(the things surrounding the template above) to dene multi line strings, it makes dening large templates

like this a viable option if you prefer (rather than doing something ugly like concatenating a bunch of strings).

This object that you can supply to the decorator can be used for a lot more than just dening the template

though, heres a look at a more complex decorator that you might come across:

@Component({
templateUrl: 'build/pages/my-page/my-page.html',
providers: [MyProvider],
directives: [MyCoolDirective, MyCoolerDirective],
pipes: [MyPipe]
})
export class MyPage {

44
}

Now things are looking a little interesting. Were still telling the class where to grab the template from, but

now we are also telling it that we wanted to include some providers, directives and pipes. If you dont

know what these are yet dont worry, well be discussing them shortly.

Im not going to try and cover everything you can do with a decorator here, but one more important

thing to note is that since Ionic has removed the @App decorator in favour of following a style that is more

consistent with Angular 2, we now need to bootstrap our applications (the @App decorator used to handle

this automatically for us).

Bootstrapping just basically means setting up the root component of your application (which lives in the

app.ts le), which is essentially the rst component that is created that will then go on and create all of the

rest. To demonstrate, your app.ts le might look something like this:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {

rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();

45
});
}
}

ionicBootstrap(MyApp, [Data]);

You may notice that this @Component doesnt really look any dierent to any of the other components,

except that at the bottom a call to the ionicBootstrap function is made:

ionicBootstrap(MyApp, [Data]);

This is a special Ionic avoured version of the bootstrap function provided by Angular 2. We supply it the

component we want to use as the root component (which is the component dened in the same le), and

we also provide it an array of any providers we are using (rather than declaring them in the @Component

decorator which we will usually be doing.

You can also supply a cong option as a third parameter to the bootstrap function to dene some default

behaviours for the app:

ionicBootstrap(MyApp, [Data], {
backButtonText: 'Go Back',
iconMode: 'ios',
modalEnter: 'modal-slide-in',
modalLeave: 'modal-slide-out',
tabbarPlacement: 'bottom',
pageTransition: 'ios'
});

This denes some behaviour that we want the app to exhibit, so if we used the cong above our app would

use Go Back as the back button text and would use the iOS icons instead of the Android icons (as well

as a few other things obviously).

You can also use the cong object to dene platform specic behaviour:

46
ionicBootstrap(MyApp, [Data], {
tabbarPlacement: 'bottom',
platforms: {
ios: {
tabbarPlacement: 'top',
}
}
});

This conguration says that we want to place our tab bars at the bottom by default, but if we are running

on the iOS platform then we want them to display at the top instead (whereas usually tabs are placed at

the bottom on iOS).

Now that weve covered the basics of what a decorator is and what it does, lets take a look at some

specics.

Common Decorators in Ionic 2 Applications

There are quite a few dierent decorators that we can use. In the end, their main purpose is simply to

describe what the class we are creating is, so that it knows what needs to be imported to make it work.

Lets discuss the main decorators you are likely to use, and what the role of each one is. Were just going to

be focusing on the decorator for now, we will get into how to actually build something useable by dening

the class in the next section.

@Component

I think the terminology of a component can be a little confusing in Ionic 2. As I mentioned, our application

is made up of a bunch of components that are all tied together. These components are contained within

folders inside of our app folder, which look like this:

47
home

home.ts

home.html

home.scss

A @Component is not specic to Ionic 2, it is used generally in Angular 2. A lot of the functionality provided

by Ionic 2 is done through using components. In Ionic 2 for example you might want to create a search

bar, which you could do using one of the components that Ionic 2 provides like this:

<ion-searchbar></ion-searchbar>

You simply add this custom tag to your template. Ionic 2 provides a lot of components but you can also

create your own custom components, and the decorator for that might look something like this:

@Component({
selector: 'my-cool-component'
})

which would then allow you to use it in your templates like this:

<my-cool-component></my-cool-component>

NOTE: Technically speaking a component should have a class denition and a template. Things like pipes

and providers arent viewed on the screen so have no associated template, they just provide some addi-

tional functionality. Even though these are not technically components you may often see them referred

to as such, or they may also be referred to as services or providers.

@Directive

The @Directive decorator allows you to create your own custom directives. Typically, the decorator would

look something like this:

@Directive({
selector: '[my-selector]'

48
})

Then in your template you could use that selector to trigger the behaviour of the directive you have created

by adding it to an element:

<some-element my-selector></some-element>

It might be a little confusing as to when to use @Component and @Directive, as they are both quite similar.

The easiest thing to remember is that if you want to modify the behaviour of an existing component use a

directive, if you want to create a completely new component use a component.

@Pipe

@Pipe allows you to create your own custom pipes to lter data that is displayed to the user, which can

be very handy. The decorator might look something like this:

@Pipe({
name: 'myPipe'
})

which would then allow you to implement it in your templates like this:

<p>{{someString | myPipe}}</p>

Now someString would be run through your custom myPipe before the value is output to the user.

@Injectable

An @Injectable allows you to create a service for a class to use. A common example of a service created

using the @Injectable decorator, and one we will be using a lot when we get into actually building the apps,

is a Data Service that is responsible for fetching and saving data. Rather than doing this manually in your

classes, you can inject your data service into any number of classes you want, and call helper functions

49
from that Data Service. Of course this isnt all you can do, you can create a service to do anything you

like.

An @Injectable will often just look like a normal class with the @Injectable decorator tacked on at the top:

@Injectable()
export class DataService {

IMPORTANT: Remember that just about everything you want to use in Ionic 2 needs to be imported rst

(we will cover importing in more detail in the next section). In the case of pipes, directives, injectables and

components they not only need to be imported, but also declared in your decorator like in the example I

gave above:

@Component({
templateUrl: 'build/pages/my-page/my-page.html',
providers: [DataService],
directives: [MyCoolDirective, MyCoolerDirective],
pipes: [MyPipe]
})

Summary

The important thing to remember about decorators is: theres not that much to remember. Decorators are

powerful, and you can certainly come up with some complex looking congurations. Your decorators may

become complex as you learn more about Ionic 2, but in the beginning, the vast majority of your decorators

will probably just look like this:

@Component({
templateUrl: 'build/pages/home/home.html'
})

50
I think a lot of people nd decorators o putting because at a glance they look pretty weird, but they look

way scarier than they actually are. In the next lesson well be looking at the decorators partner in crime:

the class. The class denition is where we will do all the actual work, remember that the decorator just sits

at the top and provides a little extra information.

51
Lesson 5: Classes

In the last section we covered what a decorator is. To recap, its a little bit of code that sits above our class

denitions that declares what the class is, what the class needs, and how the class should be congured.

Now we are going to talk about the class itself.

What is a Class?

Depending on your experience with programming languages, you may or may not know what a class is.

So Im going to take a step back rst and explain what a class is as a general programming concept, as it

is not specic to Ionic, Angular or even Javascript.

Classes are used in Object Oriented Programming (OOP), they essentially behave as blueprints for ob-

jects. You can dene a class, and then using that class you can create, or instantiate, objects from it. If

classes are a completely new concept to you, itd be worth doing a little bit of your own research before

continuing, but lets take a look at a simple example.

class Person {

constructor(name, age){
this.name = name;
this.age = age;
}

setAge(age){
this.age = age;
return true;
}

getAge(){
return this.age;

52
}

setName(name){
this.name = name;
return true;
}

getName(){
return this.name
}

isOld(){
return this.age > 50;
}
}

This class denes a Person object. The constructor is run whenever we create an instance of this

class (an object is an instance of a class), and it takes in two values: name and age. These values are

used to set the member variables of the class, which are this.name and this.age.

These values can be accessed from anywhere within the object by using the this keyword. The this

keyword refers to the current scope, so what it evaluates to depends on where you use it, but if you use it

within a class (and not within a callback or anything else which would change the scope) this will refer to

the class itself.

If you imagine yourself as this and your location in the physical world as the scope, consider the following

example: if you are in your hotel room your scope is the room, if you leave your room your scope is now

the hotel itself, if you leave the hotel your scope is now the world (or the country you are in if you prefer).

If youre not familiar with the this keyword, Id recommend reading this.

We have our class dened which acts as a blueprint for creating objects, so we could create a new Person

53
object like this:

var john = new Person('John', 32);

The two values Ive supplied here will be passed into the constructor of the Person class to set up the

member variables. Now if I were to run the following code:

console.log(john.getName());

Johns name would be logged to the console. Similarly we could also call the getAge function to retrieve

his age or we could even change his name or age using the set functions. Getters and setters are very

common for classes, but weve also dened a more interesting function here which is isOld. This will

return true if the Person is over 50 years old, which is not the case for John.

Perhaps the most important concept to remember is that the class is just a blueprint, an object is kind

of like an individual copy of a class. So we can have multiple objects created from the same class, e.g:

var john = new Person('John', 32);


var louise = new Person('Louise', 28);
var david = new Person('David', 52);

console.log(john.isOld());
console.log(louise.isOld());
console.log(david.isOld());

In the code above, John, Louise and David are all individual objects of the Person class, and maintain

their values separately. If we ran the code above, it would only return true for David (he may be old, but

Im sure he is wise).

Classes in Ionic 2

We know what a class is, but why are they suddenly a thing in Ionic 2 and Angular 2? Weve touched on this

earlier, but classes are a new addition to Javascript with the ES6 specication. It is certainly a welcome

54
change because it is one of the most widely used patterns in programming, and in fact most JavaScript

applications were already using classes anyway, ES6 just made it more ocial.

Before ES6 it was common (and I guess it still is since most people are still using ES5) to create a class

like structure by using functions. This looks a little something like this:

var Person = function (name, age) {


this.name = name;
this.age = age;
};

Person.prototype.isOld = function() {
return this.age > 50;
};

var david = new Person('david', 52);


console.log(david.isOld());

It looks a bit dierent, but the end result is basically the same. Since ES6 adds a class keyword we can

now use a more normal approach.

So lets take a look at what a class looks like in Ionic 2:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {SomePage} from '../pages/some-page/some-page';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

constructor(public nav: NavController) {

55
}

The rst thing you will notice is the import statements. Anything that is required by the class that you are

creating will need to be imported. In this case we are importing Component from @angular/core which

allows us to use the @Component decorator, and NavController from the Ionic library which we can use

to control navigation.

We are also importing SomePage which is a class of our own creation. The path for this simply follows

the directory structure of your project, in this case we have the SomePage component dened inside a

folder called pages which is one level above the current le. The import should link to wherever the .ts le

is for the class, but it is not necessary to include the .ts extension.

Next up we have the decorator, which you should be pretty familiar with by now. Remember that anything

that you are supplying to the decorator (like pipes, directives etc.) will need to be imported rst, just like

the classes above were imported. It might look something like this:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {SomePage} from '../pages/some-page/some-page';
import {MyDirective} from './directives/my-directive/my-directive';
import {MyPipe} from './pipes/my-pipe/my-pipe';

@Component({
templateUrl: 'build/pages/home/home.html',
pipes: [MyPipe],
directives: [MyDirective]
})
export class HomePage {

56
constructor() {

}
}

In the case of providers, pipes, components and directives, they will also need to be declared inside of the

decorator. However, if you are declaring providers in your root component (app.ts) make sure you add

the providers as an array in the ionicBootstrap function NOT in the decorator.

Once we get past the decorator, we nally arrive at the class itself. Notice though that it is preceded by

the export keyword, e.g:

export class MyApp {

The export keyword works in tandem with the import keyword, so we export classes that we want to

import somewhere else. The last thing we have left to discuss is the constructor. Weve already talked

about the role of a constructor in classes in general, and it is no dierent here: the code inside of the

constructor is run when the class is instantiated.

Theres a little bit more to it that you need to know though. In Ionic 2 we need to inject any services that

we want to use inside of this class into the constructor, which looks like this:

constructor(platform: Platform, nav: NavController) {

platform.ready().then(() => {

});

In this example we want to use the Platform service to detect when the device is ready, so we inject it into

57
the constructor and then we can use it within the constructor.

We dont need to use platform outside of the constructor here, but in most cases you will want to use the

services you inject elsewhere in the class as well. So to make the service available to any function within

the class, you must set it as a member variable. This will look like this:

import {Component} from '@angular/core';


import {Platform} from 'ionic-angular';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

someMemberVariable: any = hey!;

constructor(platform: Platform) {
this.platform = platform
}

someOtherMethod(){
this.platform.ready().then(() =>{

});
}
}

OR we can use the public keyword I mentioned before when using TypeScript to automatically create

these member variable references, like this:

import {Component} from '@angular/core';


import {Platform} from 'ionic-angular';

58
@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

someMemberVariable: any = hey!;

constructor(public platform: Platform) {

someOtherMethod(){
this.platform.ready().then(() =>{

});
}
}

You can also use the private keyword instead of public which limits access to that particular member

variable to the current class (i.e. another class would not be able to access the variable if it were declared

private).

Also note that any variables declared above the constructor like someMemberVariable is here will also

automatically be set up as member variables. So we could access someMemberVariable from anywhere

in this class through this.someMemberVariable. Any services you need to inject (and possibly set

up as member variables) should be added to the constructor, any member variables you want to create

for any other purpose should be declared above the constructor. If this concept sounds confusing to you

right now it should make a little more sense when we start going through some examples.

Now we can access platform from anywhere in this class through this.platform. If we did not set

59
up this member variable and tried to access platform from our someOtherMethod function, it would not

work.

Creating a Page

Pages will usually make up a large portion of your application - for each screen you want to have in your

application you will have a separate Page dened. Previously, there was a special decorator in Ionic for

this, but now they are just regular @Components.

So, as weve already discussed, the class for a page might look something like this:

import {Component} from '@angular/core';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class MyPage {
constructor() {

}
}

and the template le we are referencing in the decorator might look something like this:

<ion-header>
<ion-navbar>
<ion-title>
My Page
</ion-title>
</ion-navbar>
</ion-header>

60
<ion-content>

<ion-list>
<ion-item>I</ion-item>
<ion-item>Am</ion-item>
<ion-item>A</ion-item>
<ion-item>List</ion-item>
</ion-list>

</ion-content>

The template le makes up what the user will actually see (were going to discuss templates in a lot more

detail later). The template le and the class work in unison: the class denes what template is to be shown

to the user, and the template can make use of data and functions available in the class.

Weve covered the basic structure of what a Page class looks like and what the constructor function

does, but you can also add other functions that your page can make use of, for example:

import {Component} from '@angular/core';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class MyPage {

constructor() {
//this runs immediately
}

someMethod(){
//this only runs when called

61
}

someOtherMethod(){
//this only runs when called
}

You could have your constructor call these other functions or you could even have them triggered by a

user clicking a button in the template for example. These additional functions can be added to any class,

its not just specic to pages. We will cover these concepts in much more detail later, for now we just want

to understand the basic structure of the dierent class types and what they do.

NOTE: You can auto generate a Page in your project using the command ionic g page MyPage

Creating a Component

The code for a generic component looks a lot like the code for a page (remember, a page is a component),

just as it will look like just about everything else we create. When creating pages with components we use

Ionics built in navigation to handle displaying them. A page is a component that will take up the whole

screen (i.e. a view for the user), but a component will also allow you to create your own custom element

that you can insert into your templates as well. Maybe you want to create a custom date picker component

to add to your page, or a box that displays random motivational quotes - in both of these cases you would

create a custom Component.

The class for a generic component will look like this:

import {Component} from '@angular/core';

@Component({
selector: 'my-component',

62
templateUrl: 'build/components/my-component/my-component.html'
})
export class MyComponent {

text: any;

constructor() {
this.text = 'Hello World';
}
}

Really the only dierence with the component here is that it species a selector. This will be the name of

the element that you use to insert the component into your template, i.e:

<my-component></my-component>

Before we get to using it though, lets also talk about the template for the component. This isnt really any

dierent to how the template for a page works. Were referencing a le called my-component.html which

might contain something like this:

<div>
{{text}}
</div>

Just like with a page, we can reference data that is stored in the class denition (as well as functions). With

this template, all our component will do is render:

<div>Hello World</div>

into the DOM. Which is pretty boring of course, but you can create some pretty interesting, and reusable,

stu with this functionality. So lets take a quick look at how to now actually use the component in a page.

First, you just need to import it and add it to the list of directives for the page you want to use it on:

63
import {Component} from '@angular/core';
import {MyComponent} from '../../components/my-component/my-component';

@Component({
templateUrl: 'build/pages/home/home.html',
directives: [MyComponent]
})

Weve imported the component into our page, declared it in the directives array in the decorator, and then

you can just reference it in the pages template like this:

<my-component></my-component>

Its actually pretty unlikely that you will need to create your own custom components like this, as Ionic al-

ready provides most of what you would need (lists, tabs, buttons, inputs and so on). If you need something

that Ionic does not already provide though, then youll have to look at creating your own component.

NOTE: You can auto generate a Component in your project using the command ionic g component

MyComponent

Creating a Directive

As I mentioned before, components and directives are very similar, but in general a component is used to

create a completely new element, and a directive is used to modify the behaviour of an existing one.

The class for a custom directive looks like this:

import {Directive} from 'angular2/core';

@Directive({
selector: '[my-directive]'
})

64
export class MyDirective {
constructor() {

}
}

In this directive, we also have a selector like we did for the component - but its slightly dierent. Rather

than representing the name of a tag, this can be used as an attribute on an element. You will do this a lot

in Ionic 2, on buttons for example:

<button secondary>

or on your lists:

<ion-list no-lines>

but in this case were creating our own custom directive that we can use on anything, e.g:

<button my-directive>

But notice we dont actually have a template for this directive. Although we usually refer to any feature in

our applications as components, technically speaking a component consists of a class and a template

(view) - if it does not have a view then it is not a component (it would be better to refer to it as a service

or provider). To actually use this directive, you will need to make sure to import it and include it in the

decorator of the page you want to use it on, just like before:

import {Component} from '@angular/core';


import {MyDirective} from '../../directives/my-directive/my-directive';

@Component({
templateUrl: 'build/pages/home/home.html',
directives: [MyDirective]
})

65
I wanted to just cover the basics here, but I think its also useful to know about ElementRef. This will give

you access to the element that the directive was added to. You can include it in your directive like this:

import {Directive, ElementRef} from '@angular/core';

@Directive({
selector: '[my-directive]'
})
export class MyDirective {

constructor(element: ElementRef) {
this.element = element;
}
}

NOTE: You can auto generate a Directive in your project using the command ionic g directive

MyDirective

Creating a Pipe

Pipes might seem a little complex at rst glance, but theyre actually really easy to implement. They look

like this:

import {Injectable, Pipe} from '@angular/core';

@Pipe({
name: 'myPipe'
})
@Injectable()
export class MyPipe {
transform(value, args) {

66
//do something to 'value'

return value;
}
}

Notice that a pipe is also an @Injectable, we will talk about what that is in just a moment. So the idea is

that whatever you are passing through the pipe will go to this transform function, you do whatever you

need to do to the value, and then you return the new value back. Now this new value will be rendered out

to the screen, rather than the initial value. You can use it in your templates like this:

<p>{{someValue | myPipe}}</p>

which will run whatever someValue is through your custom pipe before displaying it. Once again, make

sure you import and reference the pipe in your decorator wherever you want to use it:

import {Component} from '@angular/core';


import {MyPipe} from '../../pipes/my-pipe/my-pipe';

@Component({
templateUrl: 'build/pages/home/home.html',
pipes: [MyPipe]
})

NOTE: You can auto generate a Pipe in your project using the command ionic g pipe MyPipe

Creating an Injectable

An Injectable allows you to create a service that you can use throughout your application (like an interface

between your application and an external or internal data service). Injectables might also be referred to as

Providers. The @Injectable that the Ionic CLI automatically generates looks like this:

67
import {Injectable, Inject} from '@angular/core';
import {Http} from '@angular/http';

@Injectable()
export class MyProvider {

constructor(http: Http) {
this.http = http;
this.data = null;
}

load() {
if (this.data) {
// already loaded data
return Promise.resolve(this.data);
}

// don't have the data yet


return new Promise(resolve => {
// We're using Angular Http provider to request the data,
// then on the response it'll map the JSON data to a parsed JS object.
// Next we process the data and resolve the promise with the new data.
this.http.get('path/to/data.json')
.map(res => res.json())
.subscribe(data => {
// we've got back the raw data, now generate the core schedule data
// and save the data for later reference
this.data = data;
resolve(this.data);
});

68
});
}
}

This code creates a provider called MyProvider that loads data from a JSON source (which can either be

a local JSON le, an external JSON le or a response from a server). It returns a promise, which will allow

the data to be retrieved from the http request after it has nished executing. If the data has already been

loaded then it just returns the data directly (also through a promise). Well go into fetching data with http

in more depth later, for now we just want to focus on the basics of an injectable.

So if we want to grab the data that this service returns, we would rst inject it into wherever we want to

use it, i.e:

import {Component} from '@angular/core';


import {MyProvider} from '../../providers/my-provider/my-provider';

@Component({
templateUrl: 'build/pages/home/home.html',
providers: [MyProvider]
})
export class MyPage {

constructor(public myProvider: MyProvider){

and then you could make use of any function the injectable provides through this.myProvider, for

example:

this.myProvider.load().then((data) => {

69
console.log(data);
});

Notice that we use then() here because it is a promise that is returned, so we need to wait for the promise

to complete before we can access the data. You might extend this provider further so that it also oered

a save function, i.e:

this.myProvider.save(someData);

Of course, you can use a provider to do other things besides fetching data - but thats a very common use

case.

NOTE: You can auto generate an Injectable in your project using the command ionic g provider

MyProvider

Summary

Weve taken a pretty broad and basic look at how to create the dierent types of classes in Ionic 2, and

theres certainly a lot more to know. But you should know enough now that it wont all look weird and foreign

to you when we start diving into some real examples. when we start diving into some real examples.

70
Lesson 6: Templates

Templates, I think, are one of the most fun bits of Ionic 2. Its where the power of the framework really

shines. Take this code for example:

<ion-header>
<ion-navbar secondary>
<ion-title>
My Friends
</ion-title>

<ion-buttons end>
<button (click)="doSomethingCool()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>
<ion-searchbar (input)="getItems($event)"></ion-searchbar>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-avatar item-left>
<img [src]="item.picture">
</ion-avatar>
<h2>{{item.name}}</h2>
<p>{{item.description}}</p>
</ion-item>
</ion-list>

</ion-content>

71
with no additional styling, the code above would look like this right out of the box:

It doesnt look amazing, but we already have a pretty complex layout set up with just a few lines of code,

throw a bit of custom styling in and wed have a pretty sleek interface. Were going to go through dierent

72
aspects of creating templates in Ionic 2 more thoroughly in just a moment, but I wanted to give you a sense

of what a full template for a page might look like, and also how easy it is to use the components provided

by Ionic.

Theres a lot more to know about template syntax in Ionic 2, and theres been a few signicant changes

from what you might have been used to in Ionic 1 - so were going to dive into templates in a lot of detail.

Well also be covering quite a few other topics after this, but this is the last of what I would consider to

be the core knowledge required to get started with Ionic 2 - once youve got the basics of classes and

templates down you can start jumping into building some stu. So, strap in - were going to start o with

a little theory then get into some practical examples.

The * Syntax

Perhaps one of the most confusing things about the new template syntax in Ionic 2 is this little guy: *.

Youll often come across code that looks like this:

<ion-item *ngFor="let item of items">

or

<p *ngIf="someBoolean"><p>

and so on. In Angular 2 the * syntax is used as a shortcut for creating an embedded template, so if we

use *ngIf as an example, the above could be expanded to:

<template [ngIf]="someBoolean">
<p></p>
</template>

The reason for using templates is that Angular 2 treats templates as chunks of the DOM that can be

dynamically manipulated. So in the case of *ngIf we dont want to just literally render out:

<p *ngIf="someBoolean"></p>

73
to the DOM. We want to render out:

<p></p>

if someBoolean is true, and nothing if it is false. Similarly, if we were to use *ngFor we dont want to

literally render out:

<p *ngFor="let item of items">{{item.name}}</p>

we want to render out paragraph tags stamped with the information for each particular item:

<p>Bananas</p>
<p>More Bananas</p>
<p>Pancakes</p>

So we need to use <template> to allow for this functionality, but writing out templates manually is a lot

of work, and the * syntax just makes this a lot easier.

Now that weve got that out of the way, lets jump into some specics of how to use directives like *ngIf

and *ngFor.

Looping

Quite often you will want to loop over a bunch of items - when you have a list of articles and you want to

render all of the titles into a list for example. We can use the ngFor directive which is supplied by Angular

2 to achieve this - it looks something like this:

<ion-list>
<ion-item *ngFor="let article of articles" (click)="viewArticle(article)">
{{article.title}}
</ion-item>
</ion-list>

In this example, we create an <ion-list> and then for every article we have in our articles array we

add an <ion-item>. I mentioned before (in the basics section) the use of let to create a local variable

74
and we are using that here. This allows us to access whatever article the loop is currently up to, and we

are using that to grab the title of the current article and render it in the list, and also to pass it into the

viewArticle function that is triggered when the item is clicked.

By passing a reference to the current article to the viewArticle function we would then be able to do

something like trigger a new page with the specics of that article on it.

Conditionals

Sometimes you will want to display certain sections of the template only when certain conditions are met,

and theres a few ways to do this.

<div *ngIf="someBoolean">

ngIf will render the node it is attached to only if the expression evaluates to be true. So in this case, if
someBoolean is true, it will be added to the DOM, if it is false then it will not be added to the DOM.

ngIf is great for boolean - true or false - scenarios, but sometimes you will want to do multiple dierent
things based on a value. In that case you can use ngSwitch:

<div [ngSwitch]="paragraphNumber">
<p *ngSwitchWhen="1">Paragraph 1</p>
<p *ngSwitchWhen="2">Paragraph 2</p>
<p *ngSwitchWhen="3">Paragraph 3</p>
<p *ngSwitchDefault>Paragraph</p>
</div>

In this example we are checking the value of paragraphNumber with ngSwitch. Whichever

ngSwitchWhen statement the value matches will be the DOM element that will be rendered, and if none
match the ngSwitchDefault element will be used.

Another method to display or hide certain elements based on a condition is to use the hidden property.

For example:

75
<ion-avatar [hidden]="hideAvatar" item-left>

In this example, if the hideAvatar expression evaluates to be true the element will be hidden, but if it is

false then it will be rendered. Using this method, you would have a this.hideAvatar variable in your

class denition which you could toggle to hide and show these elements.

As well as conditionally displaying an entire element, you could also attach dierent classes to an element

based on a condition, for example:

<ion-avatar [class.my-class]="showMyClass" item-left>

This is a similar concept to the [hidden] method above, but instead of showing and hiding the element

based on a condition, it will add a class you have dened in your CSS based on a condition. This can

come in really handy, for example you might want to use it to style items that have already been read by

the user a dierent colour.

Ionic 2 Template Components

Everything weve covered above is general Angular 2 stu, theres nothing specic to Ionic there (except

for the <ion-list> and <ion-item> elements we used). You will be using this syntax a lot throughout

your templates, along with some Ionic specic components. Were going to go through some of the Ionic

specic stu now, starting with the basic layout of an Ionic 2 page template:

<ion-header>
<ion-navbar>
<ion-title>
Home
</ion-title>
</ion-navbar>
</ion-header>

<ion-content class="home">

76
<ion-card>
<ion-card-header>
Card Header
</ion-card-header>
<ion-card-content>
Hello World
</ion-card-content>
</ion-card>
</ion-content>

This is the automatically generated code you will get for your template if you use the blank layout. Theres

two important components here that are used in just about every template and they are <ion-navbar>

and <ion-content>.

The <ion-content> element is simply used to hold the main content of the page (which in this case is

just a card), and allows for scrolling. Notice that it has a class of home, if you look in the home.scss

le you should also see a home class dened. This doesnt do anything special, its just a convention to

allow you to target styles to that <ion-content> specically (remember, even though you add styles to

the home.scss le they will still apply to the whole application, the separate le is purely for organisation).

The more interesting of the two is <ion-navbar>. This is what adds the header bar to the top of the page,

where you can place the title of the page as well as buttons on the left or right. Its not purely an aesthetic

thing though, it also has a lot of inbuilt smarts for navigation. If you were to push a new page (a concept

we will cover in detail later), then a back button will automatically be added to the <ion-navbar> which

will automatically allow the user to navigate back to the previous page, rather than you having to handle

that manually.

All of the above covers o on the basic template syntax you will see in a lot of your Ionic 2 pages, the

rest is basically just dropping in and conguring various components that Ionic 2 oers (or if youre feeling

adventurous, your own custom components).

Now lets take a look at how to implement a few of Ionics components into our templates. Were not going

77
to be covering anywhere near all of them because there is so many, I just want to give you a taste. For a

full list of all the available components, take a look at the Ionic 2 documentation.

Lists

Lists are one of the most used components in mobile applications, and they provide an interesting chal-

lenge. That smooth scrolling you get when you swipe a list on native applications, with smooth acceleration

and deceleration, and it all just feels right - well thats really hard to replicate. Fortunately, you dont have

to worry about it because Ionic 2 does all the hard stu for you, and using a list is as simple as adding the

following to your template:

<ion-list>
<ion-item>Item 1</ion-item>
<ion-item>Item 2</ion-item>
<ion-item>Item 3</ion-item>
</ion-list>

or if you wanted to dynamically create your list for a bunch of items dened in your class:

<ion-list>
<ion-item *ngFor="let item of items" (click)="itemSelected(item)">
{{item.title}}
</ion-item>
</ion-list>

Slides

Slides are another common element used in mobile applications, slides look like this:

78
where you have multiple dierent images or pages to show and the user can cycle through them by swiping

left or right. Just like lists in Ionic 2, creating slides is really easy as well:

79
<ion-slides [options]="slideOptions">

<ion-slide>
<h2>Slide 1</h2>
</ion-slide>

<ion-slide>
<h2>Slide 2</h2>
</ion-slide>

<ion-slide>
<h2>Slide 3</h2>
</ion-slide>

</ion-slides>

A container of <ion-slides> is used, and then each individual slide is dened with <ion-slide>. It

is also possible to supply some options to dene the behaviour of the slides; whether it should loop and

whether it should have a pager for example (Ill give you a more complete example later).

Input

In Ionic 2, rather than using <input> tags for user input you use the Ionic equivalent which is

<ion-input>. Just like a normal <input> you can specify a type depending on what sort of data you
are capturing, but by using the Ionic versions we will be taking advantage of the custom inputs Ionic has

designed for mobile.

<ion-list>

<ion-item>
<ion-label fixed>Username</ion-label>

80
<ion-input type="text" value=""></ion-input>
</ion-item>

<ion-item>
<ion-label fixed>Password</ion-label>
<ion-input type="password"></ion-input>
</ion-item>

</ion-list>

As well as <ion-input> specically, you will also nd that Ionic provides other input elements like

<ion-select>, <ion-radio>, <ion-checkbox> and <ion-toggle>.

Grid

The Grid is a very powerful component, and you can use it to create complex layouts. If youre familiar

with CSS frameworks like Bootstrap, then it is a very similar concept. When placing components into your

templates, in general things just display one after the other, but with the Grid you can come up with just

about any layout you can imagine.

It works by positioning elements on the page based on rows and columns. Rows display underneath each

other, and columns (which are placed inside of rows) display side by side. For example:

<ion-row>
<ion-col></ion-col>
<ion-col></ion-col>
<ion-row>
<ion-row>
<ion-col></ion-col>
<ion-col></ion-col>
<ion-col></ion-col>

81
<ion-row>

This will create a layout with two rows, the top row will have two columns and the bottom row will have

three columns. By default everything will be evenly spaced, but you can also specify how wide columns

should be if you like:

<ion-row>
<ion-col width-10></ion-col>
<ion-col width-20></ion-col>
<ion-col width-25></ion-col>
<ion-col width-25></ion-col>
<ion-col width-20></ion-col>
</ion-row>

This will create a single row with 5 dierent columns of varying widths (you will probably want to make sure

your column widths add up to 100!).

For a full list of available widths take a look at the documentation.

Icons

Icons are heavily used in most applications today, they are great because they allow you to communicate

what something does rather than relying on text. Its good for usability (most of the time) and looks way

better than using a button that says something like Add Item.

Ionic provides a ton of icons that you can use out of the box, like:

<ion-icon name="heart"></ion-icon>

You just have to specify the name of the icon you want to use. They even have variations of the same icons

so that they display dierently between iOS and Android to better match the style of the platform. For a

full list of available icons you can go here.

82
Theres a ton more default components, and a lot more to know about even the ones Ive mentioned here,

so make sure you have a look through the documentation to familiarise yourself with whats available.

83
Lesson 7: Styling & Theming

One thing I really like about Ionic is that the default components look great right out of the box. Everything is

really neat, sleek and clean but also maybe a little boring. I like simple, simple is great, but you probably

dont want your app to look like every other app out there. Take the example we used in the templates

lesson:

84
We have a simple and clean interface, but its probably not going to win any awards for its design. It uses

the default styling with absolutely no customisation whatsoever. If you take a look at some of the example

apps that Ive included in this course you will see that they mostly all have custom styling:

85
Some of the applications have simple styling, where just a few changes are made to achieve a much more

attractive look. Some of the applications have more complex styling, that completely change the look and

feel of the application.

86
I certainly dont claim to be some design guru, but I think the themes Ive created for these applications are

visually pleasing and help give the apps some character. In this lesson Im going to show you the dierent

ways you can customise your Ionic 2 applications, and the theory behind theming in general.

Introduction to Theming in Ionic 2

When styling an Ionic 2 application, there is nothing inherently dierent or special about it its no dierent

than the way you would style a normal website. I often see questions like:

Can I create [insert UI element / interface] in Ionic?

and the answer is generally yes. Could you do it on a normal webpage? If you can then you can do it in

Ionic as well.

A lot of people may be used to just editing CSS les to change styles, but there is some added complexity

with Ionic, which is primarily due to the fact that it uses SASS. Again, SASS isnt specic to Ionic or mobile

web app development it can also be used on any normal website but many people may not be as

familiar with SASS as they are with plain old CSS.

If youre not already familiar, .scss is the le type for SASS or Syntactically Awesome Style Sheets. If

this is new to you, you should read more about what SASS is and what it does here. For those of you

short on time, what you put in your .scss les is exactly the same as what you would put in .css les, you

can just do a bunch of extra cool stu as well like dene variables that can be reused in multiple areas.

These .scss les are then compiled into normal .css les (its basically the same concept we use in Ionic 2,

where we code using all the fancy new ES6 features, but that is then transpiled into ES5 which is actually

supported by browsers now).

When theming your application, youre mainly going to be editing your .html templates and .scss

stylesheets you will NEVER edit any .css les directly. The .css les are generated from the .scss les,

so if you make any changes to the .css le its just going to get overwritten.

If you take a look at the les generated when you create a new Ionic 2 project, you will see a bunch of

.scss les inside of your theme folder, so lets quickly run through what their purpose is.

87
app.core.scss is the main .scss le. It is used to declare any styles that will be used globally

throughout the application, and this le is also responsible for importing the stylesheets for other

components

app.ios.scss is used to create iOS specic styles

app.md.scss is used to create Android specic styles (md stands for Material Design)

app.wp.scss is used to create Windows Phone specic styles

app.variables.scss is used to modify the apps shared variables. Here you can edit the default

values for things like $colors which sets up the default colours for the application, as well as

$list-background-color, $checkbox-ios-background-color-on and so on. The gen-


eral idea is that Ionic uses the variables dened in this le to determine styling for a lot of components,

so it can be a great place to make quick changes. For a list of all the variables that you can overwrite,

take a look at this page

On top of these .scss les inside of your theme folder, you will also have one for each component you

create (or at least you should). To refresh your memory, most components you create in Ionic 2 will look

like this:

my-component

my-component.ts

my-component.html

my-component.scss

We have the class denition in the .ts le, the template in the .html le and any styles for the component

in the .scss le. Although its not strictly required, you should always create the .scss le for any

components that have styling, rather than just dening the style in the app.core.scss le. If you use the

auto generate commands the Ionic CLI provides then the .scss le will be created automatically for you

anyway.

Why? Well you could just put all of your styles in the app.core.scss le and everything would work exactly

the same, but theres two major benets to splitting your styles up in the way I described above:

Organisation Splitting your code up in this way will keep the size of your les down, making it a lot easier

88
to maintain. Since all of the styles for a particular component can be found in that components .scss le,

youll never have to search around much.

Modularity one of the main reasons for the move to this component style architecture in Angular 2 and

Ionic 2 is modularity. Before, code would be very intertwined and hard to separate and reuse. Now, almost

all the code required for a particular feature is contained within its own folder, and it could easily be reused

and dropped into other projects.

Now that weve gone over the theory, lets look at how to actually start styling our Ionic 2 applications.

Methods for Theming an Ionic 2 Application

Im going to cover a few dierent ways you can alter the styles in your application. It may seem a little

unclear what way to do things, because in a lot of cases you could achieve the same thing multiple dierent

ways. In general, you should try to achieve what you want to do without creating custom styles (which

we will cover last here). Instead you should rst try using the pre-dened attributes or overriding SASS

variables. If it can not be done any other way, then look into creating your own custom styles. Dont worry

too much though, just try to keep things as simple as you can.

1. Attributes

One of the easiest ways to change the style of your application is to simply add an attribute to the element

youre using. As I mentioned above, SASS is used to dene some colours, and these are:

primary

secondary

danger

light

dark

favorite

89
which you can see dened in the app.variables.scss le:

$colors: (
primary: #387ef5,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

As you can see above, Ionic provides some defaults for what these colours are, but you can also override

each of these to be any colours you want. So if you add the primary attribute to most elements it will turn

blue, or if you add the danger attribute it will be a red colour. But if you modied these then primary could

make things purple and danger could make things pink.

To give you an example, if I wanted to use the secondary colour on a button I could do this:

<button secondary></button>

or if I wanted to use the secondary colour on a the nav bar I could do this:

<ion-navbar secondary></ion-navbar>

Keep in mind that these attributes arent limited to just changing the colour of elements, some attributes

will also change things like the position:

<ion-navbar secondary>
<ion-buttons end>
<button primary>I'm a primary coloured button in the end position of the
nav bar</button>
</ion-buttons>
</ion-navbar>

90
The example above uses the end attribute to decide where the buttons should appear. We could also

control whether or not a list should have borders:

<ion-list no-lines></ion-list>

or even whether a list item should display an arrow to indicate that it can be tapped:

<ion-item detail-push></ion-item>

Theres a bunch more of these attributes, so make sure to poke around the documentation when you are

using Ionics in built components. The no-lines attribute is a real easy way to remove lines from a list, but if

you didnt know this attribute existed (which is quite possible) then youd likely end up creating your own

custom styles unneccesarily. This is why I recommend trying to do things with attributes rst if you can,

because you could save yourself a lot of eort.

2. SASS Variables

The next method you can use to control the style of your application is to change the default SASS variables

(like editing the $colors we talked about above). These are really handy because it allows you to make

app wide style changes to specic things. I touched on SASS variables before, but basically in your .scss

les you can do something like this:

$my-variable: red;

and then you could reference $my-variable anywhere in the .scss le. So for example if you wanted

to make the background colour on 20 dierent elements red, rather than doing:

background-color: red;

for all of them, you could instead do this:

background-color: $my-variable;

The benet of this is that now if you wanted to change the background color from red to green, all you

have to do is edit that one variable not every single class you have created. This is why youll nd that

91
variables are named in the manner of primary and danger rather than specically blue and red. There

may come a time when you want to change your primary colour to be purple, but if you give variables

specic names like $my-blue-color and you change it to be purple its going to make your code pretty

confusing.

You probably wont be creating many of your own variables, but Ionic denes and uses a bunch of these

variables, and you can easily overwrite them to be something else. Lets take a look at a few:

$background-color

$link-color

$list-background-color

$list-border-color

$menu-width

$segment-button-ios-activated-transition

You can look at the documentation for more information on these and what they default to, but its pretty

clear by their name what they do. As you can see by the last example there, they even get very specic.

Editing these variables is really simple, just open app.variable.scss and insert your own denitions. Heres

an example app.variable.scss from one of the applications in this book:

$colors: (
primary: #387ef5,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

$list-background-color: #fff;
$list-ios-activated-background-color: #3aff74;
$list-md-activated-background-color: #3aff74;

92
$checkbox-ios-background-color-on: #32db64;
$checkbox-ios-icon-border-color-on: #fff;

$checkbox-md-icon-background-color-on: #32db64;
$checkbox-md-icon-background-color-off: #fff;
$checkbox-md-icon-border-color-off: #cecece;
$checkbox-md-icon-border-color-on: #32db64;

In this example some of the default colours have been changed, and some overrides for specic styles on

both iOS and Android are provided.

Notice the use of md here, this stands for material design and is used for Android. Ionic 2 seamlessly

adapts to the conventions of the platform it is running on with little to no style changes required from you

for Android this means material design is used.

The great thing about editing these default SASS variables is that you can, with one change, make all the

changes necessary everywhere in the app. Some variables use the values of other variables, so if you

wanted to just do this manually with CSS you would probably need to make a lot of edits to get the eect

you wanted.

3. Conguration

Another convenient way to change the styling of your application is through the Cong object that you

can provide to the ionicBootstrap function in app.ts.

In general this is used for setting app wide defaults like the placement of buttons and tabs, the style of

icons to be used, transitions and so on. Usually its best to leave these unaltered unless you have a specic

reason for changing it, since Ionic will adapt to the conventions of the platform it is running on automatically

- messing with the cong could break this.

Sometimes you will want to force things to be a certain way though, and the Cong can be a good way

93
to do that. Heres an example of what it might look like from the documentation:

ionicBootstrap(MyApp, [], {
backButtonText: 'Go Back',
iconMode: 'ios',
modalEnter: 'modal-slide-in',
modalLeave: 'modal-slide-out',
tabbarPlacement: 'bottom',
pageTransition: 'ios'
});

and if you wanted to force iOS to use Material Design you could set the mode using the Cong options:

ionicBootstrap(MyApp, [], {
mode: 'md'
});

Again, Id stress against doing something like this unless you have a good reason. You might like and be

used to material design if youre an Android user, but your users on iOS (and vice versa) will not have the

same view as you. With that in mind, the Cong also allows you to congure things specically for specic

platforms like this:

ionicBootstrap(MyApp, [], {
tabbarPlacement: 'bottom',
platforms: {
ios: {
tabbarPlacement: 'top',
}
}
});

For more information on the Cong object, take a look at the documentation.

94
4. Custom Styles

Before we talked about using attributes to change the colours of elements. Given that you can override

these attributes to whatever you like, its a good approach to set the primary, secondary, danger etc.

variables to match the colour palette of your design, and then use those to set the styles of elements,

rather than dening custom CSS classes.

But, sometimes there will come a time where you need to dene some plain old CSS classes to achieve

what you want. You can either dene these custom classes in app.core.scss if the class will be used

throughout the application, or in an individual components .scss le if it is only going to be used for one

component.

Before these custom styles will take eect, you will need to import them into the core .scss le, so if I

had a component called checklist that had a checklist.scss le, I would need to add the following

line to my app.core.scss le:

@import "pages/checklist/checklist";

Of course, you can also dene custom styles on the element directly by using the style tag, but make sure

you use this sparingly.

As you can see, theres a few dierent ways you can change the styling of your Ionic 2 applications. In

general, its best to do as little as possible to achieve what you need. Try to achieve as much as you can

with attributes and SASS variables, because it will make your life easier.

As I mentioned before, Ionic seamlessly adapts to the UI conventions of both iOS and Android, so the more

hacky or brute force your solution for styling is, the greater chance you have of breaking this behaviour.

95
Lesson 8: Navigation

If you come from an Ionic 1 or Angular 1 background, then you would be used to handling navigation

through routing with URLs, states and so on. The focus in Ionic 2 though is using a navigation stack,

which involves pushing views onto the navigation stack and popping them o. Before we get into the

specics of how to implement this style of navigation in Ionic 2, lets try to get a conceptual understanding

of how it works rst.

Pushing and Popping

Imagine your root page is a piece of paper that has a picture of a cat on it, and you put that piece of paper

on a table. It is the only piece of paper currently on the table and you are looking down on it from above.

Since it is the only piece of paper on the table right now, of course you can see the picture of the cat:

Now lets say you want to look at a dierent piece of paper (i.e. go to a dierent page), to do that you can

push it onto the stack of papers you have. Lets say this one is a picture of a dog, you take that piece of

paper and place it over the top of the picture of the cat:

96
The cat is still there, but we cant see it anymore because it is behind the dog. Lets take it even further

and say that now you want to push another piece of paper, a cow, it would now look like this:

Both the cat and the dog are still there, but the cow is on top so that is what we see. Now lets reverse

97
things a bit. Since all of the pieces of paper are stacked in the order they were added we can easily cycle

back through them by popping. If you want to go back to the picture of the dog you can pop the stack

of papers, removing the piece of paper that is currently on top (the cow). If you want to go back to the

picture of the cat you can pop the stack of papers once more to remove the piece of paper that is now on

top (the dog). Now were back to where we started.

Im sure you can see how this style of navigation is convenient for maintaining history and it makes a lot of

sense when navigating to child views, but it doesnt always make sense to push or pop. Sometimes you

will want to go to another page without the ability to go directly back to the page that triggered the change

(a login screen that leads to the main app for example, or even just dierent sections of an app available

through a menu).

In this case, we could change the root page which, given our pieces of paper on the table analogy, is like

disregarding the other stack of papers we have and just focusing on a new piece of paper on the table:

In the example above, Ive set the cow page as the root page, so rather than being on top of the other

pages, its all by itself.

98
At rst, it may be hard to understand whether you should set the root page to navigate to a dierent page

or push the view. In general, if the view you want to switch to is a child of the current view, or if you want

the ability to navigate back to the previous view from the new view, you should push. For example, if I was

viewing a list of artists and tapped on one I would want to push the details page for that artist. If I was

going through a multi-page form and clicked Next to go to page 2 of the form, I would want to push that

second page.

If the view you are switching to is not a child of the current view, or it is a dierent section of the application,

then you should instead change the root page. For example, if you have a login screen that leads to

the main application you should change the root page to be your main logged in view once the user

has successfully authenticated. If you have a side menu with the options Dashboard, Shop, About and

Contact you should set the root page to whichever of these the user selects.

Keep in mind that the root page is dierent to the root component, typically the root component (which

is dened in app.ts) will declare what the root page is the root page can be changed throughout the

application, the root component can not.

Basic Navigation in Ionic 2

Ok, weve gone through the theory so now were going to get into a more practical Ionic 2 example and

look at how to push, pop, set the root page and even how to pass data between pages.

An important part of all this is the NavController which is provided by Ionic. You will often see this imported

in Ionic 2 applications:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';

@Component({
templateUrl: 'build/pages/home/home.html',
})
export class MyPage {

99
constructor(public nav: NavController) {

}
}

We inject the NavController and a reference to it is created so that we can use it anywhere within the class.

As you might have been able to guess, the NavController helps us control navigation - so lets take a look

at how to do just that by pushing and popping.

To push a page, which will take a page and put it on top of the navigation stack (which sets it as the current

page), you can do something like this:

this.nav.push(SecondPage);

This uses the reference to the NavController we created before, and all you need to supply to it is a

reference to the page that you want to navigate to, which you will need to make sure you also import at

the top of the le, like this:

import {SecondPage} from './pages/second/second';

and thats it, your app should switch to the new page whenever the push code is triggered. When you

push a page, a Back button will automatically be added to the nav bar (assuming you have one), so you

often dont need to worry about using pop to navigate back to the previous page since the Back button

does this automatically for you.

There may be circumstances where you do want to manually pop a page o of the navigation stack though,

in which case you can use this:

this.nav.pop();

Easy enough right? As I mentioned before there is still another way to change the page and that is by

setting the root page. If you take a look at your app.ts le you will notice the following line:

rootPage: any = MyPage;

100
Declaring rootPage in the root component will set the root page, and thats because the template for the

root component looks like this:

<ion-nav [root]="rootPage"></ion-nav>

So were setting the root property on <ion-nav> to be whatever rootPage is dened as. To change

the root page at any point throughout the application, you can use our friend the NavController all you

have to do is call the setRoot function like this:

this.nav.setRoot(SecondPage);

Passing Data Between Pages

A common requirement of mobile applications is to be able to pass data between pages. One really

common example is when using the Master Detail pattern, which is basically where you have a list of

items and then you click on one to go to another page where it displays more details about that item. When

navigating to the detail page, were going to need to know which item we are displaying data for, which

will involve passing in data from the previous page. In Ionic 2 this can be done using NavParams. First,

you must pass through the data you want within the push call (this can also be done when using setRoot):

this.nav.push(SecondPage, {
thing1: data1,
thing2: data2
});

This is exactly the same as what we were doing before, except now there is an extra parameter which is

an object that contains the data we want to send through to SecondPage. Then on the receiving page we

need to import NavParams and inject it into our constructor:

import {Component} from '@angular/core';


import {NavController, NavParams} from 'ionic-angular';

@Component({

101
templateUrl: 'build/pages/second/second.html'
})
export class SecondPage {
constructor(nav: NavController, navParams: NavParams){

}
}

Then you can grab the data that was passed through by doing the following:

this.navParams.get('thing1');

Navigation Components

Some of the components that Ionic provides also eect navigation in some way. These arent really core

navigation concepts, but they will have an impact on navigation in your application. So lets cover what

these are and when you might want to use them.

Modals

Youre probably familiar with the concept of a modal already. In web development, a modal is basically

some box that pops up on the screen and covers the content behind it. Usually modals have the lightbox

style, with a blacked out background and the focus on the content area.

A Modal in Ionic is similar, in that it pops up on top of your content, but it doesnt actually look any dierent

to a normal page. Generally you would want to use a modal, rather than pushing a page, when you want

to give the user the ability to launch and then dismiss (close) a view, rather than navigating back to the

previous page.

One cool thing about Modals are that they give you the ability to pass some data back to the page that

launched it when the Modal is dismissed. For example, you can create a Modal like this (remember to

102
import Modal as well!):

let myModal = Modal.create(MyPage);

this.nav.present(myModal);

Notice that the modal is presented using the NavController, rather than being pushed onto the naviga-

tion stack. Now if we wanted to allow some data to be passed back from that modal, we could add an

onDismiss handler to it before presenting it:

let myModal = Modal.create(MyPage);

myModal.onDismiss(data => {
console.log(data);
});

this.nav.present(myModal);

Now when the modal is dismissed it will pass back a data object that we can do something with. To

dismiss a modal, all you have to do is call the following code inside of the Modal:

this.view.dismiss();

where this.view is a reference to the ViewController which is kind of like the NavController and also

needs to be imported and injected into your constructor. If we want to pass data back to that onDismiss

handler though, we will need to do something like this:

let data = {
thing1: "value1",
thing2: "value2"
};

this.view.dismiss(data);

103
Now the data object will be passed back to the onDismiss handler from the page that launched the

modal.

Tabs

Tabs are a very popular component that have a big impact on how navigation works in your application.

Using tabs is really simple, basically in your template you will create something like this:

<ion-tabs>
<ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="navigate"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="person"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Tab 3" tabIcon="bookmarks"></ion-tab>
</ion-tabs>

and then in your class denition you just dene the pages to be used as the tabs like this:

tab1Root: any = TabOne;


tab2Root: any = TabTwo;
tab3Root: any = TabThree;

constructor(){

Notice that each tab has its own root page. You can think of switching tabs as switching between dierent

root pages, and then you can push and pop pages in each tab. With a tab layout, you can switch between

dierent tabs, but each tab will still maintain its own history.

Sidemenu

A side menu doesnt really do anything out of the ordinary in terms of navigation, the side menu is really

just a UI element but its a convenient, and common, place to add buttons that allow the user to navigate to

104
another page (the actual switching of pages is just done manually with setRoot or push though). Adding a

side menu to your application is super easy, you just need to modify the template of your root component

to include it like this:

<ion-menu [content]="content">

<ion-content>
<ion-list>
<button ion-item (click)="openPage(homePage)">
Home
</button>
</ion-content>
</ion-menu>

<ion-nav id="nav" #content [root]="rootPage"></ion-nav>

We use <ion-menu> to create a menu, and we also have to tell it what to attach itself to. This is why we set

the [content] property to content which is a reference to the local variable we created on the <ion-nav>

by adding #content. So this is basically saying that the <ion-nav> is our main content area, and we

want the menu to attach to that.

There is a bit to learn about navigation in Ionic 2, but once youve got a handle on the basics discussed in

this lesson you should be able to get by in most circumstances without too much trouble.

105
Lesson 9: User Input

Not all mobile applications require user input, but many do. At some point, youre going to want to collect

some data from your users. That might be some text for a status update, their name and shipping address,

a search term, a title for their todo list item or anything else.

Whatever the data is, the user is going to be entering it into one of the templates in your application. To

give you an example, in Ionic 2 we could create a form in our template with the following code:

<ion-list>

<ion-item>
<ion-label>Username</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label>Password</ion-label>
<ion-input type="password"></ion-input>
</ion-item>

</ion-list>

which would produce a simple login form that looks like this:

106
This would allow the user to enter some information into these input elds. However, we need to know

how to get the data that is being entered into our .html template and make use of it in our .ts class. In

this lesson we are going to discuss a couple of dierent ways you can do that.

Two Way Data Binding

This concept will be very familiar to you if youve previously used Ionic 1, if not then dont worry because

its pretty straightforward concept. Two way data binding essentially links a value of an input eld in the

template, to the value of a variable in the class. Take the following example:

Template:

<ion-input type="text" [(ngModel)]="myValue"></ion-input>

107
Class:

constructor(){
this.myValue = 'something';
}

If we changed the value of the input in the template, then the this.myValue variable in the class will

be updated to reect that. If we change the value of this.myValue in the class, then the input in the

template will be updated to reect that. By using ngModel the two values are tied together, if one changes,

the other changes.

Lets say you also had a submit button in your template:

<ion-input type="text" [(ngModel)]="myValue"></ion-input>

<button (click)="logValue()">Log myValue!</button>

When the user clicks the button we want to log the value they entered in the input to the console. Since

the button calls the logValue function when it is clicked, we could add that to our class:

logValue(){
console.log(this.myValue)
}

This function will grab whatever the current value of the input is and log it out to the screen. Rather than

logging it out to the screen, you could also do something useful with it. This can be a convenient way to

handle input, because we dont need to worry about passing the values through a function, we can just

grab the current values whenever we need.

It becomes a bit cumbersome when we have a lot of inputs though, so its not always the perfect solution.

When dealing with more complex forms, we also have another option, which we will discuss now.

108
Form Builder

Form Builder is a service provided by Angular 2, which makes handling forms a lot easier. Theres quite a

lot Form Builder can do but at its simplest it allows you to manage multiple input elds at once and also

provides an easy way to validate user input (i.e. to check if they actually did enter a valid email address).

To use Form Builder it needs to be imported and injected into your constructor, e.g:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {FormBuilder, Validators} from '@angular/common';

@Component({
templateUrl: 'build/pages/my-details/my-details.html',
})
export class MyDetailsPage {

constructor(public formBuilder: FormBuilder) {

Notice that Validators are also being imported here, which are what allow you to validate user input with

Form Builder. Lets cover a really quick example of how you can use Form Builder to build a form. The

most important dierence with this method is that your inputs will have to be surrounded by a <form> tag

with the ngFormModel property dened:

<form [ngFormModel]="myForm" (submit)="saveForm($event)">

<ion-item>
<ion-label stacked>Field 1</ion-label>

109
<ion-input ngControl="field1" type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Field 2</ion-label>
<ion-input ngControl="field2" type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Field 3</ion-label>
<ion-input ngControl="field3" type="text"></ion-input>
</ion-item>

<button type="submit">Save Form</button>

</form>

You will also notice in the example above that we have dened the ngControl attribute on all of our inputs,

this is how we will identify them with Form Builder in just a moment. Of course we need a way to submit

the form, so weve added a submit button and have also added a (submit) listener on the form which

calls the saveForm function. We will dene that function in a moment, but for any of this to work we rst

need to initialise the form in the constructor function for the page. This will look something like this:

this.myForm = formBuilder.group({
field1: [''],
field2: [''],
field3: ['']
});

We simply supply all of the elds that are in the form (using the ngControl names we gave them) and

provide the elds with an initial value (which we have left blank in this case). You can also supply a second

110
value to each eld to dene a Validator if you like, e.g:

this.myForm = formBuilder.group({
field1: ['', Validators.required],
field2: ['', Validators.required],
field3: ['']
});

Also note that the variable this.myForm has to be the same as the value we supply for [ngFormModel]

in the template. Now lets look at that saveForm function.

saveForm(event){
event.preventDefault();
console.log(this.myForm.value);
}

We pass through the submit event and then call preventDefault so that the default action for submitting

a form doesnt occur, we just want to handle it ourselves with this function. To grab the details that the

user entered into the form we can simply use this.myForm.value which will contain all the values that

the user entered.

Setting up forms using Form Builder is a little more complex, but its much more powerful and worth the

eort for more complex forms. For more simple requirements, using [(ngModel)] is ne in most cases.

111
Lesson 10: Saving Data

Lets say youve create an Ionic 2 application where users can create shopping lists. A user downloads

your application, spends 5 minutes adding their list and then closes the application and all their data is

gone.

Quite often when creating mobile applications you will want to store data that the user can retrieve later. A

lot of the time this is done by storing the data somewhere that can be accessed through a remote server

(think Facebook, Twitter etc.) which would require an Internet connection and fetching the data remotely

(which we will discuss in the next lesson). In many cases though, we may also want to store some data

locally on the device itself.

Theres a few reasons we might want to store some data locally:

The application is completely self contained and there is no interaction with other users, so all data

can be stored locally

We need to store data locally for some specic functionality like remembering logged in users

We could skip unnecessary calls to a server by storing preference settings locally

We could skip unnecessary calls to a server by caching other data locally

We want to sync online and oine data so that the user can continue using the application even

when they are oine (Evernote is a good example of this)

HTML5 applications run in the browser, so we dont have access to the storage options that native appli-

cations do. We do still have access to the usual browser storage options that websites have access to

though like Local Storage, Web SQL (deprecated) and IndexedDB. These options might not always be

ideal (for reasons I will cover shortly), but we can also access native data storage by using Cordova which

can overcome the shortfalls of browser based data storage.

There are plenty of dierent options out there for storing data locally, but we are going to focus on the main

ones when it comes to Ionic 2 applications, and they are Local Storage and SQLite.

112
Local Storage

This is the most basic storage option available, which allows you to store up to 5MB worth of data in the

users browser. Remember, Ionic 2 applications technically run inside of an embedded browser.

Local storage gets a bit of a bad wrap, and is generally considered to be unreliable. I think the browsers

local storage can be a viable option and it is reasonably stable and reliable, but, it is possible for the data

to be wiped, which means for a lot of applications its not going to be a great option. Even if it worked

99% of the time, thats still not good enough for storing most types of data.

In general, you should only use it where data loss would not be an issue, and it shouldnt ever be used

to store sensitive data since it can be easily accessed. One example of where local storage might be a

suitable option is if you wanted to store something like a temporary session token. This would allow you

to tell if a user was already logged in or not, but if the data is lost its not really a big deal because the user

will just need to enter in their username and password again to reauthenticate.

If you are just using local storage to cache data from a server it would also not be a big issue if the data is

lost since it can just be fetched from the server again.

Local Storage is a simple key-value system, and can be accessed through the globally available

localStorage object:

localStorage.setItem('someSetting', 'off');

let someSetting = localStorage.getItem('someSetting');

This is the native (as in native to web browsers, not iOS or Android native) way to set and retrieve local

storage data, but Ionic 2 provides its own service for interacting with local storage, which adds promise

support.

If you want to use local storage in Ionic 2 you would do something like this:

import {Component} from '@angular/core';


import {Storage, LocalStorage} from 'ionic-angular';

113
@Component({
template: `<ion-content></ion-content>`
})
export class MyClass{

local: Storage;

constructor(){
this.local = new Storage(LocalStorage);
this.local.set('didTutorial', true);
}
}

This is the example provided in the documentation, as you can see both Storage and LocalStorage need

to be imported, and then a new instance of Storage is created with LocalStorage supplied as the storage

engine. Once that storage object has been created you can interact with it using the get and set methods.

Keep in mind that retrieving items from storage will return a promise, so the correct syntax to use would

look like this:

this.local.get('didTutorial').then((result) => {
console.log(result);
});

SQLite

SQLite is basically an embedded SQL database that can run on a mobile device. Unlike a normal SQL

database it does not need to run on a server, and does not require any conguration. SQLite can be utilised

by both iOS and Android applications (as well as others), but the SQLite database can only be accessed

natively, so it is not accessible by default to HTML5 mobile apps.

114
We can however use a Cordova to easily gain access to this functionality. Simply run the following com-

mand in your project to install it:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

The main benets of using SQLite are that it:

Provides persistent data storage

There is no size limitation on how much can be stored

It provides the SQL syntax, so it is a very powerful tool for managing data

Although there are some dierences in supported commands between SQL and SQLite, it is almost exactly

the same. Heres an example of how you might execute some simple queries in SQLite:

var db = window.sqlitePlugin.openDatabase({name: "my.db"});

db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary
key, data text, data_num integer)');

tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)",


["test", 100], function(tx, res) {
console.log("insertId: " + res.insertId + " -- probably 1");
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");

}, function(e) {
console.log("ERROR: " + e.message);
});
});

The example above looks pretty freaky, but if youre familiar with SQL then at least some of it should look

familiar. This is the standard way to use SQLite with Cordova, but Ionic also provides its own service for

115
using SQLite.

In order to use the SQLite service provided by Ionic 2 you will rst need to make sure you have the SQLite

plugin installed:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

and then youll have to set it up in your project, much like we just did with the Local Storage service:

import {Component} from '@angular/core';


import {Storage, SqlStorage} from 'ionic-angular';

@Component({
template: `<ion-content></ion-content>`
})
export class MyClass{

storage: Storage;

constructor(){
this.storage = new Storage(SqlStorage, {name: 'dbname'});

this.storage.set('name', 'Max');
this.storage.get('name').then((name) => {
console.log(name);
});

this.storage.query('insert into projects(name, data) values("Cool


Project", "blah")');
this.storage.query('select * from projects').then((response) => {
console.log(response);
});

116
}
}

Its basically the same idea here except we supply SqlStorage as the storage engine instead. Getting and

setting data isnt really any dierent, but obviously we can now run more complex queries for inserting and

retrieving data as well.

The cool thing about this service is that it falls back to using browser based storage if SQLite is not available.

You should always remember to install the SQLite plugin so SqlStorage makes use of it, but if youre

testing your application through a browser where SQLite isnt available then everything should still work

pretty smoothly.

As I mentioned, there are dierent storage options available (and depending which package you bought,

we may discuss this further later) but in general, LocalStorage and SqlStorage will serve you well in most

cases.

117
Lesson 11: Fetching Data, Observables and Promises

Although some mobile applications are completely self contained (like calculators, soundboards, todo lists,

photo apps, ashlight apps), many applications rely on pulling in data from an external source to function.

Facebook has to pull in data for news feeds, Instagram the latest photos, weather apps the latest weather

forecasts and so on.

In this section we are going to cover how you can pull external data into your Ionic applications. Before

we get into the specics of how to retrieve some data from a server somewhere, I want to cover a little

more theory on a few specic things that we are going to be making use of.

Mapping and Filtering Arrays

The map and lter functions are very powerful and allow you to do a lot with arrays. These are not fancy

new ES6 or Angular 2 features either, theyve been a part of JavaScript for a while now.

To put it simply, map takes every value in an array, runs the value through some function which may change

the value, and then places it into a new array. It maps every value in an array into a new array. To give you

an example of where this might be useful, you might have an array of lenames like this:

['file1.jpg', 'file2.png', 'file3.png']

You could then map those values into a new array that contains the full path to the les:

['http://www.example.com/file1.jpg', 'http://www.example.com/file2.png',
'http://www.example.com/file2.png']

Doing that might look something like this:

let oldArray = ['file1.jpg', 'file2.png', 'file3.png'];

let newArray = oldArray.map((entry) => {


return 'http://www.example.com/' + entry;

118
});

So we supply the map with a function that returns the modied value. The function that we provide will

have each value passed in as a parameter.

A lter is very similar to a map, but instead of mapping each value to a new array, it only adds values that

meet a certain criteria to the new array. Lets use the same example as before, but this time we want to

return an array that only contains .png les. To do that, we would use lter like this:

let oldArray = ['file1.jpg', 'file2.png', 'file3.png'];

let newArray = oldArray.filter((entry) => {


return entry.indexOf('.png') > -1;
});

Suppose we still want to have the full path as well though. Fortunately, we can quite easily chain lter and

map like this:

let oldArray = ['file1.jpg', 'file2.png', 'file3.png'];

let newArray = oldArray.filter((entry) => {


return entry.indexOf('.png') > -1;
}).map((entry) => {
return 'http://www.example.com/' + entry;
});

So now we are rst ltering out the results we dont want, and then mapping them to a new array with the

full le path. The result will be an array containing the full paths of only the two .png les. Im going to

leave it there for now, but when we get into an example of how to fetch data soon it will become very clear

why it was worth explaining how these work.

119
Observables and Promises

If youve used Ionic 1 or have a reasonably strong background in Javascript then you would probably be

familiar with Promises, but far fewer people are familiar with Observables. Observables are one of the

core new features included Angular 2 (provided by RxJS) so it is important to understand what they are

and how they are dierent to Promises (they do look and behave very similar).

Before we get into Observables, lets cover what a Promise is at a very high level. Promises come into play

when we are dealing with asynchronous code, which means that the code is not executed one line after

another. In the case of making a HTTP request for data, we need to wait for that data to be returned, and

since it might take 1-10 seconds for it to be returned we dont want to pause our entire application whilst

we wait. We want our application to keep running and accepting user input, and when the data from the

HTTP request becomes available to us, then we do something with it.

A Promise handles this situation, and if youre familiar with callbacks its basically the same idea, just a

little nicer. Lets say we have a method called getFromSlowServer() that returns a promise, we might

use it like this:

getFromSlowServer.then((data) => {
console.log(data);
});

We call the then method which a Promise provides, which basically says Once you have the data from

the server, do this with it. In this case we are passing the data returned into a function where we log it

out to the console. So our application will go about doing whatever else it has to do and when the data is

available it will execute the code above. You could think of it like being at work and writing some report,

you need some additional information so you ask your assistant to go nd it for you, but you dont just sit

there and wait for the assistant to get back - you keep writing your report and when the assistant returns

then you use the information.

We understand what a Promise is now, so whats an Observable and what does it do that Promises dont?

An Observable serves the exact same purpose as a Promise, but it does some extra stu too. The main dif-

120
ference between a Promise and an Observable is that a Promise returns a single result, but an Observable

is a stream than can emit more than one value over time. It might be easier to think of Observables as

streams, because they are, they are just called Observables because the stream is observable (as in, we

can detect values that are emitted from the stream).

An Observable looks a lot like a Promise, but instead of using the then method we use the subscribe

method. Since a Promise only returns a single value, it makes sense to have that value returned and then

do something. As I mentioned, an Observable is a stream that can emit multiple values, so it makes sense

to subscribe to it (like your favourite YouTube channel), and run some code every time a value is emitted.

It might look something like this:

someObservable.subscribe((result) => {
console.log(result);
});

It is obvious that our program would need to wait for data to be returned when making a HTTP request,

and thus Promises and Observables would be useful. Its not the only instance of where you will need to

program asynchronously though. There are some less obvious situtations like fetching locally stored data,

or even getting a photo from the users camera, where you would also need to wait for the operation to

nish before using the data.

If you want to go more indepth into everything weve discussed above, I highly recommend this interactive

tutorial. It introduces RxJS which includes Observables, but also builds up a solid foundation of how to

use map, lter and other functions. If youd also like to dive into some more specics about how an

Observable diers from a Promise, I highly recommend this egghead.io video.

Using Http to Fetch Data from a Server

Ok, you should be armed with all the theory you need now - lets get into an example. Were going to

use the Reddit API to demonstrate here because it is publicly accessible and very easy to use. If youve

purchased one of the packages for this book that includes the Giist application then we will be exploring

this in a lot more detail later.

121
You can create a JSON feed of posts from subreddits simply by visiting a URL in the following format:

https://www.reddit.com/r/gifs/top/.json?limit=10&sort=hot

If you click on that link, you will see a JSON feed containing 10 submissions from the gifs subreddit, sorted

by the hot lter. If youre not familiar with JSON, I would recommend reading up on it here but essentially

it stands for JavaScript Object Notation and is a great way to transmit data because it is very readable to

humans, and is also easily parsed by computers. If youve ever created a JavaScript object like this:

var myObject = {
name: 'bob',
age: '43',
hair: 'purple'
};

then you should be able to read a JSON feed pretty easily once you tidy it up a little. But how do we get

it into our Ionic 2 application?

The answer is to use the Http service which is provided by Angular 2, and allows you to make HTTP

requests. If youre not familiar with what a HTTP request is, basically every time your browser tries to load

anything (a document, image, a le etc.) it sends a HTTP request to do that. So we can make a HTTP

request to a page that spits out some JSON data, and pull that into our application.

First we need to set up the Http service, so lets take a look at a test page that has that service imported

and injected into the constructor:

import {Component} from '@angular/core';


import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {

122
constructor(public http: Http) {

}
}

Since we have injected the Http service into our constructor and made it available through this.http

by using public, we can now make use of it anywhere in this class. Also note that we are importing the

map operator from the RxJS library. As I mentioned before map is a function that is provided by default on

arrays - so why would we need to import it from some weird library? Its because the Http service doesnt

return an array, it returns an Observable. The RxJS library makes the map function available for us on

Observables, but we need to import it rst.

Now lets take a look at how we might make a request to a reddit URL:

import {Component} from '@angular/core';


import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {

constructor(public http: Http) {

this.http.get('https://www.reddit.com/r/gifs/new/.json?limit=10').map(res
=> res.json()).subscribe(data => {
console.log(data);
});
}

123
}

The rst part of the call returns us an Observable. Then we make use of the map function, what were

doing here is taking the plain text JSON response (which is just a string) and converting it into a JavaScript

object by calling the json()function. This makes it much more friendly for us to play with.

IMPORTANT: Remember, Http requests are asynchronous. This means that your code will continue exe-

cuting whilst the data is being fetched, which would take anywhere from a few milliseconds, to 10 seconds,

to never. So its important that your application is designed to deal with this. To give you an example, if

you were to run the following code:

this.posts = null;

this.http.get('https://www.reddit.com/r/gifs/top/.json?limit=2&sort=hot')
.map(res => res.json()).subscribe(data => {
this.posts = data.data.children;
});

console.log(this.posts);

You would see null output to the console. But if you were to run:

this.posts = null;

this.http.get('https://www.reddit.com/r/gifs/top/.json?limit=2&sort=hot')
.map(res => res.json()).subscribe(data => {
this.posts = data.data.children;
console.log(this.posts);
});

You would see the posts output to the console, because everything inside of the subscribe function will

only run once the data has been returned.

124
Getting back to our example, after we map the response we chain a subscribe call which allows us to do

something with the data that is emitted from the stream (Observable). As I mentioned above an Observable

is useful because we can listen for multiple values over time but why use it here? The Http call is only ever

going to return one result, why doesnt it just use a Promise instead of an Observable and save everyone

the confusion?

The reason for a bit of favouritism of Observables over Promises is that an Observable can do everything

a Promise can, its technically better behind the scenes, and it can do extra fancy things that Promises

cant. We could set up an interval for example so that the Http call res every 5 or 10 seconds, we can

easily set up debouncing which ensures that a request is not red o too frequently (and subsequently

making a ton of requests to a server), Observables can cancel old in ight requests if a new request is

made before the result of the old request is returned and a whole bunch of other things.

Take this example from the Giist application:

this.subredditControl.valueChanges.debounceTime(1000)
.distinctUntilChanged().subscribe(subreddit => {
this.subreddit = subreddit;

if(this.subreddit != ''){
this.changeSubreddit();
}
});

In this case subredditControl is an Observable. The value of this can be controlled by the user using

an input in the application. Its set up though so that the code inside the subscribe call will only run if

there has been no change for more than 1 second (by using debounceTime) and it will also only run when

a distinct value is supplied. I think this example shows well how a whole bunch of weird and useful stu

can be chained before the subscribe call to do a lot of useful things.

If youre looking at the example above and thinking Wow Ionic 2 is way too hard and confusing, take

me back to Ionic 1! then dont. This is a pretty advanced example using Observables to demonstrate a

125
point, Ionic 2 input handling and two way data binding is just as easy as Ionic 1.

Observables are a huge topic, so there is a ton to learn. Dont feel intimidated if you dont really have much

of an idea of whats going on though. Having a better understanding of Observables will help you when

creating Ionic applications, but they are a reasonably small part of Ionic (well, they are a big part but you

wont really have to deal with them much) and you can get by just ne by having just a basic understanding.

Fetching Data from your Own Server

We know how to pull in data using a JSON feed like the one provided by reddit, but what if you want to

pull in your own data? How can you go about setting up your own JSON feed?

Going into the detail of how to set up your own API is a bit beyond what I wanted to achieve with this

lesson, but I would like to give you a high level overview of how its done. Basically:

1. Make a request from your Ionic application to a URL on your server

2. Fetch the data using whatever server side language you prefer

3. Output the required data to the page in JSON format

Ill quickly walk you through the steps of how you might implement a simple API with PHP, but you could

use whatever language you want - as long as you can output some JSON to the browser.

1. Create a le called feed.php that is accessible at http://www.mywebsite.com/api/feed.php

2. Retrieve the data. In this case Im doing that by querying a MySQL database but the data can come

from anywhere:

$mysqli = new mysqli("localhost", "username", "password", "database");


$query = "SELECT * FROM table";
$dbresult = $mysqli->query($query);

while($row = $dbresult->fetch_array(MYSQLI_ASSOC)){
$data[] = array(
'id' => $row['id'],

126
'name' => $row['name']
);
}

if($dbresult){
$result = "{'success':true, 'data':" . json_encode($data) . "}";
}
else {
$result = "{'success':false}";
}

3. Output the JSON encoded data to the browser:

echo($result);

4. Use http://www.mywebsite.com/api/feed.php in your http.get() call in your applica-

tion

As I mentioned you can use whatever language and whatever data storage mechanism you like to do this.

Just grab whatever data you need, get it in JSON format, and then output it to the browser.

This should give you a pretty reasonable overview of how to fetch remote data using Ionic 2 and the Http

service. The syntax and concepts might be a little tricky to get your head around at rst, but once youve

got the basics working theres not really much more you need to know. Your data might get more complex

and you might want to perform some fancier operations on it or display it in a dierent way, but the basic

idea will remain the same.

127
Lesson 12: Native Functionality

The problem with web based mobile applications is that whilst we can run them on iOS and Android

through a web browser, users cant install them natively on their device and the app can not access native

APIs like Contacts, Bluetooth and so on. This is why we use Cordova in conjunction with Ionic, Cordova

allows us to wrap our applications in a native wrapper which allows for submission to app stores as well

as communication with native APIs through plugins. When using Cordova, a HTML5 mobile application

can do just about anything a normal native application can do.

As I just mentioned, to access native functionality we need to use plugins. Cordova provides a bunch of

default plugins which include:

Device

Network Information

Camera

Geolocation

File

In App Browser

Media

Splash Screen

But there are hundreds of open sourced plugins developed by the community to do just about everything,

a few popular examples being:

Local Notications

Facebook Connect

SQLite

Social Sharing

Basically what a plugin does is create an interface where Javascript code can trigger native calls. So if

you ever run into a situation where you need a Cordova plugin that doesnt exist yet (which is pretty rare),

you can even write it yourself (but it does involve writing native code).

128
IMPORTANT: Most plugins only work when running on a real device, so if you are trying to test a Cordova

plugin through ionic serve you will likely receive errors.

Using Cordova Plugins in Ionic 2

Theres two ways to implement native functionality in Ionic 2. You can just use any Cordova plugin directly

by installing the plugin in your project:

ionic plugin add plugin-name

and then accessing the functionality that the plugin provides, which is usually available on a global object

like this:

window.plugins.somePlugin.someMethod();

Nothing needs to be imported, required, called from a specic section of code or anything else - once

you have installed the plugin through the command line you will be able to access it from anywhere. Not

all plugins will be accessible exactly like this, but its how most plugins work. This is not specic to Ionic

2, you can use Cordova plugins in this manner in any Cordova project (the only dierence being that you

would use cordova plugin add instead of ionic plugin add). When using the normal Cordova

syntax, using a plugin in Ionic 2 is no dierent than using it in Ionic 1, Sencha Touch, jQuery Mobile or a

normal web page built with Cordova.

Keep in mind that if you use Cordova plugins in this way, your application may fail to compile due to

TypeScript warnings. This is because TypeScript does not know what it is, and you may need to install

typings for it. To brute force your way past this, you can simply add:

declare var variableCausingProblems;

above the decorator in the class that you are using the plugin in.

Alternatively, you can use Ionic Native to make use of Cordova plugins, which is specic to Ionic 2. If

youre familiar with ngCordova from Ionic 1 then this is basically the same thing, just for Ionic 2. If youre

129
not familiar with ngCordova, Ionic Native basically just makes Cordova plugins play a little bit more nicely

with Angular 2, by adding support for Promises and Observables.

Ionic Native is installed by default in all Ionic 2 applications, so all you need to do is install the plugin you

want to use, just like you would normally, for example:

ionic plugin add cordova-plugin-geolocation

Next you will need to import the plugin from Ionic Native into the class you want to use it in:

import {Geolocation} from 'ionic-native';

and then you can use it in your code:

Geolocation.getCurrentPosition().then((resp) => {
console.log("Latitude: ", resp.coords.latitude);
console.log("Longitude: ", resp.coords.longitude);
});

Notice that in the code above a promise is returned and we set up a handler using .then(), if we were

just using the standard Cordova syntax this wouldnt be possible - we would instead have to use callback

functions, which are a bit messier.

Its also important to note that not all Cordova plugins are available in Ionic Native. For a list of all of the

available plugins, and how to use them, you should check the Ionic Native documentation. If a plugin you

want to use is not available in Ionic Native, then you can just go back to using the standard Cordova syntax

(or you can add it to Ionic Native yourself).

Although it is not required, you should use Ionic Native wherever possible. Itll make your code cleaner,

and it makes much more sense in the Angular 2 ecosystem (and typings will be handled automatically this

way, so TypeScript wont complain). Using plain old Cordova is not a crime though, so dont feel too bad

about it.

130
Chapter 3

Quick Lists

131
Lesson 1: Introduction

Quick Lists is the de facto step-by-step tutorial application for this course - no matter which of the packages

you purchased, you will have access to this lesson. The reason I chose Quick Lists to ll this role is because

it covers a broad range of the core concepts in Ionic 2, and the skills you learn throughout building this

application will be used frequently in most other applications you create.

A lot of people (myself included) create todo application tutorials when explaining some new technology

or framework, the reason for this is usually because a todo application covers most of the basic things you

would want to do in an application, for example:

General strucutre & setup

User Interface

Creating, reading, updating and deleting data

Accepting user input

These are all obviously important concepts that need to be covered, but I really wanted to avoid building

another todo application for this book - I wanted to do something that was just a little bit more complex

and interesting. The result is pretty similar and covers the same bases as a todo application would, but I

think its a little more fun to build.

About Quick Lists

The idea for Quick Lists came from a personal need of mine. At the time of writing this I am currently working

remotely and traveling around Australia in a caravan. Its certainly a great experience, but essentially

lugging your entire house around the country (and its a big country) every week or so comes with some

complications.

One particularly complicated thing is packing up and hitching the caravan to the car, as well as unhitching

the caravan and setting it up. I wont bore you with the details, but theres at least 20 or so dierent things

that need to be done and checked each time. Some are inconsequential, but some are really important

132
like making sure the chains are attached to the car, that the gas is o and that the brakes and indicators

are working.

So I decided to create an app where you could create pre-ight checklists. The checklists would contain

a bunch of items that you could check o as being done, and when you needed to restart the checklist for

the next time you could just hit a refresh button to reset everything. A repeatable todo list application in a

sense.

As I mentioned, this application covers similar concepts to what a traditional todo application would.

Specically though, the main concepts we will cover are:

Complex Lists

Data Models

Observables

Forms and User Input

Simple Navigation

Passing Data Between Pages

Creating, Reading, Updating and Deleting Data

Data Storage and Retrieval

Theming

Heres a quick run down on the exact features of the application:

The rst time the user uses the application an introduction tutorial will be shown

The user can create any number of checklists

The user can add any number of individual items to any checklist

An item in a checklist can be marked as complete or incomplete

The user can reset a checkist at any time

The user can edit or delete any checklist or items in a checklist

All data will be remembered upon returning to the application (including the completion state of

checklist items)

and heres a couple of screenshots to put everything in context.

133
134
135
Lesson Structure

1. Getting Ready

2. Basic Layout

3. Data Models and Observables

4. Creating Checklists and Checklist Items

5. Saving and Loading Data

6. Creating an Introduction Slider & Themeing

Ready?

Now that you know what youre in for, lets get to building it!

136
Lesson 2: Getting Ready

In this lesson we are going to prepare our application for the journey ahead. We are going to of course

generate the application, and we are also going to set up all of the components and Cordova plugins we

will need. At the end of this rst lesson we should have a nice skeleton application set up with everything

we need to start diving into coding.

A good rule of thumb before starting any new application is to make sure you have the latest version of

Ionic and Cordova, so if you havent done it recently then make sure to run:

npm install -g ionic@beta cordova

or

sudo npm install -g ionic@beta cordova

before you continue.

Generate a new application

We will be using the blank starter template for this application which, as the name implies, is basically an

empty Ionic project. It comes with one page built in called home which we will repurpose as our main

page that will hold our checklists in the next lesson.

> Run the following command to generate a new application

ionic start quicklists blank --v2

> Make the new project your current working directory by running the following command:

cd quicklists

Your project should now be generated - now you can open up the project folder in your favourite editor.

You can take a look at how your application looks by running the following command:

137
ionic serve

which for now should look something like this:

138
###Create the Required Components

This application will have a total of three page components. We will have our HomePage that will display

a list of all the checklists, an IntroPage that will display the introduction tutorial, and a ChecklistPage

which will display the individual items for a specic checklist. Weve already got the Home page, so lets

create the other two now.

> Run the following command to generate the Introduction page:

ionic g page Intro

> Run the following command to generate the Checklist detail page:

ionic g page Checklist

Also remember that any time we generate a new component, we need to import its .scss le into our

app.core.scss le. The Ionic CLI automatically generates pages for us, but it doesnt automatically import

the styles in app.core.scss.

> Modify app.core.scss to import the components styles:

@import "../pages/home/home";
@import "../pages/intro/intro";
@import "../pages/checklist/checklist";

Create the Required Services

We are going to be creating a couple of services in this application to help us out. We will be creating

a data model for our checklists which will allow us to more easily create and update them, and we will

also be creating a Data service to handle saving the checklist data into storage and retrieving them from

storage.

> Run the following command to generate a Data provider:

ionic g provider Data

139
> Run the following command to generate a Checklist data model provider:

ionic g provider ChecklistModel

Add Required Platforms

Before you can build for certain platforms, you need to add them to your project. This is something we

will be doing way later on in this course, but you may as well just set them up now.

> Run the following command to add the iOS platform to your application

ionic platform add ios

> Run the following command to add the Android platform to your application

ionic platform add android

Add Required Cordova Plugins

This application will use a few dierent Cordova plugins. Remember, Cordova plugins can only be used

when running on a real device. Ill explain each plugin as we run through the commands for adding them.

> Run the following command to add the SQLite plugin:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

This plugin gives you access to native storage with an SQLite database. We are adding it to this application

because the Ionic local storage service can make use of this plugin to provide more stable data storage.

> Run the following command to add the Status Bar plugin:

ionic plugin add cordova-plugin-statusbar

140
We will be adding this plugin to all projects to give us control over the status bar in our application (the bar

at the top of the devices screen that contains the time, battery information and so on).

> Run the following command to add the Splash Screen plugin:

ionic plugin add cordova-plugin-splashscreen

This plugin allows us to control the splash screen (the fullscreen graphic that briey displays when you

open an app)

> Run the following command to add the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

This plugin is required for all applications, and helps to dene what resources should be allowed to be

loaded in your application. Without it, resources you try to load will fail.

As well as adding the plugin, you also need to dene a Content Security Policy in your index.html

le. We will be adding a very permissive policy which will essentially allow us to load any resources.

Depending on your application, you may look into providing a more strict policy, but an open policy is

good for development.

> Modify your www/index.html le to include the following meta tag:

<meta http-equiv="Content-Security-Policy" content="font-src 'self' data:;


img-src * data:; default-src * 'unsafe-eval' 'unsafe-inline'">

> Run the following command to add the Crosswalk plugin:

ionic plugin add cordova-plugin-crosswalk-webview

This is another plugin that we will add to every application, but you may decide you want to leave it out.

By adding this plugin, when you build for Android Crosswalk will be used. Android has a lot of issues,

especially with older devices, because there is so many dierent software versions out there and dierent

versions have dierent browsers (remember, since we are building HTML5 applications it is actually a

browser engine powering and running our application). What Crosswalk does is bundle a modern browser

141
into your application so no matter what device you are running on your app will be powered by the same

browser, and the Crosswalk browser can improve performance a lot.

The only real downside to this is that it increases the size of your application by a signicant amount. In

general, I think its worth it and Id recommend you include it but you may leave it out if you like. For more

information take a look at the Crosswalk Project website: https://crosswalk-project.org/

Set up Images

When building this application we are going to be making use of a few images. Ive included these in your

download pack but you will need to set them up in the application you generate.

> Copy the images folder in the download pack for this application from www/images to your own

www folder

Summary

Thats it! Were all set up and ready to go, now we can start working on the interesting stu.

142
Lesson 3: Basic Layout

Were going to start things o pretty slow and easy in this lesson, and just focus on creating the basic

layout for the application. We will need to create templates for the Home page, which will display all of

the checklists that have been created, and the Checklist page, which will display all of the items for one

specic checklist. If youve been paying attention then youll know theres also one more page, but we will

be creating that later.

As Ive mentioned before, Ive tried to make this course as modular as possible so that you can build

the applications that interest you rst, and arent forced to follow a particular order. Since this is the rst

application though, and because it is the only application contained in the basic package Ill be paying

special attention to making sure all the little things are explained thoroughly.

The Home Page

Before we jump into the code, lets get a clear picture in our mind of what we are actually building. Heres

a screenshot of the completed home page:

143
Its got a bit of fancy styling which we will cover later, but essentially its a pretty simple list of items with a

button in the top right to add a new checklist. Its not entirely simple though, youll notice one of the list

items looks a little dierent and is displaying an Edit and Delete button. This is because we will be using

144
the sliding list component Ionic provides, which allows us to specify some content that will be displayed

when the user swipes the list item.

So lets get into building it. First were going to look at the entire template to see everything in context,

then were going to break it down into smaller chunks that we will discuss in detail.

> Modify home.html to reect the following

<ion-header>
<ion-navbar secondary>
<ion-title>
<img src = "images/logo.png" />
</ion-title>
<ion-buttons end>
<button (click)="addChecklist()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>
<ion-list no-lines>

<ion-item-sliding>

<button ion-item (click)="viewChecklist(checklist)">


<ion-item-content>
TITLE GOES HERE
<span>0 items</span>
</ion-item-content>
</button>

145
<ion-item-options>
<button light (click)="renameChecklist(checklist)"><ion-icon
name="clipboard"></ion-icon> Edit</button>
<button danger (click)="removeChecklist(checklist)"><ion-icon
name="trash"></ion-icon> Delete</button>
</ion-item-options>

</ion-item-sliding>

</ion-list>
</ion-content>

Lets start o by discussing the <ion-header> section:

<ion-header>
<ion-navbar secondary>
<ion-title>
<img src = "images/logo.png" />
</ion-title>
<ion-buttons end>
<button (click)="addChecklist()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

The <ion-navbar> allows us to add a header bar to the top of our application that can hold buttons,

titles and even integrates directly with Ionics navigation system to display a back button when necessary.

We add the secondary attribute to the navbar to style it with our secondary colour, which is dened in

app.variable.scss. Inside of this navbar we use <ion-title>, which is typically used to display a text

title for the current page, to display our logo. We also use <ion-buttons> to create a button in the

146
navbar. By specifying the end attribute, the buttons will be placed on the right side on iOS, but if we were

to specify the start attribute it would be displayed on the left instead. Remember that Ionic 2 has platform

continuity built in, so by default the buttons will be placed in the most appropriate place for the platform

they are running on.

Finally we have the button itself inside of <ion-buttons>. This button uses a circle icon and has a click

handler attached to it, which will call the addChecklist() function in our home.ts le (which we have

not created yet of course). Lets move onto the list section now:

<ion-content>
<ion-list no-lines>

<ion-item-sliding>

<button ion-item (click)="viewChecklist(checklist)">


<ion-item-content>
TITLE GOES HERE
<span>0 items</span>
</ion-item-content>
</button>

<ion-item-options>
<button light (click)="renameChecklist(checklist)"><ion-icon
name="clipboard"></ion-icon> Edit</button>
<button danger (click)="removeChecklist(checklist)"><ion-icon
name="trash"></ion-icon> Delete</button>
</ion-item-options>

</ion-item-sliding>

</ion-list>

147
</ion-content>

Before we get to the list, notice that everything is wrapped inside of <ion-content> - this is what holds

the main content for the page and in most cases everything except the navbar will be inside of here.

Much like a list is created with plain HTML, e.g:

<ul>
<li></li>
<li></li>
<li></li>
</ul>

A list in Ionic is created in basically the same way:

<ion-list>
<ion-item></ion-item>
<ion-item></ion-item>
<ion-item></ion-item>
</ion-list>

Of course, ours looks a little more complicated than that so lets talk through it. The rst thing out of the

ordinary we are doing is adding the no-lines attribute to the <ion-list>. Just like the secondary

attribute we added to navbar, this attribute also styles our list except that it will cause the items in the list

to not display with borders.

The next bit gets a little trickier, as it is where we set up our sliding item. An <ion-sliding-item>, op-

posed to a normal <ion-item>, has two sets of content - the item itself, and then the <ion-item-options>

which will be revealed when the user slides the item.

The rst block of code inside of <ion-sliding-item> is the normal <ion-item> denition, but instead

of using <ion-item> directly we are using <button ion-item> which is actually a button with the

styling of an item. Visually, these two methods are exactly the same, but on mobile everything that is not

148
a <button> or <a> element that has a click handler will have a slight tap delay. We dont want to have

this delay so we use the button instead.

The click handler we attach to the button calls a function viewChecklist but it also passes in a parameter

of checklist. We havent dened what this is yet, but eventually we will be creating a bunch of these

items from an array of data, and we will create a reference to each individual item that we can pass into

this function. So eventually, the checklist we are passing in here will be a reference to the specic item

that was clicked (we will discuss exactly how we do that later).

Finally, we have the second block of code, <ion-item-options>, which simply allows us to dene what

content we want to display when the user slides the item. In this case we are just adding Edit and Delete

buttons which will also pass in a reference to the checklist it was called on (again, we will have to create

this reference later, for now it will just cause issues).

Thats all there is to the home page, so lets move on to the checklist page now.

The Checklist Page

As we did before, lets rst take a look at what we are building before we jump in:

149
This screen looks very similar to the last one, and for the most part it is, but there are some dierences.

Obviously we have an extra button now, and a back button to return to the main page. The items in our

list also now have a checkbox next to them that will be used for marking an item as complete, and we still

have our sliding items set up.

Again, lets add the code for the template to the application and then talk through it:

> Modify checklist.html to reect the following

<ion-header>
<ion-navbar secondary>
<ion-title>

150
CHECKLIST TITLE
</ion-title>
<ion-buttons end>
<button (click)="uncheckItems()"><ion-icon
name="refresh-circle"></ion-icon></button>
<button (click)="addItem()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>

<ion-list no-lines>

<ion-item-sliding>

<ion-item>
<ion-label>ITEM TITLE</ion-label>
<ion-checkbox [checked]="item.checked" (click)="toggleItem(item)">
</ion-checkbox>
</ion-item>

<ion-item-options>
<button light (click)="renameItem(item)"><ion-icon
name="clipboard"></ion-icon> Edit</button>
<button danger (click)="removeItem(item)"><ion-icon
name="trash"></ion-icon> Delete</button>
</ion-item-options>
</ion-item-sliding>

151
</ion-list>

</ion-content>

You already know how the navbar works, this time weve just got an extra button inside of our

<ion-buttons> and it is still using the end attribute, so both items will be aligned to the right. We are
using the <ion-title> in a more traditional way this time to display the title of the checklist that is

currently being viewed (at least, we will be doing that soon). The buttons also both have click handlers to

dierent functions, but since we are in the checklist component now, these functions will be triggered in

the checklist.ts le (which we also have not created yet).

You also know how the sliding items work now, but this time we have an <ion-checkbox> as the main

item instead of the title of the checklist and when it is clicked it will trigger the toggleItem() function

which we will need to dene later. Notice that we are just using a plain <ion-item> now instead of

<button ion-item>, this is because were attaching the click handler directly to the checkbox rather
than having it on the entire item.

Theres also a bit of new syntax here, lets take a closer look:

[checked]="item.checked"

When something is surrounded by [square brackets] it means we will be modifying a property on

that element, and we will be setting it to the expression contained in the quotes, not a string. So in this

case it would set the checked property to the value of item.checked. Right now we havent created a

reference to item so it wont work anyway, but later it will. The important thing to remember here is that the

square brackets will evaluate whatever is inside the quotation marks. Lets imagine you have the following

dened in the class for your component:

this.myName = "Josh"

If I were to use the following code:

<something myName="myName">

152
the myName attribute would be set to literally myName, but if I were to use this code instead:

<something [myName]="myName">

the myName property would be set to Josh, because myName would get evaluated rst in this instance.

Moving on, theres nothing else surprising in the rest of the template - we simply add the Edit and Delete

buttons to our sliding list like we did on the last page.

If you run ionic serve now, you should see something like this:

153
I think we can both agree thats pretty ugly, but the structure is there. Please keep in mind that we havent

styled the logo properly yet, so depending on what size device you are looking at it with it may not t as

nicely as the image above does.

154
In the following lessons we will be getting the list to pull in real data and styling it so that it looks a lot

better. The template syntax in Ionic 2 looks a little confusing at rst, but once you get your head around it

its quite nice to use.

155
Lesson 4: Data Models and Observables

In this lesson were going to design a data model for the checklists that we will use in the application,

which will also incorporate Observables. A data model is not something that is specic to Ionic 2, a model

in programming is a generic concept. Depending on the context, the exact denition of a model may vary,

but in general a model is used to store or represent data.

In the context of Ionic 2 & Angular 2, if we wanted to keep a reference to some data we might do something

like this:

this.myDataArray = ['1', '2', '3'];

However, if we were to create a model it might look something like this:

this.myDataArray = [
new MyDataModel('1'),
new MyDataModel('2'),
new MyDataModel('3')
];

So instead of storing plain data, we are creating an object that holds that data instead. At rst it might be

hard to see why we would want to do this, for simple data like the example above it just looks a lot more

complicated, but it does provide a lot of benets. The main benet for us in this application will be that it:

Allows us to clearly dene the structure of our data

Allows us to create helper functions on the data model to manipulate our data

Allows us to reuse the data model in multiple places, simplifying our code

Hopefully this lesson will show you how useful creating a data model can be, but let me preface this by

saying this isnt something that is absolutely required. You can quite easily just dene some data directly

in your class if you like.

Were also going to be creating and making use of our own Observable in this data model, but lets cross

that bridge when we get there.

156
Creating a Data Model

Usually if we wanted to create a data model we would create a class that denes it (its basically just a

normal object), along with its helper functions, like this:

class PersonModel {

constructor(name, age){
this.name = name;
this.age = age;
}

increaseAge(){
this.age++;
}

changeName(name){
this.name = name;
}

Then we could create any number of instances (objects) from it like this:

let person1 = new PersonModel('Jason', 43);


let person2 = new PersonModel('Louise', 22);

and we can call the helper functions on any individual instance (object) like this:

person1.increaseAge();

The idea in Ionic 2 is pretty much exactly the same, except to do it in the Ionic 2 / Angular 2 way we create

an Injectable (which we discussed in the basics section). Remember that an Injectable is used to create

157
services that can be injected into any of our other components, so if we want to use the data model we

create we can just inject it anywhere that we want to use it.

Lets take a look at what the data model will actually look like, and then walk through the code.

> Modify checklist-model.ts to reect the following:

export class ChecklistModel {

checklist: any;
checklistObserver: any;

constructor(public title: string, public items: any[]){

this.items = items;

addItem(item): void {

this.items.push({
title: item,
checked: false
});

removeItem(item): void {

let index = this.items.indexOf(item);

if(index > -1){

158
this.items.splice(index, 1);
}

renameItem(item, title): void {

let index = this.items.indexOf(item);

if(index > -1){


this.items[index].title = title;
}

setTitle(title): void {
this.title = title;
}

toggleItem(item): void {
item.checked = !item.checked;
}

What were trying to do with this data model is essentially create a blueprint for what an individual checklist

is. A checklist has a title and it can have any number of items associated with it that need to be completed.

So we set up member variables to hold these values: a simple string for the title, and an array for the items.

Notice that we allow the title and the items to be passed in through the constructor. A title must be supplied

to create a new checklist, but providing an array of items is optional. If we want to immediately add items

159
to a checklist we can supply an items array when we instantiate it, otherwise it will just be initialised with

an empty array.

We include a bunch of helper functions which are all pretty straight forward, they allow us to either change

the title of the checklist, or modify any of the checklists items (by changing their name, removing an item,

adding a new item to the checklist, or toggling the completion state of an item).

Also notice that we have added : void after each of the functions. Just like we can declare that a variable

has a certain type by doing something like this:

checklist: any;

we can also declare what type of data a function returns. In this case, no data is being returned so we

use void. If one of these functions were to return a string, then we would instead use : string on the

function.

With all of that set up, we can easily create a new checklist in any component where we have imported the

Checklist Model (which we will be doing in the next lesson) by using the following code:

let newChecklist = new ChecklistModel('My Checklist', []);

or

let newChecklist = new ChecklistModel('My Checklist', myItemsArray);

Were going to get a little bit fancier now and incorporate an Observable into our data model so that we

can tell when any checklist has been modied (which will allow us to trigger a save to memory later).

Adding an Observable

Youve had a little bit of exposure to Observables already in the basics section of this course - to refresh

your memory we can use the Observable the Http service returns like this:

this.http.get('https://www.reddit.com/r/gifs/new/.json?limit=10').map(res
=> res.json()).subscribe(data => {

160
console.log(data);
});

We call the get method, and then subscribe to the Observable it returns. Remember that an Observ-

able, unlike a Promise, is a stream of data and can emit multiple values over time, rather than just once. This

concept isnt really demonstrated when using the Http service, since in most cases we are just retrieving

the data once. The Observable is also already created for us in the case of Http.

We are about to create our very own Observable from scratch in our data model, which will allow other

parts of our application to listen for when changes occur to our checklist (because we will emit some data

every time a change occurs). When implementing this Observable you will see how to create an observable

from scratch, and youll also see how an Observer can emit more than one value over time.

Before we get to implementing it, lets talk about Observables in a little more detail, in the context of what

were actually trying to do here. In the subscribe method in the code above we are only handling one

response:

this.http.get(url).subscribe(data => {
console.log(data);
});

which is actually the onNext response from the Observable. Observers also provide two other responses,

onError and onCompleted, and we could handle all three of those if we wanted to:

this.http.get(url).subscribe(

(data) => {
console.log(data);
},

(err) => {
console.log(err);
},

161
() => {
console.log("completed");
}
);

In the code above the rst event handler handles the onNext response, which basically means when we

detect the next bit of data emitted from the stream, do this. The second handler handles the onError

response, which as you might have guessed will be triggered when an error occurs. The nal handler

handles the onCompleted event, which will trigger once the Observable has returned all of its data.

The most useful handler here is onNext and if we create our own observable, we can trigger that onNext

response as many times as we need by calling the next method on the Observable, and providing it some

data.

Now that we have the theory out of the way, lets look at how to implement the observable.

> Modify checklist-model.ts to reect the following:

import {Observable} from 'rxjs/Observable';

export class ChecklistModel {

checklist: any;
checklistObserver: any;

constructor(public title: string, public items: any[]){

this.items = items;

this.checklist = Observable.create(observer => {


this.checklistObserver = observer;

162
});

addItem(item): void {

this.items.push({
title: item,
checked: false
});

this.checklistObserver.next(true);

removeItem(item): void {

let index = this.items.indexOf(item);

if(index > -1){


this.items.splice(index, 1);
}

this.checklistObserver.next(true);

renameItem(item, title): void {

let index = this.items.indexOf(item);

163
if(index > -1){
this.items[index].title = title;
}

this.checklistObserver.next(true);

setTitle(title): void {
this.title = title;
this.checklistObserver.next(true);
}

toggleItem(item): void {
item.checked = !item.checked;
this.checklistObserver.next(true);
}

The rst thing to notice here is that we are now importing Observable from the RxJS library. Then in our

constructor, we set up the Observable:

this.checklist = Observable.create(observer => {


this.checklistObserver = observer;
});

Our this.checklist member variable in the code above is now our very own observable. Since it is

an observable, we can subscribe to it, and since it is part of our data model, we can subscribe to it on any

checklist we have created in our application. For example:

164
let newChecklist = new ChecklistModel('My Checklist', []);

newChecklist.checklist.subscribe(data => {
console.log(data);
});

Of course, we arent doing anything with the Observable yet so its never going to trigger that onNext

response. This is why we have added the following bits of code to each of our helper functions:

this.checklistObserver.next(true);

So whenever we use one of our helper functions to change the title, or add a new item, or anything else, it

will notify anything that is subscribed to its Observable. All we want to know is that a change has occurred

so we are just passing back a boolean (true or false), but we could also easily pass back some data if we

wanted.

The result of this is that now we can observe any checklists we create for changes that occur. Later on

we will make use of this by listening for these changes and then triggering a save.

Summary

In this lesson weve gone a little bit beyond the beginner level and created a pretty robust data model. As

Ive mentioned, this certainly has its benets but dont feel too intimidated if you had trouble following

along with this lesson - as a beginner you can mostly get away with just dening data directly on the class

and not worrying about data models and observables.

I particularly dont want to freak you out too much with the Observabes - they are confusing (until you

get your head around them) and outside of subscribing to responses from the Http service, you really

dont have to use them in most simple applications. But once you do understand them, you can do some

powerful stu with them.

Although this lesson was a little more advanced, its a great way to demonstrate how you might make

165
use of Observables in your project, and if youve kept up through this lesson then hopefully the next ones

should be a breeze!

166
Lesson 5: Creating Checklists and Checklist Items

Weve done a lot of setting up and structuring so far, but in this lesson well be getting to the bones of what

were building. Well be adding ways to create new checklists, viewing those checklists and adding items

to them (as well as modifying any items or the checklist itself). Its going to be a big one so strap in and

get some coee ready if you are so inclined.

Checklists

The rst thing we are going to do is add everything we need for creating and viewing checklists. This will

mean adding to our class denition, as well as modifying the template we created before to actually display

the checklist data.

Lets start o by setting up our class denition.

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {NavController, AlertController} from 'ionic-angular';
import {ChecklistPage} from '../checklist/checklist';
import {ChecklistModel} from
'../../providers/checklist-model/checklist-model';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

checklists: ChecklistModel[] = [];

167
constructor(public nav: NavController, public dataService: Data, public
alertCtrl: AlertController) {

addChecklist(): void {

renameChecklist(checklist): void {

viewChecklist(checklist): void {

removeChecklist(checklist): void{

save(): void{

Were importing a few things from the Ionic library here. NavController you should already be pretty familiar

with, but you might not know what AlertController is. AlertController allows us to present various alerts to

the user, including a basic prompt, prompts with input, conrmation prompts and more. We will be using

168
these as a method to add new checklists.

Were also importing our ChecklistPage which we will nish implementing later, but most importantly we

are importing the Checklist Model we created in the last lesson. As well as importing the Checklist Model,

we also need to declare it as a provider inside of the @Page decorator.

Finally, we also import the Data provider we generated earlier, but we wont be implementing its functionality

until later.

By adding the public keyword in the constructor we are simply setting up a reference to the Nav-

Controller and Data provider that we can use throughout the class later by referencing this.nav and

this.dataService. Basically its shorthand for this:

constructor(nav: NavController, dataService: Data){


this.nav = nav;
this.dataService = dataService;
}

which you may have seen used before.

Weve also declared a checklists array at the top of the class which will make it accessible throughout

the class by referencing this.checklists. The rest of the class is various functions which we will step

through creating one by one now.

addChecklist

This function will be responsible for allowing the user to create a new checklist. It will launch a prompt,

and use the data that is entered to create a new checklist (making use of the data model we created).

> Modify the addChecklist function to reect the following:

addChecklist(): void {
let prompt = this.alertCtrl.create({
title: 'New Checklist',

169
message: 'Enter the name of your new checklist below:',
inputs: [
{
name: 'name'
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: data => {
let newChecklist = new ChecklistModel(data.name, []);
this.checklists.push(newChecklist);

newChecklist.checklist.subscribe(update => {
this.save();
});

this.save();
}
}
]
});

prompt.present();
}

We are presenting a prompt to the user that will contain a single name input eld, and two buttons Cancel

170
and Save. The cancel button does nothing except dismiss the prompt, but we add a handler to the save

button that will pass in the data that was entered into the input eld.

Inside of this handler we rst generate a new checklist by passing the entered name into a new instance of

our checklist model, and then we push that object into our this.checklists array. Then we subscribe

to the observable we added to the data model in the last lesson to listen for whenever the checklist is

modied in anyway, and when it is we trigger the save function. Notice that we have two calls to save here,

one that is triggered by the observable and one that res straight away (since we have just added a new

checklist).

Finally, we trigger the prompt by calling its present method.

If you take a look at your template le again, youll remember that weve already added a call to this function

for when the add button is clicked:

<button (click)="addChecklist()"><ion-icon
name="add-circle"></ion-icon></button>

renameChecklist

Next were going to dene the renameChecklist function which, obviously, will allow us to rename a

checklist.

> Modify the renameChecklist function to reect the following:

renameChecklist(checklist): void {

let prompt = this.alertCtrl.create({


title: 'Rename Checklist',
message: 'Enter the new name of this checklist below:',
inputs: [
{
name: 'name'

171
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: data => {

let index = this.checklists.indexOf(checklist);

if(index > -1){


this.checklists[index].setTitle(data.name);
this.save();
}

}
}
]
});

prompt.present();

The rst thing that you may notice is that it looks very similar to our addChecklist function, and that is

because it is. We use the same prompt with the same inputs and buttons, we just have a slightly dierent

handler.

Notice that we are passing in a parameter to this function, which will be a reference to the checklist that

172
we want to rename. We will be updating the template shortly to pass in this reference, but for now just

pretend that we have it.

We use this reference to the checklist to nd it in our this.checklists array and then set it to the new

title that was entered, then we trigger a save.

Again, if you recall from before, we have already set up a click handler that will call this function in the

template:

<button light (click)="renameChecklist(checklist)"><ion-icon


name="clipboard"></ion-icon></button>

removeChecklist

Next up we are going to add the ability to delete a checklist.

> Modify the removeChecklist function to reect the following:

removeChecklist(checklist): void{

let index = this.checklists.indexOf(checklist);

if(index > -1){


this.checklists.splice(index, 1);
this.save();
}

This function is quite a lot simpler because it doesnt require any user input, we just need to get rid of the

checklist. Just as we did before, we are passing in a reference to the checklist and then nding the checklist

in our this.checklists array. We then simply remove it from the array using the splice method and

trigger a save.

173
Heres the code from the template that triggers this function:

<button danger (click)="removeChecklist(checklist)"><ion-icon


name="trash"></ion-icon> Delete</button>

viewChecklist

We can create and modify our checklists now, but we also need to be able to see the details of specic

checklists, and to add individual items to checklists. To do this, were going to use our NavController to

push a new page and pass in a reference to the checklist that was clicked.

> Modify the viewChecklist function to reect the following:

viewChecklist(checklist): void {
this.nav.push(ChecklistPage, {
checklist: checklist
});
}

We pass in the ChecklistPage we imported before (which we are yet to nish) to the push method, as well

as the data we want to send along to the new page, which is a reference to the checklist the user is trying

to view. We will be able to use NavParams in the class for our checklist page to grab this data later.

save

This one function is going to be the odd one out for now as we arent actually going to implement it. Theres

quite a lot that needs to go into it so well be covering saving and loading data in its own lesson later.

To bring everything together, we need to nish o the template for the home page. We can do all of this

stu with our data now, but we cant even see the results.

> Modify home.html to reect the following:

174
<ion-header>
<ion-navbar secondary>
<ion-title>
<img src = "images/logo.png" />
</ion-title>
<ion-buttons end>
<button (click)="addChecklist()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>
<ion-list no-lines>

<ion-item-sliding *ngFor="let checklist of checklists">

<button ion-item (click)="viewChecklist(checklist)">


<ion-item-content>
{{checklist.title}}
<span>{{checklist.items.length}} items</span>
</ion-item-content>
</button>

<ion-item-options>
<button light (click)="renameChecklist(checklist)"><ion-icon
name="clipboard"></ion-icon></button>
<button danger (click)="removeChecklist(checklist)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>

175
</ion-item-sliding>

</ion-list>
</ion-content>

Were doing a few interesting things here now, most notably we have added an ngFor loop:

<ion-item-sliding *ngFor="let checklist of checklists">

What this will do is loop over every entry we have in our this.checklists array and create a sliding

item for it in the list. Remember, the * syntax used in front of the ngFor here is a shortcut for creating

embedded templates in Angular 2, so what we are doing essentially is creating a template that is stamped

out for as many times as we have items in our array. Each time this template is stamped it will contain

the information for the specic item it was stamped out for, so anywhere inside of the ngFor loop we can

grab the data of the specic checklist that is being rendered using:

{{checklist.title}}

Notice that we also have a let infront of checklist in the ngFor loop. In Angular 2 using let like

this allows us to create a local variable, and this is what then allows us to pass a reference to the specic

checklist into all of the functions we just created. To make the concept more clear, if we were to use the

following code instead:

<ion-item-sliding *ngFor="let check of checklists">

Then we would render the data like this:

{{check.title}}

and pass the reference into our functions like this:

removeChecklist(check)

176
That just about nishes up our home page, and you should now be able to add, edit and delete checklists,

as well as launch the details page for a specic checklist (which wont contain anything just yet, and wont

really work).

If you re up ionic serve now you should see something like this:

177
If you try to run this code youre going to get complaints about a missing provider, so you should

also modify app.ts to reect the following:

178
import {Component} from "@angular/core";
import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {

rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data]);

It is also important to note that you must remove this line from checklist.html:

<ion-checkbox [checked]="item.checked"
(click)="toggleItem(item)"></ion-checkbox>

if you want to create an item and then go to its detail page. In a later lesson we will only include this if

there is item data available, but right now we arent doing that so an error will be caused since it is trying

to access data that does not exist.

We will get to all of this later anyway, so you only need to do these extra steps if you want to have a play

179
around with the app right now.

Checklist Items

Now that we can trigger the checklist detail page, wed best get some content in there and add a way

for people to create and modify individual checklist items. In this section we will be implementing our

Checklist Page, which is launched by the home page and is supplied some data regarding which checklist

is being viewed.

Lets start by setting up the class denition again.

> Modify checklist.ts to reect the following:

import {Component} from '@angular/core';


import {NavController, NavParams, AlertController} from 'ionic-angular';

@Component({
templateUrl: 'build/pages/checklist/checklist.html'
})
export class ChecklistPage {

checklist: any;

constructor(public nav: NavController, public navParams: NavParams, public


alertCtrl: AlertController){
this.checklist = this.navParams.get('checklist');
}

addItem(): void {

180
toggleItem(item): void {

removeItem(item): void {

renameItem(item): void {

uncheckItems(): void {

}
}

Theres nothing much going on here that you are not already familiar with, the only thing out of the ordinary

is the use of NavParams. When we pass in data to another page, we can grab it by injecting NavParams

and using the get method. In this instance we are just passing in the checklist data that we want to view,

but you can also pass in multiple values if you like.

Just as we did before, we are going to go through implementing these functions one by one now. A lot of

these will be quite similar to what we just did for the home page.

addItem

> Modify the addItem function to reect the following:

addItem(): void {

181
let prompt = this.alertCtrl.create({
title: 'Add Item',
message: 'Enter the name of the task for this checklist below:',
inputs: [
{
name: 'name'
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: data => {
this.checklist.addItem(data.name);
}
}
]
});

prompt.present();

This should all look very familiar, but notice the dierence in the handler. Since we created an addItem

help function on our data model, all we have to do is call that and pass in the name of the item we want to

create (I told you the data model would come in handy!).

182
renameItem

> Modify the renameItem function to reect the following:

renameItem(item): void {

let prompt = this.alertCtrl.create({


title: 'Rename Item',
message: 'Enter the new name of the task for this checklist below:',
inputs: [
{
name: 'name'
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: data => {
this.checklist.renameItem(item, data.name);
}
}
]
});

prompt.present();

183
Once again, almost the exact same idea except we are calling the renameItem helper function on our

data model in the handler, and we are also passing through a reference to the specic item that we are

renaming.

removeItem

> Modify the removeItem function to reect the following:

removeItem(item): void {
this.checklist.removeItem(item);
}

This one is even simpler, we simply call the removeItem helper function on the data model and pass it a

reference to the item we want to delete.

toggleItem

> Modify the toggleItem function to reect the following:

toggleItem(item): void {
this.checklist.toggleItem(item);
}

This function is used to toggle the checkmarks on an individual item on and o, and once more we simply

pass through a reference to the item we want to toggle to the data model.

uncheckItems

> Modify the uncheckItems function to reect the following:

uncheckItems(): void {
this.checklist.items.forEach((item) => {

184
if(item.checked){
this.checklist.toggleItem(item);
}
});
}

This function is tied to the reset button we added to our template, and will loop through every item we have

in the checklist and call the toggleItem function in the data model if the current item is checked. This

allows the user to uncheck all items at once.

Now all we have left to do is update the template for our checklist page. Weve already set up most of the

structure for this template, but just like with the home page we will need to add a little bit more to handle

displaying data.

> Modify checklist.html to reect the following

<ion-header>
<ion-navbar secondary>
<ion-title>
{{checklist.title}}
</ion-title>
<ion-buttons end>
<button (click)="uncheckItems()"><ion-icon
name="refresh-circle"></ion-icon></button>
<button (click)="addItem()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>

185
<ion-list no-lines>

<ion-item-sliding *ngFor="let item of checklist.items"


class="home-sliding-item">

<ion-item>
<ion-label>{{item.title}}</ion-label>
<ion-checkbox [checked]="item.checked" (click)="toggleItem(item)"
class="checklist-item">
</ion-checkbox>
</ion-item>

<ion-item-options>
<button light (click)="renameItem(item)"><ion-icon
name="clipboard"></ion-icon></button>
<button danger (click)="removeItem(item)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

This should all look pretty similar to the home page template, but there are a few dierences. We are using

the checklist data from our class to display the title of the current checklist in the navbar. We are again

looping through data using ngFor but this time we are only looping through the items, which are a child

of the checklist. Also notice that as well as rendering data using double braces like this:

{{item.title}}

186
we can also set properties on elements using the square brackets like this:

[checked]="item.checked"

this will set checked to be the value of whatever item.checked evaluates to.

Summary

You should now be able to perform just about every function of the application, which includes creating

checklists, modifying them, viewing individual checklists, and adding items to individual checklists.

Try running the application in your browser and adding your own checklists and checklist items.

In the next lesson well work on saving data and then well look into prettying up the application (function

before form right?).

187
Lesson 6: Saving and Loading Data

You know what would be really annoying? If you create an entire checklist full of items for some task you

need to complete, come back to use it later and its just gone. Well thats exactly how the application

works right now, so we are going to need to add a way to save any data the user adds to the application

for use later.

Weve already set up a lot of the structure for this, were subscribing to our Observables and calling the

save function every time some data changes, we just need to implement that function now.

> Modify the save function in home.ts to reect the following:

save(): void {
this.dataService.save(this.checklists);
}

If you recall, earlier we already generated and imported a data service, so all we need to change here is to

add a call to it and pass in the current checklists data. Of course, we havent actually implemented that

data service yet so it wont do anything with the data, but we will get to that shortly.

One more thing we need to do before we can successfully use this data service is to declare it in the

providers array. You may remember that we also declared our Checklist Model in the providers array for

the home page, and we could do the same for the data service, but instead we are going to add it to the

providers array in app.ts.

> Import the Data provider in app.ts and modify the decorator to reect the following:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';

@Component({

188
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {

rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data]);

In this particular application it doesnt make a dierence if we add the provider to app.ts or home.ts

because the data service is only ever called from home.ts. But in some applications it would be likely that

the data service would be called from multiple dierent components, so by adding it to app.ts you dont

have to worry about declaring it in every component you are using it in. Although you dont need to declare

it as a provider in every class you want to use it in by doing it this way, you do still need to import it at the

top of each class you want to use it in.

Its important to keep in mind that if you add the provider declaration to the app.ts le, then it will create one

app wide instance of that provider. If you add the provider declaration to multiple dierent components,

then each of those components will get their own instance of the provider (so if you were looking to share

values across the application, you would want to declare the provider in app.ts.

Now all we have to do is make the data service do something with the data we are sending it.

189
Saving Data

Were going to add the code for data.ts now so that it will save into storage any data that it is passed. The

code for this service is actually surprisingly simple, so lets take a look at it rst and then talk through it.

> Modify data.ts to reect the following:

import {Storage, SqlStorage} from 'ionic-angular';


import {Injectable} from '@angular/core';

@Injectable()
export class Data {

storage: Storage;

constructor(){
this.storage = new Storage(SqlStorage, {name:'checklist'});
}

getData(): Promise<any> {
return this.storage.get('checklists');
}

save(data): void {

let saveData = [];

//Remove observables
data.forEach((checklist) => {
saveData.push({
title: checklist.title,

190
items: checklist.items
});
});

let newData = JSON.stringify(saveData);


this.storage.set('checklists', newData);
}
}

Just like our Checklist Model, this is also an Injectable. Its really not any dierent to the model, its functions

just do dierent things - an Injectable / Provider / Service is just a way to create a generic thing that does

stu.

Theres a couple new imports here that we havent seen before:

import {Storage, SqlStorage} from 'ionic-angular';


import {Injectable} from '@angular/core';

Storage is Ionics generic storage service, and SqlStorage is used to dene the type of storage we want

to use. In the case of SqlStorage that means we will be storing data using a native SQLite database

(remember how we added the SQLite plugin before?). The SQLite database will only be available when

running natively on a device, so SqlStorage also uses local browser storage store data if the SQLite

database is not available.

Alternatively, you could just use the LocalStorage service instead of **SqlStorage* which just stores data

in the browsers local storage, rather than hooking into the SQLite plugin. In general this is a bad idea (but

were going to use it a little later anyway just to prove a point), because the browser based local storage

is not completely reliable and can potentially be wiped by the operating system. Having your data wiped

randomly is obviously not ideal, so you should use SqlStorage whenever possible, which does not have

this issue (it also allows you to store more data).

Lets take a look at what were doing in our constructor:

191
constructor(){
this.storage = new Storage(SqlStorage, {name:'checklist'});
}

We create a reference to our database by instantiating a new instance of Storage. We provide the storage

type we want to use, and provide the name of our database. If the database does not already exist, it will

be created.

Now lets take a look at the getData function:

getData(): Promise<any> {
return this.storage.get('checklists');
}

This function will allow us to retrieve the latest data that has been stored, and it will return it in the form of a

Promise. We are setting the return type for this function as a Promise that returns <any> type, this is one

of the more complicated types. Remember, adding types like this is not required so if you are confused by

this and would prefer to leave it out you could just do this instead:

getData(){
return this.storage.get('checklists');
}

Notice that we are not setting up the handler for when the promise nishes here, instead we just return the

result of the get method (which will be a promise which resolves with the data that is currently in storage).

Remember that this operation is not instant, and this allows us to set up the handler from wherever in the

code this method is being called, which makes more sense for the ow of the application (hopefully this

will be made more clear shortly).

Then we have our saveData function, which handles actually saving the data into storage:

save(data): void {

let saveData = [];

192
//Remove observables
data.forEach((checklist) => {
saveData.push({
title: checklist.title,
items: checklist.items
});
});

let newData = JSON.stringify(saveData);


this.storage.set('checklists', newData);
}

As I mentioned, we are storing the data as a single JSON encoded string, so we call the JSON.stringify

function and then store the data using the set method on our storage object. Before we do that though,

we remove the observable stu from the data by only pushing the title and the items since it doesnt

play nice with JSON (it causes a circular object error), and well just be recreating them later anyway.

Thats all there is to saving the data, which isnt really all that complex. Now we just need to handle loading

that data back into the application.

Loading Data

We are going to want to load the checklists data from storage whenever the user opens the application,

so a good place to do this is the constructor on our home page. Weve already imported and set up a

reference to the data service in this page, so all we need to do is make use of it.

> Modify the constructor in home.ts to reect the following:

constructor(public nav: NavController, public dataService: Data, public


alertCtrl: AlertController) {

193
this.dataService.getData().then((checklists) => {

let savedChecklists: any = false;

if(typeof(checklists) != "undefined"){
savedChecklists = JSON.parse(checklists);
}

if(savedChecklists){

savedChecklists.forEach((savedChecklist) => {

let loadChecklist = new ChecklistModel(savedChecklist.title,


savedChecklist.items);
this.checklists.push(loadChecklist);

loadChecklist.checklist.subscribe(update => {
this.save();
});

});

});

Were making a call to the getData function that we just dened in our data service. As I mentioned, the

194
getData function returns a promise rather than the data directly, which allows us to handle the response

here once it has nished loading. If the getData function just returned the data directly, rather than a

promise, then the data would likely not have even been returned yet when we try to access it.

So we wait for the data to be retrieved, and then pass the checklists data into our handler. First we decode

the JSON string into an array that we can work with, and then we loop through every item in the array

and create a new Checklist Model based on its data. The reason we loop through the data and create

new models rather than just settings this.checklists to be savedChecklists directly is because

by converting the checklists into a JSON string when we store it we lose the ability to use the helper

functions we dened on the model. So we just use the title and items data to recreate new objects for all

the checklists.

Finally, we set up the listener for the Observable again so that the save function will be triggered whenever

the data changes.

Summary

Thats all there is to it, the data will now be saved to the SQLite database whenever changes are made,

and when the application is reopened all of the data will be loaded back in. Try adding some checklists or

modifying the state of your checklists and reloading the application to see if the changes stick around.

195
Lesson 7: Creating an Introduction Slider & Theming

In the last lesson for the Quick Lists application we are going to be adding a few nal touches to improve

the user experience. We will add a slideshow tutorial to show the user how to use the app (which will only

display on their rst time using the app) and we will also add some styles to make the application look a

bit prettier.

Lets start o with the slider.

Slider Component

Its pretty common for mobile applications to display some instructions to the user through the use of a

sliding card style tutorial. Ionic has a slide component built-in so we are going to make use of that, and

to make sure the user doesnt have to go through the tutorial every time we will be keeping track of if they

have already seen it or not.

The slider itself is going to be pretty simple, it will allow us to display a series of images and on the last

slide there will be a button to start using the application.

First, were going to build the slider component and then we are going to look at how to integrate it into

our application. Well start by creating the template.

> Modify intro.html to reect the following

<ion-content>

<ion-slides [options]="slideOptions">

<ion-slide>
<img src="images/slide1.png" />
</ion-slide>

196
<ion-slide>
<img src="images/slide2.png" />
</ion-slide>

<ion-slide>
<img src="images/slide3.png" />
</ion-slide>

<ion-slide>
<ion-row>
<ion-col>
<button light (click)="goToHome()" style="margin-top:20px;">Start
Using Quicklists</button>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<img src="images/slide4.png" />
</ion-col>
</ion-row>
</ion-slide>

</ion-slides>

</ion-content>

The rst thing you might notice about this is that it doesnt contain a navbar, only the content area. Its

not necessary to include the navigation bar on every page, and we do not want to display it here. The rest

of the code is pretty simple, we use <ion-slides> with a options property so that we can congure it

with some options in our class denition in a moment, and we use <ion-slide> to dene each one of

197
our slides.

So in the code above, the user will rst see a slide containing the slide1 image, then when they swipe they

will see slide2 and so on until they reach the last slide which contains a button to go to the home page.

The last slide is a little more complicated because we are making use of <ion-row> and <ion-col>

so that we can position the button where we want it. These two directives make up Ionic 2s grid system,

where rows get placed underneath one another, and cols within those rows appear side by side. This

diagram should help illustrate that:

This is a very simple example because we just want the button to appear above the image, but you can

create quite complex layouts by supplying a width to the cols like this:

<ion-row>
<ion-col width-10></ion-col>
<ion-col width-50></ion-col>

198
</ion-row>

and you can include as much nesting as you like. The result of our grid layout will look like this:

199
Now we need to dene the class for our intro component.

> Modify intro.ts to reect the following

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {HomePage} from '../home/home';

@Component({
templateUrl: 'build/pages/intro/intro.html'
})
export class IntroPage {

slideOptions: any;

constructor(public nav: NavController){


this.slideOptions = {
pager: true
};
}

goToHome(): void {
this.nav.setRoot(HomePage);
}
}

Isnt this just the simplest class youve seen so far? All it does is import the home page, set the pager

option for our slider, and change the root page to it using the NavController when the goToHome function

is called. This allows us to tap the button on the last slide to go to our main home page view, but we will

have a bit of a problem. Every time the user opens the application they are going to have to go through

this tutorial to get to the main app. To solve this, we are going to make one more change to our home.ts

200
le.

> Modify home.ts to reect the following imports:

import {Component} from '@angular/core';


import {NavController, AlertController, Storage, LocalStorage} from
'ionic-angular';
import {ChecklistPage} from '../checklist/checklist';
import {ChecklistModel} from
'../../providers/checklist-model/checklist-model';
import {Data} from '../../providers/data/data';
import {IntroPage} from '../intro/intro';

> Modify the constructor in home.ts to reect the following:

checklists: ChecklistModel[] = [];


local: Storage;

constructor(public nav: NavController, public dataService: Data, public


alertCtrl: AlertController) {

this.local = new Storage(LocalStorage);

this.local.get('introShown').then((result) => {
if(!result){
this.local.set('introShown', true);
this.nav.setRoot(IntroPage);
}
});

this.dataService.getData().then((checklists) => {

201
let savedChecklists: any = false;

if(typeof(checklists) != "undefined"){
savedChecklists = JSON.parse(checklists);
}

if(savedChecklists){

savedChecklists.forEach((savedChecklist) => {

let loadChecklist = new ChecklistModel(savedChecklist.title,


savedChecklist.items);
this.checklists.push(loadChecklist);

loadChecklist.checklist.subscribe(update => {
this.save();
});

});

});

Were importing the Storage service again now, but this time we are also importing the LocalStorage service

instead of SqlStorage. Were going to use this to store a ag that will tell us whether or not the tutorial

has already been viewed. I could just as easily also use SqlStorage for this, but I wanted to show you that

this is an acceptable use case of where you could use local storage. As I mentioned before, local storage

202
isnt stable and could be wiped so usually its not suitable for storing data, but if the worst were to happen

and the data was wiped in this case it wouldnt really matter - the user would just have to go through the

tutorial again.

So we set up our new local storage object and we check for the existence of an introShown ag. If it

does not exist then we switch to our intro tutorial page and then set that ag to be true so it doesnt show

next time.

Theming

As far as functionality in the application goes, were 100% done. Now were just going to add a bit of

styling to the application to make it look quite a bit better than it currently does.

If you remember from the basics section, theres quite a few dierent ways we can add styles to the

application. We will be basically using all of these methods. We will be adding specic styles to each of

our components, we will be adding some generic styles in our core le, and we will be overriding some

SASS variables in the variables le. If you skipped over that part or are not entirely sure what Im talking

about here, Id recommend going back and reading about theming in the basics sections.

Since we just nished working on our intro component, lets add the styles for that rst:

> Modify intro.scss to reect the following:

ion-slide {
background-color: #32db64;
}

ion-slide img {
height: 85vh !important;
width: auto !important;
}

203
These styles will make the background colour of the slides green, and also set the images inside of the

slides to take up 85% of the available viewport height.

The rest of our components are going to require us adding some classes into our template that we can

hook into, so from now on Ill post both the nalised template code, as well as the styles to go along with

it.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar secondary>
<ion-title>
<img src = "images/logo.png" class="logo" />
</ion-title>
<ion-buttons end>
<button (click)="addChecklist()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>
<ion-list no-lines>

<ion-item-sliding *ngFor="let checklist of checklists"


class="home-sliding-item">

<button ion-item (click)="viewChecklist(checklist)" class="home-item">


<ion-item-content>
{{checklist.title}}
<span class="secondary-detail">{{checklist.items.length}}
items</span>

204
</ion-item-content>
</button>

<ion-item-options>
<button light (click)="renameChecklist(checklist)"><ion-icon
name="clipboard"></ion-icon></button>
<button danger (click)="removeChecklist(checklist)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>

</ion-item-sliding>

</ion-list>
</ion-content>

> Modify home.scss to reect the following:

.home-sliding-item {
margin: 5px;
}

.home-item {
font-size: 1.2em;
font-weight: bold;
color: #282828;
padding-top: 10px;
padding-bottom: 10px;
}

.secondary-detail {
display: block;

205
color: #cecece;
font-weight: 400;
margin-top: 10px;
}

Were not doing anything too crazy here, just adding a few tweaks to the margins, padding and colours.

Now lets do the same to the checklist page.

> Modify checklist.html to reect the following:

<ion-header>
<ion-navbar secondary>
<ion-title>
{{checklist.title}}
</ion-title>
<ion-buttons end>
<button (click)="uncheckItems()"><ion-icon
name="refresh-circle"></ion-icon></button>
<button (click)="addItem()"><ion-icon
name="add-circle"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>

<ion-content>

<ion-list no-lines>

<ion-item-sliding *ngFor="let item of checklist.items"


class="home-sliding-item">

206
<ion-item>
<ion-label>{{item.title}}</ion-label>
<ion-checkbox [checked]="item.checked" (click)="toggleItem(item)"
class="checklist-item">
</ion-checkbox>
</ion-item>

<ion-item-options>
<button light (click)="renameItem(item)"><ion-icon
name="clipboard"></ion-icon></button>
<button danger (click)="removeItem(item)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

> Modify checklist.scss to reect the following:

.checklist-item {
font-size: 0.9em;
font-weight: bold;
color: #282828;
padding-top: 0px;
padding-bottom: 0px;
padding-left: 4px;
border: none !important;
}

207
ion-item-content {
border: none !important;
}

ion-checkbox {
border-bottom: none !important;
}

ion-checkbox .item-inner {
border-bottom: none !important;
}

Once again, just a few minor tweaks here. Now we are going to add the styles that will apply across the

entire application:

> Modify app.core.scss to reect the following:

@import "../pages/home/home";
@import "../pages/checklist/checklist";
@import "../pages/intro/intro";

ion-content {
background-color: #32db64;
}

.logo {
max-height: 39px;
}

button {
border: none !important;

208
}

Here were making the entire application have a background colour of green, we set a maximum height

for the logo and remove borders from our buttons. Finally, we are going to override some of the SASS

variables.

> Modify app.variables.scss to reect the following:

$colors: (
primary: #387ef5,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

$list-background-color: #fff;
$list-ios-activated-background-color: #3aff74;
$list-md-activated-background-color: #3aff74;

$checkbox-ios-background-color-on: #32db64;
$checkbox-ios-icon-border-color-on: #fff;

$checkbox-md-icon-background-color-on: #32db64;
$checkbox-md-icon-background-color-off: #fff;
$checkbox-md-icon-border-color-off: #cecece;
$checkbox-md-icon-border-color-on: #32db64;

Weve modied the colours for the application and set a few iOS and Android specic styles (the names

should make it pretty clear what is being changed). One of the coolest things about Ionic 2 is how well it

handles UI dierences between iOS and Android, for the most part it just works awlessly out of the box.

209
If you take a look at the application on iOS and on Android, you will see the dierences:

NOTE: A handy way to see iOS and Android side by side is to use Ionic Lab, which can be activated

by using the ionic serve -l command. You may notice that the applications have a scrollbar on the

edge when viewing through Ionic Lab, I believe this is a bug and it is not present when running on an actual

device (or on the Chrome Dev Tools emulator).

As you can see, Ionic 2 automatically conforms to the norms of whatever platform the application is running

on.

210
Summary

Thats it! The application should now be completely nished and it nally actually looks pretty nice too.

211
Conclusion

Congratulations on making it through the Quick Lists tutorial. This application is a great example for

beginners to start getting their feet wet, and the main take aways from it are:

How to create, read, update and delete data

How to permanently store data and retrieve it

How to create and use your own observables

How to navigate and pass data between pages

How to create a data model

Theres always room to take things further though, especially when youre trying to learn something. Fol-

lowing tutorials is great, but its even better when you gure something out for yourself. Hopefully you

have enough background knowledge now to start trying to extend the functionality of the application by

yourself, heres a few ideas to try out:

Retheme the application with your own styling, try dierent colours, padding, margins and so on

[EASY]

Add a Date Created eld to the data model that records when a checklist was created, and display

it in the template (dont forget to make sure it gets loaded from memory too!) [MEDIUM]

Figure out how many items have been marked as completed in a single checklist, and display a

progress indicator (i.e 5/7 completed) [MEDIUM]

Add the ability to attach notes to any specic checklist item [HARD]

Remember, the Ionic 2 documentation is your best friend when trying to gure things out.

What next?

You have a completed application now, but thats not the end of the story. You also need to get it running

on a real device and submitted to app stores, which is no easy task. The nal sections in this book will

walk through how to take what you have done here, and get it onto the app stores so make sure to give

212
that a read.

213
Chapter 4

Giist

214
Lesson 1: Introduction

Giist was the rst application I created with Ionic 2, and it was originally built on one of the really early

alpha versions of Ionic 2. Its been updated a bit since then, and more recently with the Ionic 2 beta for

this book. Even though it was the rst app I built its still my favourite, so Im excited to walk you through

building it.

I think its my favourite because its just a really fun app. Its a fun app to build because a lot of interesting

topics are covered like using the Reddit API and using HTML5 video, and its a fun app to use because

who doesnt like looking at funny GIFs?

About Giist

Giist is a pretty simple application, the general idea is that a user can enter in any subreddit from reddit,

and the app will fetch and display GIFs from that subreddit.

Im going to assume if youre reading this book then you know what reddit is - but - I dont really like to

make any assumptions so if youre somehow not familiar with Reddit, basically its a site where people can

submit links to anything and the user base votes up stu that they think is cool or relevant. A subreddit

is basically a category on reddit, a few popular ones being:

gifs

askreddit

worldnews

As well as just fetching GIFs from reddit, users will also be able to supply some settings to congure the

application to their preferences. Although the concept is pretty simple, there are a few interesting things

that you will learn through building it, this will include:

Fetching data from a 3rd party API

Data storage

Theming

215
Lists

Modals

Data models

HTML5 Video

Heres a quick run down on the exact features of the application:

The user will be able to input any subreddit

An endless list of GIFs will be displayed as a list (assuming GIFs are available)

The user will be able to play a GIF by tapping it

The user will be able to set the following options: default subreddit, sort order, GIFs per page

The users settings will be remembered upon returning to the application

and a couple of screenshots to put it into context:

216
217
###Lesson Structure

218
1. Getting Ready

2. The List Page

3. The Reddit API and HTML5 Video

4. Settings

5. Theming

Ready?

Now that you know what youre in for, lets get to building it!

219
Lesson 2: Getting Ready

In this lesson we are going to prepare our application for the journey ahead. We are going to of course

generate the application, and we are also going to set up all of the components and Cordova plugins we

will need. At the end of this rst lesson we should have a nice skeleton application set up with everything

we need to start diving into coding.

A good rule of thumb before starting any new application is to make sure you have the latest version of

Ionic and Cordova, so if you havent done it recently then make sure to run:

npm install -g ionic@beta cordova

or

sudo npm install -g ionic@beta cordova

before you continue.

Generate a new application

We will be using the blank starter template for this application which, as the name implies, is basically an

empty Ionic project. It comes with one page built in called home which we will repurpose as our list page

in the next lesson.

> Run the following command to generate a new application

ionic start giflist blank --v2

> Make the new project your current working directory by running the following command:

cd giflist

Your project should now be generated - now you can open up the project folder in your favourite editor.

You can take a look at how your application looks by running the following command:

220
ionic serve

which for now should look something like this:

221
###Create the Required Components

The structure of this application is pretty simple, so all were going to have is 2 pages: the list page and

the settings page. Weve already got a home component which we will use as our list page, so we only

need to create the Settings page.

> Run the following command to generate a Settings page:

ionic g page Settings

Also remember that any time we generate a new component, we need to import its .scss le into our

app.core.scss le. The Ionic CLI automatically generates pages for us, but it doesnt automatically import

the styles in app.core.scss.

> Modify app.core.scss to import the components styles:

@import "../pages/home/home";
@import "../pages/settings/settings";

Create the Required Services

As well as our 2 pages, we are also going to create a data service to handle storing and retrieving the users

settings.

> Run the following command to generate a Data provider:

ionic g provider Data

Add Required Platforms

Before you can build for certain platforms, you need to add them to your project. This is something we

will be doing way later on in this course, but you may as well just set them up now.

> Run the following command to add the iOS platform to your application

222
ionic platform add ios

Run the following command to add the Android platform to your application

ionic platform add android

Add Required Cordova Plugins

This application will use a few dierent Cordova plugins. Remember, Cordova plugins can only be used

when running on a real device. Ill explain each plugin as we run through the commands for adding them.

> Run the following command to add the SQLite plugin:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

This plugin gives you access to native storage with an SQLite database. We are adding it to this application

because the Ionic local storage service can make use of this plugin to provide more stable data storage.

> Run the following command to add the In App Browser plugin:

ionic plugin add cordova-plugin-inappbrowser

This plugin makes a webview available to us that we can launch external websites in. We will be using this

plugin in this application to allow the users to view the original Reddit submission of a GIF in a browser.

> Run the following command to add the Status Bar plugin:

ionic plugin add cordova-plugin-statusbar

We will be adding this plugin to all projects to give us control over the status bar in our application (the bar

at the top of the devices screen that contains the time, battery information and so on).

> Run the following command to add the Splash Screen plugin:

ionic plugin add cordova-plugin-splashscreen

223
This plugin allows us to control the splash screen (the fullscreen graphic that briey displays when you

open an app)

> Run the following command to add the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

This plugin is required for all applications, and helps to dene what resources should be allowed to be

loaded in your application. Without it, resources you try to load will fail.

As well as adding the plugin, you also need to dene a Content Security Policy in your index.html

le. We will be adding a very permissive policy which will essentially allow us to load any resources.

Depending on your application, you may look into providing a more strict policy, but an open policy is

good for development.

> Modify your www/index.html le to include the following meta tag:

<meta http-equiv="Content-Security-Policy" content="font-src 'self' data:;


img-src * data:; default-src * 'unsafe-eval' 'unsafe-inline'">

Run the following command to add the Crosswalk plugin:

ionic plugin add cordova-plugin-crosswalk-webview

This is another plugin that we will add to every application, but you may decide you want to leave it out.

By adding this plugin, when you build for Android Crosswalk will be used. Android has a lot of issues,

especially with older devices, because there is so many dierent software versions out there and dierent

versions have dierent browsers (remember, since we are building HTML5 applications it is actually a

browser engine powering and running our application). What Crosswalk does is bundle a modern browser

into your application so no matter what device you are running on your app will be powered by the same

browser, and the Crosswalk browser can improve performance a lot.

The only real downside to this is that it increases the size of your application by a signicant amount. In

general, I think its worth it and Id recommend you include it but you may leave it out if you like. For more

information take a look at the Crosswalk Project website: https://crosswalk-project.org/

224
Set up Images

When building this application we are going to be making use of a few images. Ive included these in your

download pack but you will need to set them up in the application you generate.

> Copy the images folder in the download pack for this application from www/images to your own

www folder

Summary

Thats it! Were all set up and ready to go, now we can start working on the interesting stu.

225
Lesson 3: The List Page

In the last lesson we worked on getting everything set up correctly, and now were going to start actually

building stu. In this lesson well mostly be focusing on modifying the Home page to act as the list that

will contain our feed of GIFs.

The Layout

Before we start building the layout, which will be dened in home.html, lets take a look at what were

building:

226
Its a reasonably simple layout, we have a toolbar at the top that contains a search bar and a settings

button (which will launch our Settings page later). Beneath that, we have a list that holds all of the GIFs

returned from Reddit. Not pictured is the Load More button which sits at the bottom of the list, when the

227
user taps this it will load in the next page of GIFS.

First were going to look at the entire template to see everything in context, then were going to break it

down into smaller chunks that we will discuss in detail.

> Modify home.html to reect the following

<ion-header>
<ion-navbar secondary>

<ion-title>
<ion-searchbar primary placeholder="enter subreddit name..."
hideCancelButton></ion-searchbar>
</ion-title>

<ion-buttons end>
<button (click)="openSettings()"><ion-icon
name="settings"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content class="home">

<ion-list>

<ion-item no-lines>
GIF GOES HERE
<ion-list-header>
TITLE GOES HERE
</ion-list-header>

228
</ion-item>

<ion-item *ngIf="loading" no-lines style="text-align: center;">


<img src="images/loader.gif" style="width: 50px" />
</ion-item>

</ion-list>

<button light (click)="loadMore()">Load More...</button>

</ion-content>

In the rst part of this code we are setting up the navigation bar:

<ion-navbar secondary>

<ion-title>
<ion-searchbar primary placeholder="enter subreddit name..."
[(ngModel)]="subredditValue" hideCancelButton></ion-searchbar>
</ion-title>

<ion-buttons end>
<button (click)="openSettings()"><ion-icon
name="settings"></ion-icon></button>
</ion-buttons>

</ion-navbar>

We add the secondary directive to the <ion-navbar> which will alter its style to use the secondary

colour, which we will be changing later.

Were using <ion-title>, which is usually used to supply a title to be displayed on the navbar, to position

229
our search bar in the middle of the navbar. Usually youd have a separate toolbar for a searchbar so that it

can take up the whole space, but I didnt want to clutter the screen too much so were going to bend the

rules a bit here with this. In order for it to display properly, well need to add a few custom styles later.

Notice that we are also supplying the primary directive to give the searchbar the primary colour, and we

also use hideCancelButton so that the cancel button doesnt display when the user is typing. The most

important part here is [(ngModel)] which will tie the input of this eld to a subredditValue variable

we create in the class denition.

Then we use <ion-buttons> to place our settings button, which will launch our settings page in the

navbar. Using the end directive will place the button on the right, and if we wanted to place a button on

the left we could use the start directive. Weve also added a (click) listener to this button so that the

openSettings function will be called when the button is clicked. We havent created this function yet
so nothing will happen, but we will dene it later in home.ts.

Next we dene a list in the main content area:

<ion-list>

<ion-item no-lines>
GIF GOES HERE
<ion-list-header>
TITLE GOES HERE
</ion-list-header>
</ion-item>

<ion-item *ngIf="loading" no-lines style="text-align: center;">


<img src="images/loader.gif" style="width: 50px" />
</ion-item>

</ion-list>

230
Lists are one of the most frequently used components in mobile applications. In Ionic you can create an

<ion-list> and supply it any number of <ion-item> tags to create a list. For now we just have a
single item, but later we will modify this to automatically loop over every GIF we want to display. Notice

that we are also using <ion-list-header> to create a header area where we will be able to display the

title of the GIF, and we also add no-lines to the item so that there is no borders around list items.

As well as our GIF items, we are going to add one additional item at the bottom of the list which will contain

a loading animation. This will be used to display a spinning animation when new GIFs are being fetched,

but since we only want it to display when loading is occuring we use the *ngIf directive to control when

it displays. In this case, the loading animation will only display when loading evaluates to true (we will

dene this in our class denition later, and toggle it on and o when we are loading).

The last bit of code we have in our template is the load more button:

<button light (click)="loadMore()">Load More...</button>

Nothing too crazy happening here, we supply the light directive to again change the colour and we have

a (click) function set up that will eventually call the loadMore() function we will dene in our class

denition.

The Class Denition

With the template dened we have our view sorted, now we need to create the class denition to handle

all the logic our list page will use. This is where we will dene all the functions that our template references,

as well as any other code we want to run on this page.

Again, we are going to set up the code for the entire class rst and then we will discuss it bit by bit.

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {ModalController, AlertController} from 'ionic-angular';
import {Http} from '@angular/http';

231
import {SettingsPage} from '../settings/settings';
import {Data} from '../../providers/data/data';
import {FORM_DIRECTIVES, Control} from '@angular/common';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

@Component({
templateUrl: 'build/pages/home/home.html',
providers: [Data]
})
export class HomePage {

settings: any;
loading: boolean = false;
posts: any = [];
subreddit: string = 'gifs';
page: number = 1;
perPage: number = 15;
after: string;
stopIndex: number;
sort: string = 'hot'
moreCount: number = 0;
subredditValue: string;

constructor(public http: Http, public dataService: Data, public modalCtrl:


ModalController, public alertCtrl: AlertController) {
this.loadSettings();
}

232
fetchData(): void {
console.log("TODO: Implement fetchData()");
}

loadSettings(): void {
console.log("TODO: Implement loadSettings()");
}

showComments(post): void {
console.log("TODO: Implement showComments()");
}

openSettings(): void {
console.log("TODO: Implement openSettings()");
}

playVideo(e, post): void {


console.log("TODO: Implement playVideo()");
}

changeSubreddit(): void {
console.log("TODO: Implement changeSubreddit()");
}

loadMore(): void {
console.log("TODO: Implement loadMore()");
}

233
Theres obviously still quite a bit of code that needs to be added to this class, but even the basic setup of

the class weve just added is looking pretty complicated. So lets start talking through it.

First up are our import statements:

import {Component} from '@angular/core';


import {ModalController, AlertController} from 'ionic-angular';
import {Http} from '@angular/http';
import {SettingsPage} from '../settings/settings';
import {Data} from '../../providers/data/data';
import {FORM_DIRECTIVES, Control} from '@angular/common';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

Theres a bunch of imports here because we are setting up everything we will use later, but this is still

probably quite a few more imports than you will likely use in most of your classes.

The rst couple we are importing from the Ionic library - ModalController and AlertController - are all

pretty standard. Weve discussed these before, but to recap: ModalController allows us to create a

modal page that can be displayed on top of the current page, and AlertController allows us to create on

screen alerts, prompts and so on.

We also import Http to allow us to send requests to the Reddit API, the SettingsPage we generated earlier

(but are yet to complete) and our Data provider that we also generated earlier but havent completed.

The rest of the imports relate to the Observable functionality we will be adding shortly, so I will leave most of

the discussion around that until then. However, FORM_DIRECTIVES imports all the form related directives

from the Angular library, and Control will allow us to create a Control for inputs (which will supply our

Observable). All of the rxjs imports are from the RxJS library - somewhat annoyingly, you have to import

any operator you want to use with an Observable, so were doing that here.

In the next section of code we have our constructor function. The constructor is an important part of the

234
class because it is the rst bit of code that will be executed when the component is created. It allows us

to inject and set up references to any components or services we are using and its also a good spot to

make any function calls that you need to execute right away.

settings: any;
loading: boolean = false;
posts: any = [];
subreddit: string = 'gifs';
page: number = 1;
perPage: number = 15;
after: string;
stopIndex: number;
sort: string = 'hot'
moreCount: number = 0;
subredditValue: string;

constructor(public http: Http, public dataService: Data, public modalCtrl:


ModalController, public alertCtrl: AlertController) {
this.loadSettings();
}

In the constructor we are setting up references for our injected services (http, dataService and nav) and

outside of the constructor we set up a bunch more member variables which will be used to store various

things the application will need, in order these are:

The settings the user has supplied

Whether new GIFs are currently being fetched

The entries for all the GIFs currently loaded

The current subreddit

The current page (i.e. how many times the user has clicked Load More)

The amount of GIFs to display per page

235
A reference to the last post retrieved from Reddit (so we know where to start for the next page)

The sort order for GIFs

The app will keep trying to load more posts from reddit until it has enough for a full page of GIFs,

moreCount is used to tell it when it should stop trying to load more (i.e. if not enough GIFs are found

after hitting the API 20 times)

A variable that references the Control we will create, which supplies our Observable

We also make a call to the loadSettings() function which will load the users settings from memory if

available, and update this.settings with them.

Lets take a look at the remaining code in the class:

fetchData(): void {
console.log("TODO: Implement fetchData()");
}

loadSettings(): void {
console.log("TODO: Implement loadSettings()");
}

showComments(post): void {
console.log("TODO: Implement showComments()");
}

openSettings(): void {
console.log("TODO: Implement openSettings()");
}

playVideo(e, post): void {


console.log("TODO: Implement playVideo()");
}

236
changeSubreddit(): void {
console.log("TODO: Implement changeSubreddit()");
}

loadMore(): void {
console.log("TODO: Implement loadMore()");
}

The rest is just a bunch of functions. These functions will either be called from the template, or from

somewhere within the class itself (the constructor, or another function). Clearly these are all blank now but

we will be expanding on them later.

Using an Observable to Control Searching

Were going to get a bit fancy now and make use of Observables. Weve discussed what exactly an

Observable is in the basics section of this book so if you cant quite remember Id recommend going back

and taking a look at the Fetching Data, Observables and Promises section.

Most of the time when using Observables you will simply be subscribing to some Observable that is re-

turning values for you, like when using the Http service to fetch some data (which we will be doing for this

application in the next section). So you rarely have to create the Observables yourself. But, theres a lot

of fancy stu you can do with them if you want to.

We will be using the Control service we imported before to create a Control that will supply our Observ-

able. Controls work very similarly to two way data binding with [(ngModel)] in that it ties a variable in

the class denition, to an input eld in the template. So we are going to have to make some modications

rst.

> Modify the searchbar in home.html to reect the following:

<ion-searchbar primary placeholder="enter subreddit name..." hideCancelButton


[(ngModel)]="subredditValue"

237
[formControl]="subredditControl"></ion-searchbar>

What weve done here is add [formControl] which is used by the Control we will be creating. Next

well have to make some changes in the constructor for our class.

> Modify the constructor in home.ts to reect the following:

settings: any;
loading: boolean = false;
posts: any = [];
subreddit: string = 'gifs';
page: number = 1;
perPage: number = 15;
after: string;
stopIndex: number;
sort: string = 'hot'
moreCount: number = 0;
subredditValue: string;

subredditControl: Control;

constructor(public http: Http, public dataService: Data, public modalCtrl:


ModalController, public alertCtrl: AlertController) {

this.subredditControl = new Control();

this.subredditControl.valueChanges.debounceTime(1500)
.distinctUntilChanged().subscribe(subreddit => {

if(subreddit != '' && subreddit){


this.subreddit = subreddit;

238
this.changeSubreddit();
}

});

this.loadSettings();

First we create a new Control, and then we subscribe to the valueChanges Observable it supplies. If

youve read the section on Observables in the basics section of this book then most of this shouldnt look

too strange. We subscribe to the observable so that every time it emits a value we run some code. The

weird stu here though is the valueChanges.debounceTime(1500).distinctUntilChanged().

Basically, we can chain together as many of these operators as we want and they all do dierent things.

For example if we just did this:

this.subredditControl.valueChanges.subscribe

Then we would run the code we have supplied above every time the value of the subredditControl input

changes. If we then changed it to this:

this.subredditControl.valueChanges.debounceTime(1500).subscribe

We would only run the code when the input has changed, and when there hasnt been another change

within 1.5 seconds. This prevents us from sending out too many pointless requests to the API. If the user

was typing chemicalreactiongifs for example, the the code would be triggered for c, then ch, then che,

then chem and so on until the full string has been typed. Not only will this send o a bunch of useless

queries to the API, its going to be a bad experience for the user as well as the list is constantly ickering

and changing as they type. By adding debouncing, the code will only run once the full string has been

typed in (assuming the user doesnt take more than 1.5 seconds between typing each letter).

Finally, we can add one nal operator:

239
this.subredditControl.valueChanges.debounceTime(1500)
.distinctUntilChanged().subscribe

This will only run the code if the value is dierent to the last time it ran. So if a user typed gifs, hit the

backspace key to make it gif but then retyped the s to make it gifs again nothing would happen. We

dont need to reload the data for gifs because we are already there.

The end result is that the code will only trigger when the value has changed, the user is not currently typing

and the input value is dierent to what it was last time. The code that we are triggering simply checks if a

non empty value was supplied, and then changes the subreddit by setting the this.subreddit member

variable, and calling the changeSubreddit() function.

This will probably be one of the most confusing parts of this application, especially if youre completely

new to Observables. As you can tell this has allowed us to create some pretty useful functionality really

easily, but we could have just as easily used the normal ngModel approach and just used a button for

triggering searches instead.

Summary

Weve got o to a really good start in this lesson, and were well on the way to getting some cool stu

happening. We have a really nice basic structure which will allow us to easily build on functionality in the

next lessons. If you take a look at the application now by running:

ionic serve

you should see something that looks like this:

240
Pretty ugly right? And youre even going to get some errors in the console too. Trust me, itll all come

together in the end! In the next lesson were going to look at pulling in some real data from reddit.

241
Lesson 4: The Reddit API and HTML5 Video

One of the most interesting things about Giflist is that it wont ever contain any actual GIF les at all. The

GIF le format can be quite large and slow to load, which is especially a problem on mobile when people

may be more wary about using their data.

So what we are going to do is make sure we only ever pull in GIFs that provide a .webm or .gifv format.

This means we wont be displaying GIFs at all, we will be displaying videos.

Were going to display those videos using the HTML5 <video> tag. Its important to remember when

building HTML5 mobile applications, we can use any HTML5 feature within our applications. A great

example of this is Geolocation - we can use the native API to access the devices GPS, but we can also

just use the plain old HTML5 Geolocation API available to any website. Basically, anything that you can

do on a website you can do in your mobile application (but obviously we can do a lot more as well, given

that we can also access native functionality).

Before we get into building this functionality, lets talk about a few important issues with HTML5 video.

HTML5 Video Behaviour on iOS and Android

Using a framework like Ionic means a lot of the platform dierences are handled pretty seamlessly for us,

but theres still going to be problems with platform dierences that you will run into when creating cross

platform applications.

First, theres a few things you should know about the <video> element:

It can be played both inline and in fullscreen

It has a poster attribute that can be used to display an image before the video is loaded

Whether controls are displayed, whether it autoplays video and whether it plays inline can all be

controlled

One of the most important things to know though is that the behaviour of the <video> element is dierent

242
depending on which platform the application is running on.

On iOS videos play in fullscreen by default, but can be forced to play inline by using the webkit-

playsinline attribute. However, this is not universal across all iOS devices. On smaller devices,

fullscreen will still be forced even if you specify the webkit-playsinline attribute (basically, its not

possible to change this)

On Android videos play inline by default, but can be made fullscreen

Given those behavioural dierences, we then need to gure out how we want to approach the problem.

We basically have two options:

1. Accept the default behaviour and maintain the same code for both platforms

2. Check for the platform we are running on and run dierent code to achieve the desired behaviour.

Personally, I think playing the videos inline achieves a more desirable result. But since its not possible to

achieve that on small iOS devices, I decided to just go with the default behaviour. This means that there will

be a dierence in behaviour between iOS and Android, but I think both solutions are perfectly acceptable

and it will keep our code base nice and simple.

So, lets get to work. Were going to nish implementing a few more of the functions we dened in home.ts

now and well go through them one at a time.

Fetching Data from Reddit

Were going to start o with the most interesting and complicated function, which is pretty much the core

feature of the entire application: fetchData(). We will add the code rst and then walk through it

> Modify the fetchData function home.ts

fetchData(): void {

//Build the URL that will be used to access the API based on the users
current preferences

243
let url = 'https://www.reddit.com/r/' + this.subreddit + '/' + this.sort
+ '/.json?limit='+ this.perPage;

//If we aren't on the first page, we need to add the after parameter so
that we only get new results
//this parameter basically says "give me the posts that come AFTER this
post"
if(this.after){
url += '&after=' + this.after;
}

//We are now currently fetching data, so set the loading variable to true
this.loading = true;

//Make a Http request to the URL and subscribe to the response


this.http.get(url).map(res => res.json()).subscribe(data => {

let stopIndex = this.posts.length;


this.posts = this.posts.concat(data.data.children);

//Loop through all NEW posts that have been added. We are looping
through
//in reverse since we are removing some items.
for(let i = this.posts.length - 1; i >= stopIndex; i--){

let post = this.posts[i];

//Add a new property that will later be used to toggle a loading


animation
//for individual posts

244
post.showLoader = false;

//Add a NSFW thumbnail to NSFW posts


if(post.data.thumbnail == 'nsfw'){
this.posts[i].data.thumbnail = 'images/nsfw.png';
}

/*
* Remove all posts that are not in the .gifv or .webm format and
convert the ones that
* are to .mp4 files.
*/
if(post.data.url.indexOf('.gifv') > -1 ||
post.data.url.indexOf('.webm') > -1){
this.posts[i].data.url = post.data.url.replace('.gifv', '.mp4');
this.posts[i].data.url = post.data.url.replace('.webm', '.mp4');

//If a preview image is available, assign it to the post as


'snapshot'
if(typeof(post.data.preview) != "undefined"){
this.posts[i].data.snapshot =
post.data.preview.images[0].source.url.replace(/&amp;/g, '&');

//If the snapshot is undefined, change it to be blank so it


doesnt use a broken image
if(this.posts[i].data.snapshot == "undefined"){
this.posts[i].data.snapshot = "";
}
}
else {

245
this.posts[i].data.snapshot = "";
}
}
else {
this.posts.splice(i, 1);
}
}

//We are done loading now so change the loading variable back
this.loading = false;

//Keep fetching more GIFs if we didn't retrieve enough to fill a page


//But give up after 20 tries if we still don't have enough
if(data.data.children.length === 0 || this.moreCount > 20){

let alert = this.alertCtrl.create({


title: 'Oops!',
subTitle: 'Having trouble finding GIFs - try another subreddit,
sort order, or increase the page size in your settings.',
buttons: ['Ok']
});

alert.present();

this.moreCount = 0;
}
else {
this.after = data.data.children[data.data.children.length -
1].data.name;

246
if(this.posts.length < this.perPage * this.page){
this.fetchData();
this.moreCount++;
}
else {
this.moreCount = 0;
}
}

}, (err) => {
//Fail silently, in this case the loading spinner will just continue to
display
console.log("subreddit doesn't exist!");
});

Thats a pretty big function. Ive added some inline comments to help clear things up a little bit, but lets

also talk about each bit of code in detail.

First we build the URL that we are going to use to fetch data from the Reddit API. We use whatever

subreddit, sort and perPage values the user currently has set. You can modify this URL with any values

for these that you like and it will spit out the relevant JSON. If we have an after value set then we supply

that as well. This is how the Reddit API does pagination, if the user has hit the Load More button three

times then we only want to return the posts for the third page, e.g results 10-15 if the user has a page size

of 5. With the reddit API you can supply a posts name as the after parameter and it will return only the

posts after that post.

Then we use that URL to make a Http request and subscribe to the Observable returned after we map the

response to a JSON object, this converts the JSON string returned from the Reddit API into an object that

we can more easily use.

247
After that, we want to loop through all of the posts we loaded to perform some magic. We dont want

to loop through every single post we have stored on our this.posts variable though because a lot of

these have already been processed, we just want to loop through the new posts loaded - so we create

a stopIndex that is the length of the current this.posts array, and then we add the new posts to it.

When we are looping through the newly loaded posts we are doing a few things:

Removing any posts that are not in the .gifv or .webm formats

Converting .gifv and .webm links to .mp4

Creating a new showLoader property on the posts that will be used to toggle a loading animation

later

Assigning a NSFW thumbnail to NSFW posts

Assigning a preview image to be used as the video poster if it is available

Once we have nished the loop we should have an array of posts in the format we need, ready to be

displayed in the list. Theres one more important issue we need to take care of though. If we are loading

in say 10 posts per page from the Reddit API, but then only 3 of those are suitable GIFS, our page size is

only going to be 3. This might not y too well with the user.

To solve this issue, we will recursively fetch more data by calling the fetchData() function from within

the fetchData() function. This will keep adding more and more posts to the posts array until we have

our full 10 (or whatever the page size currently is) GIFs. We need to set a limit though, because this function

could just run innitely, so if we still dont have enough GIFs after 20 tries then we give up and display an

alert to the user.

Also notice that we have an error handler for the http.get request as well. If a successful request is

made, all of the code we just discussed will run, but if not (i.e. if the subreddit the user is trying to retrieve

results in a 404) then the error will be passed into the error handler and that will run instead.

Now that we have our list of GIFs loading into the application, we can display real data in our list. But rst,

we will need to update our template to actually use the data.

> Modify home.html to reect the following

248
<ion-header>
<ion-navbar secondary>

<ion-title>
<ion-searchbar primary placeholder="enter subreddit name..."
hideCancelButton [(ngModel)]="subredditValue"
[formControl]="subredditControl"></ion-searchbar>
</ion-title>

<ion-buttons end>
<button (click)="openSettings()"><ion-icon
name="settings"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content class="home">

<ion-list>
<div *ngFor="let post of posts">
<ion-item (click)="playVideo($event, post)" no-lines
style="background-color: #000;">
<img src="images/loader.gif" *ngIf="post.showLoader" style="width:
25px; position: absolute; left: 85%;"
[style.top]="post.loaderOffset" />
<video loop [src]="post.data.url" [poster]="post.data.snapshot">
</video>
<ion-list-header (click)="showComments(post)" style="text-align:
left;">

249
{{post.data.title}}
</ion-list-header>
</ion-item>
</div>
<ion-item *ngIf="loading" no-lines style="text-align: center;">
<img src="images/loader.gif" style="width: 50px" />
</ion-item>
</ion-list>

<button light (click)="loadMore()"><span style="width: 100%;">Load


More...</span></button>

</ion-content>

As you can see above we are now using the URL of the post as the source of the video element, as well

as suplying the preview snapshot as the poster and the title in the header. Weve also added a few more

things here as well.

Weve added a click event that will call the playVideo function when the video is tapped, passing along

the click event (which we will make use of for reasons Ill explain later) and a reference to the post that was

clicked. We have a seperate click event for the <ion-list-header> that shows the GIFs title which will

launch the InAppBrowser.

We also add the loader GIF to the item in the list. When a user taps on a video it will still need some time

to load in, so we add the loading animation so that the user knows something is happening. Without it,

the user will likely just think the application isnt working.

Weve added a few standard style attributes around the place, but the one on the loader GIF looks a little

funny:

[style.top]="post.loaderOffset"

This code assigns the result of the post.loaderOffset expression directly to the style.top property,

250
which is used to dene how far away from the top the GIF should display. Youll see how we use this

loaderOffset value in just a moment when we dene the playVideo function.

Lets also add a bit of code to the loadSettings function so that the fetchData function gets called

when we run the application (since loadSettings is called from the constructor). This is just so we can

see some stu happening now, we will change it later.

> Modify the loadSettings function in home.ts to reect the following:

loadSettings(): void {
this.fetchData();
}

If you load up your application in the browser now, you should see something like this:

251
Its still not looking great, but its looking a lot better and we have some cool GIFs displaying in there now

(which unfortunately we cant actually play just yet).

252
Playing our GIFs (videos)

Our GIFs are sitting in our list now ready to go, we just need to get them movin and shakin. To do that,

we are going to have to nish implementing the playVideo function.

> Modify the playVideo function in home.ts

playVideo(e, post): void {

//Create a reference to the video


let video = e.target;

//Set the loader animation in the right position


post.loaderOffset = e.target.offsetTop + 20 + "px";

//Toggle the video playing


if(video.paused){

//Show the loader gif


post.showLoader = true;
video.play();

//Once the video starts playing, remove the loader gif


video.addEventListener("playing", function(e){
post.showLoader = false;
});

} else {
video.pause();
}

253
}

If the user taps on the video then we want to play the video (or pause it if it is already playing). We use the

event to set the correct position for the loading gif to display whilst the video is loading. Once the video

has nished loading we hide this gif.

Launching Comments in the In App Browser

Remember when we were setting up this application, we installed the In App Browser plugin? Well, we are

going to make use of that now. When the user clicks on the title of the video we want to launch a browser

with the link to the original submission of the post.

> Add the following import to the top of home.ts

import {InAppBrowser} from 'ionic-native';

Modify the showComments function in home.ts

showComments(post): void {
InAppBrowser.open('http://reddit.com' + post.data.permalink, '_system',
'location=yes');
}

Compared to the others, this is a pretty simple function. We simply grab the link from the posts data and

call the open method on InAppBrowser to launch the browser window. Its important to remember that

when running through the browser, this plugin will not be available. So if you try to launch the comments

while testing through the browser you will just get an error.

Loading More GIFS

One important function we havent go to yet is the loadMore function, this is what is called when the

user hits the Load More button. What we want to do when this happens is load in the next page of GIFs.

254
Given the setup of our fetchData function, this is actually really easy to do.

> Modify the loadMore function in home.ts

loadMore(): void {
this.page++;
this.fetchData();
}

All we do is increase the page number and then call fetchData again, simple!

Changing Subreddits

The nal function we want to nish up for now (there is still a bit more to do later) is the changeSubreddit

function.

> Modify the changeSubreddit function in home.ts

changeSubreddit(): void {

this.page = 1;
this.posts = [];
this.after = null;
this.fetchData();

We really got the hard stu out of the way early, as this is another pretty simple function. When the subreddit

changes we need to reset everything. If we were up to the 5th page of chemicalreactiongifs then we dont

want to load the 5th page when we switch to perfectloops. All we have to do here is reset the page, clear

the posts data, and clear the after value, then we just call the fetchData function again. Weve already

set the subreddit elsewhere, so calling the fetchData function will pull in data from whatever the current

subreddit is now.

255
Summary

This lesson was really the bulk of the whole application, theres still a bit more to do but you can take a

breather if youve made it this far. You should now have an app that pretty much works - itll still be pretty

ugly and doesnt have the ability to save settings, but it works. Well be getting to both of those things in

the next two lessons.

256
Lesson 5: Settings

We have everything working pretty nicely now - theres GIFs coming in from reddit, ltered to only include

the le format we want and we have them displaying nicely using <video> elements.

We currently have the gifs subreddit set as the default, which is certainly a good choice for this app, and

the user can change this if they like. When they leave the application and come back though, it is going

to get set back to the default gifs subreddit. This would get pretty annoying if they arent interested in

looking at the gifs subreddit.

So what we are going to do is create a Settings page, where the user can set the following:

Default subreddit

GIFs to load per page

Sort order

The cool thing about implementing a feature like this is that it also gives us a chance to cover how to

store data permanently so that the application will remember the settings upon the user returning to the

application.

Creating the Settings Page

To open the settings page we are going to use a Modal. Opening a page as a Modal is a bit dierent to

just opening a page normally in that it is completely separate to the navigation ow of the app. A Modal is

basically a single page that can be opened over the top of the current content and then closed (Im sure

youre familiar with other modal windows around the web like Facebooks photo viewer).

Any page can be opened as a Modal, so when we create our Settings page we are going to do it just like

we would any other page. Lets start o by dening the template for our Settings page.

> Modify settings.html to reect the following:

<ion-header>

257
<ion-toolbar secondary>
<ion-title>Settings</ion-title>

<ion-buttons end>
<button (click)="close()"><ion-icon name="close"></ion-icon></button>
</ion-buttons>

</ion-toolbar>
</ion-header>

<ion-content padding class="settings">

<ion-card>
<ion-card-header>
About
</ion-card-header>
<ion-card-content>
<p><strong>Giflist</strong> is a client for <strong>reddit</strong>
that will endlessly <strong>stream GIFs</strong> from <strong>any
subreddit that predominantly contains gifs</strong>. By default
it uses the popular <strong>gifs</strong> subreddit, but you can
change this to whatever you like, e.g: perfectloops, me_irl,
chemicalreactiongifs.</p>
<br />
<p>To play a GIF, just tap it. To view the original submission for
any GIF, just tap the title section and it will open in reddit.
You can configure some settings for the application below.
Enjoy!</p>
</ion-card-content>
</ion-card>

258
<h3>Subreddit</h3>

<ion-input type="text" placeholder="enter subreddit name..."


[(ngModel)]="subreddit"></ion-input>

<h3>Sort</h3>

<ion-segment secondary [(ngModel)]="sort">


<ion-segment-button value="hot">
Hot
</ion-segment-button>
<ion-segment-button value="top">
Top
</ion-segment-button>
<ion-segment-button value="new">
New
</ion-segment-button>
</ion-segment>

<h3>Per Page</h3>

<ion-segment secondary [(ngModel)]="perPage">


<ion-segment-button value="5">
5
</ion-segment-button>
<ion-segment-button value="10">
10
</ion-segment-button>
<ion-segment-button value="15">

259
15
</ion-segment-button>
</ion-segment>

<button (click)="save()" secondary style="width: 100%; margin: 20px


0px;"><span style="width:100%;">Save Settings</span></button>

</ion-content>

Theres nothing too crazy going on in this template. We dene our navbar as usual, and we add a button

to it which will call a close method we will dene shortly to dismiss the modal window.

We add an <ion-card> to display some information about the app to the user, and then we dene a

few dierent inputs. We use a simple text input for setting the subreddit, but we use segment compo-

nents to control the sort and perPage values. All of these inputs have two way data binding set up with

[(ngModel)] so we will easily be able to access these values in our class denition, which we will create
now.

> Modify settings.ts to reect the following:

import {Component} from '@angular/core';


import {ViewController, NavParams} from 'ionic-angular';

@Component({
templateUrl: 'build/pages/settings/settings.html',
})
export class SettingsPage {

perPage: number;
sort: string;
subreddit: string;

260
constructor(public view: ViewController, public navParams: NavParams) {

this.perPage = this.navParams.get('perPage');
this.sort = this.navParams.get('sort');
this.subreddit = this.navParams.get('subreddit');
}

save(): void {

let settings = {
perPage: this.perPage,
sort: this.sort,
subreddit: this.subreddit
};

this.view.dismiss(settings);
}

close(): void {
this.view.dismiss();
}
}

The rst strange thing you might notice here is that we are using navParams to grab some values. We

will pass these values into the Settings page from the Home page when we launch the Modal because we

want to pre set the values on the settings page to whatever they are currently.

Another strange thing about this Page is that we are importing the ViewController - we need this in order

to allow us to dismiss this page from within its own class when it is launched as a Modal. In this case we

want to close the page both when the user hits the save button, and when the user hits the close button.

If the user hits the close button, then we wont save the settings they supplied.

261
We are going to be working on saving the settings in the next portion of this lesson, but you might be

wondering how exactly that is going to happen - shouldnt the save function here actually do something

with the data? With the way Modals work, we can return data from the Page that was launched as a Modal

when it is dismissed, so in this case when we call the dismiss function on the view we will be sending

the settings values back to the Home page, which will be the page that launched the Modal - this is where

we will handle saving the data.

Opening the Settings Page as a Modal

Now that we have our Settings page dened, we just need to launch it as a Modal. Weve already set up a

button in the header to open the settings page, so all we need to do is nish dening the openSettings

function.

> Modify the openSettings function in home.ts:

openSettings(): void {

let settingsModal = this.modalCtrl.create(SettingsPage, {


perPage: this.perPage,
sort: this.sort,
subreddit: this.subreddit
});

settingsModal.onDidDismiss(settings => {

if(settings){
this.perPage = settings.perPage;
this.sort = settings.sort;
this.subreddit = settings.subreddit;

//this.dataService.save(settings);

262
this.changeSubreddit();
}

});

settingsModal.present();

As you can see above, we pass the Settings page we created into the Modal we are creating, and we also

pass through the data we are grabbing in the Settings page here.

We set up an onDidDismiss listener that will detect when the Modal is closed and pass in any data that

was sent back when the Modal was dismissed. Since we are only supplying data through the dismiss

function when the user hits save, this is the only time this code will run. If there are settings passed back

then we update the current values with the new values, and we also use our dataService to save the

values to storage (we still need to dene this service though). Since we have new settings values now we

also call changeSubreddit to reset everything and load new GIFs based on the new settings.

NOTE: Since we havent implemented the data service yet, we comment out the call to it for now.

Finally we call the present method to display the Modal to the user.

Saving Data

The nal piece of the puzzle is to create our Data service to handle saving our settings to memory, and

also retrieving those settings from memory.

To save the data, we are going to use the SqlStorage service provided by Ionic. This is one of my favourite

additions in Ionic 2, because it makes the complicated process of storage extremely easy.

Basically, its very easy to store some data locally to retrieve later. Local storage is supported universally

263
by most browsers, and you can easily set a bit of data like this:

localStorage.setItem('myKey', 'myValue');

Theres two main problems with this though:

1. There is a limit of 5MB on storage

2. Its not stable and can be wiped by the browser

To deal with this, there is a bunch of dierent options available. One popular method though, and the one

we will be using, is to use the SQLite plugin. This provides access to a native SQLite database to store

your data, which removes the storage restrictions and cant be wiped from memory (the data sits outside

of the browser in this case). The reason we are using the SqlStorage service from Ionic is that it provides

us a nice interface to use this plugin, and if the plugin is not available it will fall back automatically to use

other local storage methods. Weve already installed the SQLite plugin in the rst lesson, so all we need

to do is implement the SqlStorage service.

> Modify data.ts to reect the following:

import {Injectable} from '@angular/core';


import {Storage, SqlStorage} from 'ionic-angular';

@Injectable()
export class Data {

storage: Storage;

constructor() {
this.storage = new Storage(SqlStorage, {name:'giflist-settings'});
}

getData(): Promise<any> {
return this.storage.get('settings');

264
}

save(data): void {
let newData = JSON.stringify(data);
this.storage.set('settings', newData);
}

First, in our constructor we set up a reference to our storage service. We create an instance of the generic

Storage service and then use SqlStorage to set it up with a database name of settings. By using Sql-

Storage the Storage service will make use of the SQLite plugin when running on a device, alternatively

we couldve used LocalStorage to just use the browser storage. Now we will be able to interact with this

through the provider to store and retrieve data.

Then we just have two functions, one for getting data and one for saving data. The getData() function

will return all of the data we have stored on settings in storage, and save() will update settings in

storage with a new set of data. Weve already set up the call to save() when the Settings Modal is

dismissed, now we just need to dene the loadSettings function that will handle loading previously

saved settings when the app is opened.

> Modify the loadSettings function in home.ts

loadSettings(): void {

this.dataService.getData().then((settings) => {

if(typeof(settings) != "undefined"){

this.settings = JSON.parse(settings);

if(this.settings.length != 0){

265
this.sort = this.settings.sort;
this.perPage = this.settings.perPage;
this.subreddit = this.settings.subreddit;
}

this.changeSubreddit();

});

Here we are simply calling the getData function we dened in our data service, and when that returns

our settings data we parse the JSON into an object and read the values into our member variables. Then

we call the changeSubreddit function to trigger posts from Reddit to be fetched with the new settings.

Now that we have implemented the data service, we can uncomment the call to the data service we made

earlier.

> Modify the openSettings function in home.ts to reect the following:

openSettings(): void {

let settingsModal = this.modalCtrl.create(SettingsPage, {


perPage: this.perPage,
sort: this.sort,
subreddit: this.subreddit
});

settingsModal.onDidDismiss(settings => {

266
if(settings){
this.perPage = settings.perPage;
this.sort = settings.sort;
this.subreddit = settings.subreddit;

this.dataService.save(settings);
this.changeSubreddit();
}

});

settingsModal.present();

Summary

Now that weve nished up our settings page and data service, the functionality of the application is

completely nished! Its still pretty ugly though.

So we have one lesson remaining where we are going to add in a few styles to make everything look a lot

nicer.

267
Lesson 6: Styling

Were almost there, just a little bit of styling to go and well have a completed application. Before we dene

our styles we are going to add a few classes to our template les.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar secondary>

<ion-title>
<ion-searchbar primary placeholder="enter subreddit name..."
hideCancelButton [(ngModel)]="subredditValue"
[ngFormControl]="subredditControl"></ion-searchbar>
</ion-title>

<ion-buttons end>
<button (click)="openSettings()"><ion-icon
name="settings"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content class="home">

<ion-list>
<div *ngFor="let post of posts">
<ion-item (click)="playVideo($event, post)" no-lines
style="background-color: #000;">
<img src="images/loader.gif" *ngIf="post.showLoader" style="width:

268
25px; position: absolute; left: 85%;"
[style.top]="post.loaderOffset" />
<video loop [src]="post.data.url" [poster]="post.data.snapshot">
</video>
</ion-item>
<ion-list-header (click)="showComments(post)" class="gif-title"
style="text-align: left;">
{{post.data.title}}
</ion-list-header>
</div>
<ion-item *ngIf="loading" no-lines style="text-align: center;">
<img src="images/loader.gif" style="width: 50px" />
</ion-item>
</ion-list>

<button light (click)="loadMore()" class="load-more-button"><span


style="width: 100%;">Load More...</span></button>

</ion-content>

> Modify settings.html to reect the following:

<ion-header>
<ion-toolbar secondary>
<ion-title>Settings</ion-title>

<ion-buttons end>
<button (click)="close()"><ion-icon name="close"></ion-icon></button>
</ion-buttons>

</ion-toolbar>

269
</ion-header>

<ion-content padding class="settings">

<ion-card>
<ion-card-header>
About
</ion-card-header>
<ion-card-content>
<p><strong>Giflist</strong> is a client for <strong>reddit</strong>
that will endlessly <strong>stream GIFs</strong> from <strong>any
subreddit that predominantly contains gifs</strong>. By default
it uses the popular <strong>gifs</strong> subreddit, but you can
change this to whatever you like, e.g: perfectloops, me_irl,
chemicalreactiongifs.</p>
<br />
<p>To play a GIF, just tap it. To view the original submission for
any GIF, just tap the title section and it will open in reddit.
You can configure some settings for the application below.
Enjoy!</p>
</ion-card-content>
</ion-card>

<h3>Subreddit</h3>

<ion-input type="text" placeholder="enter subreddit name..."


[(ngModel)]="subreddit"></ion-input>

<h3>Sort</h3>

270
<ion-segment secondary [(ngModel)]="sort">
<ion-segment-button value="hot">
Hot
</ion-segment-button>
<ion-segment-button value="top">
Top
</ion-segment-button>
<ion-segment-button value="new">
New
</ion-segment-button>
</ion-segment>

<h3>Per Page</h3>

<ion-segment secondary [(ngModel)]="perPage">


<ion-segment-button value="5">
5
</ion-segment-button>
<ion-segment-button value="10">
10
</ion-segment-button>
<ion-segment-button value="15">
15
</ion-segment-button>
</ion-segment>

<button (click)="save()" secondary style="width: 100%; margin: 20px


0px;"><span style="width:100%;">Save Settings</span></button>

</ion-content>

271
If youve ready the basics section then youll know that theres quite a few ways we can dene styles, if

you skipped over theming in the basics section then I highly recommend going back to read it now.

Were going to be using just about every method available to dene our styles, we will be adding some

to the component specic les, some to platform specic les, some to the generic le and some to the

variables le. Well start o with the styling that is specic to the Home component.

> Modify home.scss to reect the following

.gif-title {
background-color: #fff;
}

ion-label {
margin: 0px;
}

ion-list {
margin: 0px !important;
}

ion-content img {
width: 100%;
height: auto;
}

ion-item-content, .item-inner, .item {


margin: 0 !important;
padding: 0 !important;
text-align: center;
}

272
video {
max-width: 100%;
height: auto;
background-color: #000;
}

.load-more-button {
width: 100%;
margin: 0px;
}

These are all pretty basic styles, for the most part we are just changing some colours and removing padding

and margins. The most important styles here are the ion-item-content and video styles which serve

to make sure the video scales and sits nicely in the list. Ideally we want the video to take up the full width

of the item but in the case of portrait GIFs this isnt always possible, so in that case we would center the

GIF and have a black background which looks pretty good.

Now we are going to dene just one style in the iOS specic style sheet. Our searchbar in the header is

a bit small since weve added it to the <ion-title> section, so we are going to make it a little bigger.

The searchbar already displays ne on Android though, so we dont want the style to apply there - which

is why we are going to put it in app.ios.scss.

> Add the following style to app.ios.scss:

ion-title {
padding: 0px 35px 1px 35px;
}

Next we are going to add a background colour to the body tag, which we will add to the generic .scss

le.

Add the following style to app.core.scss:

273
body {
background-color: #e74c3c;
}

Finally, we will dene some shared variables to be used in the application using the app.variables.scss

le.

> Modify the styles in app.variables.scss to reect the following:

$colors: (
primary: #ecf0f1,
secondary: #e74c3c,
danger: #f53d3d,
light: #e74c3c,
dark: #222222
);

$searchbar-ios-background-color: #e74c3c;
$searchbar-ios-input-background-color: #c94234;

$button-ios-border-radius: 0px;

Weve changed a few of the colours up here, made some modications to the search bar, and set the iOS

button border radius so that our Load More button is square instead of rounded.

If you load up the application should now look something like this:

274
Much better! If you take a look at the Android version (you can do this by setting the Emulator in Chrome

Dev Tools to emulate a Samsung or other Android device, or just use the ionic serve -l command)

you will nd it looks like this:

275
(ignore the white bar on the right, this isnt present on actual devices)

It certainly looks a little dierent, but it still looks good. One of the best things about Ionic is that it au-

tomatically conforms to design conventions of the platform it is running on as best it can. So in general,

the less you mess with this the better. If you mainly use an iOS device, or if you mainly use an Android

device, the styling of the other platform may look a little weird to you, but it wont look weird to users of

that platform. Unless you have good reason to, you shouldnt modify the styling of one platform to look

like the other (which is certainly possible).

276
Summary

This was a pretty short lesson compared to the others, but it shows how easy it can be with a little eort

to create a nice custom theme for your app.

277
Conclusion

Congratulations on making it through the Giist tutorial. Weve learned a lot through developing this appli-

cation, but the main take aways are:

How to fetch data from a 3rd party API

How to store and retrieve data locally

How to use Observables

How to use conditionals in templates

How to make use of events

Theres always room to take things further though, especially when youre trying to learn something. Fol-

lowing tutorials is great, but its even better when you gure something out for yourself. Hopefully you

have enough background knowledge now to start trying to extend the functionality of the application by

yourself, heres a few ideas to try out:

Create your own custom theme for the application [EASY]

Create a Random button that will take you to a random subreddit from a predened array [EASY]

Modify the application to return pictures instead of GIFs [MEDIUM]

Modify the application to return pictures and GIFs [HARD]

Allow users to save their favourite GIFs into its own list that can be viewed by typing favorites in

the search bar [VERY HARD]

Remember, the Ionic 2 documentation is your best friend when trying to gure things out.

What next?

You have a completed application now, but thats not the end of the story. You also need to get it running

on a real device and submitted to app stores, which is no easy task. The nal sections in this book will

walk through how to take what you have done here, and get it onto the app stores so make sure to give

that a read.

278
Chapter 5

Snapaday

279
Lesson 1: Introduction

Snapaday is based around everybodys favourite native plugin, the camera. I dont actually have any data

to back that claim up, but the camera is denitely one of the coolest integrations. Its also a plugin people

often struggle with, its one of those things that looks easy on the surface but theres a few tricks to it.

In fact, Snapaday is pretty much the native plugin app in this book - as well as covering how to use the

camera well also be integrating local notications and social sharing (weve got to get those seles on

Facebook right?).

About Snapaday

Snapaday was actually the Ionic 1 example used in my Mobile Development for Web Developers course,

so I gured when transitioning from Ionic 1 to Ionic 2 it would be a great example to use - especially for

those of you who have built the Ionic 1 version of Snapday.

The general idea is that the user takes a photo with the application every day, and then they can play back

their photos in a fast slideshow to see how theyve changed over time (ever see this video?). To be more

precise, the exact features of the application will include:

Allowing the user to take a photo (only once per day)

Displaying a list of all photos the user has taken in a list

Allowing the user to delete any photos they no longer want

The ability to play photos back in a fast slideshow format

The ability to share photos

Reminders with local notications

Throughout building this some concepts you will learn are:

How to integrate native plugins

How to use the Camera API

How to use the File API

280
How to use local notications

How to use modals

How to create a custom provider

How to store data permanently

and heres a look at what well be building:

281
###Lesson Structure

282
1. Getting Ready

2. The Layout

3. Taking Photos with the Camera

4. Storing and Retrieving Photos

5. Creating a Custom Pipe and Flipbook of all Photos

6. Integrating Local Notications

7. Styling

Ready?

Now that you know what youre in for, lets get to building it!

Lesson 2: Getting Ready

In this lesson we are going to prepare our application for the journey ahead. We are going to of course

generate the application, and we are also going to set up all of the components and Cordova plugins we

will need. At the end of this rst lesson we should have a nice skeleton application set up with everything

we need to start diving into coding.

A good rule of thumb before starting any new application is to make sure you have the latest version of

Ionic and Cordova, so if you havent done it recently then make sure to run:

npm install -g ionic@beta cordova

or

283
sudo npm install -g ionic@beta cordova

before you continue.

Generate a new application

We will be using the blank starter template for this application which, as the name implies, is basically

an empty Ionic project. It comes with one page built in called home which we will repurpose in the next

lesson.

> Run the following command to generate a new application

ionic start snapaday blank --v2

Make the new project your current working directory by running the following command:

cd snapaday

Your project should now be generated - now you can open up the project folder in your favourite editor.

You can take a look at how your application looks by running the following command:

ionic serve

which for now should look something like this:

284
###Create the Required Components

The structure of this application is pretty simple, so all were going to have is 2 pages: the home page

285
which will contain the photos and a page for the slideshow. Weve already got a home component which

we will use as our list page, so we only need to create the Slideshow page.

> Run the following command to generate a Slideshow page:

ionic g page Slideshow

Also remember that any time we generate a new component, we need to import its .scss le into our

app.core.scss le. The Ionic CLI automatically generates pages for us, but it doesnt automatically import

the styles in app.core.scss.

> Modify app.core.scss to import the components styles:

@import "../pages/home/home";
@import "../pages/slideshow/slideshow";

Create the Required Services

As well as our 2 pages, we are also going to create a data service to handle storing and retrieving the photos,

a data model to represent our photos and since well be using quite a lot of alerts in the application we will

be creating a service to handle those more easily.

> Run the following command to generate a Data provider:

ionic g provider Data

> Run the following command to generate a Photo Model provider:

ionic g provider PhotoModel

> Run the following command to generate a our Alert provider:

ionic g provider SimpleAlert

Well also be creating our own custom Pipe in this application to handle converting the dates our photos

were taken into something more user friendly like 5 days ago. Lets create that now as well.

286
> Run the following command to generate the Days Ago pipe:

ionic g pipe DaysAgo

IMPORTANT: For some reason this pipe currently generates as DaysAgo.ts rather than days-ago.ts, so

make sure you rename it to days-ago.ts.

Add Required Platforms

Before you can build for certain platforms, you need to add them to your project. This is something we

will be doing way later on in this course, but you may as well just set them up now.

> Run the following command to add the iOS platform to your application

ionic platform add ios

> Run the following command to add the Android platform to your application

ionic platform add android

Add Required Cordova Plugins

This application will use a few dierent Cordova plugins. Remember, Cordova plugins can only be used

when running on a real device. Ill explain each plugin as we run through the commands for adding them.

> Run the following command to add the SQLite plugin:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

This plugin gives you access to native storage with an SQLite database. We are adding it to this application

because the Ionic local storage service can make use of this plugin to provide more stable data storage.

> Run the following command to add the local notication plugin:

ionic plugin add de.appplant.cordova.plugin.local-notification

287
This plugin is what will allow us to create local notications for the application. Unlike push notications,

local notications are handled completely on the users device and dont require any external services.

> Run the following command to add the Camera plugin:

ionic plugin add cordova-plugin-camera

This plugin adds the ability to access the users camera and return photos from it. As well as providing

access to the camera, it also allows for the retrieval of photos from the users photo library.

> Run the following command to add the File plugin:

ionic plugin add cordova-plugin-file

The File plugin allows us to interact with the le system on the device, which we will be using to move the

photos that are taken with the application to somewhere permanent.

> Run the following command to add the Social Sharing plugin:

ionic plugin add cordova-plugin-x-socialsharing

The social sharing plugin is generic and allows users to share to a whole bunch of dierent platforms (like

social media, but also email, SMS and more) or you can just trigger a share to one specic platform.

> Run the following command to add the Status Bar plugin:

ionic plugin add cordova-plugin-statusbar

We will be adding this plugin to all projects to give us control over the status bar in our application (the bar

at the top of the devices screen that contains the time, battery information and so on).

> Run the following command to add the Splash Screen plugin:

ionic plugin add cordova-plugin-splashscreen

This plugin allows us to control the splash screen (the fullscreen graphic that briey displays when you

open an app)

288
> Run the following command to add the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

This plugin is required for all applications, and helps to dene what resources should be allowed to be

loaded in your application. Without it, resources you try to load will fail.

As well as adding the plugin, you also need to dene a Content Security Policy in your index.html

le. We will be adding a very permissive policy which will essentially allow us to load any resources.

Depending on your application, you may look into providing a more strict policy, but an open policy is

good for development.

> Modify your www/index.html le to include the following meta tag:

<meta http-equiv="Content-Security-Policy" content="font-src 'self' data:;


img-src * data:; default-src * 'unsafe-eval' 'unsafe-inline'">

> Run the following command to add the Crosswalk plugin:

ionic plugin add cordova-plugin-crosswalk-webview

This is another plugin that we will add to every application, but you may decide you want to leave it out.

By adding this plugin, when you build for Android Crosswalk will be used. Android has a lot of issues,

especially with older devices, because there is so many dierent software versions out there and dierent

versions have dierent browsers (remember, since we are building HTML5 applications it is actually a

browser engine powering and running our application). What Crosswalk does is bundle a modern browser

into your application so no matter what device you are running on your app will be powered by the same

browser, and the Crosswalk browser can improve performance a lot.

The only real downside to this is that it increases the size of your application by a signicant amount. In

general, I think its worth it and Id recommend you include it but you may leave it out if you like. For more

information take a look at the Crosswalk Project website: https://crosswalk-project.org/

289
Set up Images

When building this application we are going to be making use of a few images. Ive included these in your

download pack but you will need to set them up in the application you generate.

> Copy the images folder in the download pack for this application from www/images to your own

www folder

Summary

Thats it! Were all set up and ready to go, now we can start working on the interesting stu.

290
Lesson 3: The Layout

I nd creating a basic layout is usually a good rst step when building an application - it even serves as a

sort of wireframing exercise, to help solidify the requirements of the application. Theres nothing too crazy

we need to do for the layout of this application, but there is one little tricky (and I think clever) user interface

element that we are going to add.

We have two pages in this application, the home page and the slideshow page, and we will create both

of them in this lesson. The slideshow page is very simple though, so most of this will be about the home

page.

The Home Page

The home page in this application will contain a list of all of the photos that the user has taken with the

ability to delete them, an option to take a new photo (but only if they have not already taken one that day),

and an option to play a slideshow of all of their photos. Heres what it will look like:

291
The trickiest part of this layout is that SMILE! image at the top, which is actually a button that will serve

as our take a photo option, but again, only if the user has not already taken a photo. If they have already

292
taken a photo then we dont want to display that image.

Lets start o by creating the basic layout, and then we will look at how we can implement that take photo

button.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar danger>
<ion-title>
<img src = "images/logo.png" />
</ion-title>

<ion-buttons end>
<button (click)="playSlideshow()"><ion-icon
name="play"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content>

<ion-list>

<ion-item-sliding>

<ion-item>
<img src="http://placehold.it/100x100" />
<ion-badge item-right light>0 days ago</ion-badge>
</ion-item>

293
<ion-item-options>
<button light (click)="removePhoto(photo)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

Lets break this template down bit by bit now. The rst section of this template is our navigation bar:

<ion-navbar danger>
<ion-title>
<img src = "images/logo.png" />
</ion-title>

<ion-buttons end>
<button (click)="playSlideshow()"><ion-icon
name="play"></ion-icon></button>
</ion-buttons>

</ion-navbar>

The <ion-navbar> allows us to add a header bar to the top of our application that can hold buttons,

titles and even integrates directly with Ionics navigation system to display a back button when necessary.

We add the danger attribute to the navbar to style it with our danger colour, which is dened in

app.variable.scss (we will congure these later).

Inside of this navbar we use <ion-title>, which is typically used to display a text title for the current

page, to display our logo. We also use <ion-buttons> to create a button in the navbar. By specifying

294
the end attribute, the buttons will be placed on the right side, but if we were to specify the start attribute

it would be displayed on the left instead.

Finally we have the button itself inside of <ion-buttons>. This button uses a play icon and has a click

handler attached to it, which will call the playSlideshow() function in our home.js le (which we have

not created yet of course).

The next part of our template is the content section, which includes a list:

<ion-content>

<ion-list no-lines>

<ion-item-sliding>

<ion-item>
<img src="http://placehold.it/100x100" />
<ion-badge item-right light>0 days ago</ion-badge>
</ion-item>

<ion-item-options>
<button light (click)="removePhoto(photo)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

Before we get to the list, notice that everything is wrapped inside of <ion-content> - this is what holds

the main content for the page and in most cases everything except the navbar will be inside of here.

295
Much like a list is created with plain HTML, e.g:

<ul>
<li></li>
<li></li>
<li></li>
</ul>

A list in Ionic is created in basically the same way:

<ion-list>
<ion-item></ion-item>
<ion-item></ion-item>
<ion-item></ion-item>
</ion-list>

Of course, ours looks a little more complicated than that so lets talk through it. The rst thing out of

the ordinary we are doing is adding the no-lines attribute to the <ion-list>. Just like the danger

attribute we added to navbar, this attribute also styles our list except that it will cause the items in the list

not to display with borders.

The next bit gets a little trickier, as it is where we set up our sliding item. An <ion-sliding-item>, op-

posed to a normal <ion-item>, has two sets of content - the item itself, and then the <ion-item-options>

which will be revealed when the user slides the item.

The rst block of code inside of <ion-sliding-item> is the normal <ion-item> denition. Inside of

this item will be one of the photos the user has taken (eventually we will turn this into a loop for all photos),

but for now we are just using a placeholder image. We also add a badge that will be aligned to the right

of the item to display how many days ago the photo was taken. Were also just using dummy data for the

days ago label, but we will of course change that later.

Finally, we have the second block of code, <ion-item-options>, which simply allows us to dene what

content we want to display when the user slides the item. In this case we are just adding a Delete button

which will also pass in a reference to the photo it was called on. The photo reference it is trying to pass

296
now wont actually work (nor will the function because we havent created it yet), later on when we create

the loop for all photos we will discuss how to create this reference.

Now were going to add in that conditional photo button I mentioned earlier.

> Modify the content section of home.html to reect the following:

<ion-content>

<ion-list no-lines>

<button ion-item *ngIf="!photoTaken" detail-none (click)="takePhoto()">


<img src="images/smile.png" />
</button>

<ion-item-sliding>

<ion-item>
<img src="http://placehold.it/100x100" />
<ion-badge item-right light>0 days ago</ion-badge>
</ion-item>

<ion-item-options>
<button light (click)="removePhoto(photo)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

297
Notice now that we have included another item in our list now above the sliding item, but rather than

using <ion-item> we are using <button> with an ion-item directive. Visually, these two methods

are exactly the same, but on mobile everything that is not a <button> or <a> that has a click handler will

have a slight tap delay. We dont want to have this delay so we use the button instead.

We also add the detail-none attribute here since buttons come with some styling by default that we

dont want in this case. We of course also have a click handler set up to trigger the takePhoto function

that we will create later, but the most important thing here is the *ngIf directive.

*ngIf is one of the conditional directives provided by Angular 2, which allows you to change your template
based on some data. In the case of *ngIf it will display the element it is attached to, and everything that

element contains, only if the expression evaluates to be true. So since we have:

*ngIf="!photoTaken"

the extra button we added will only display if the photoTaken value is false, since we are essentially

saying display this button if the photoTaken variable is not true. Later on, we will create a photoTaken

variable in home.js which will keep track of whether a user has already taken a photo on the current day

or not.

If you take a look at the application now by running:

ionic serve

you should see something like this:

298
Obviously theres a lot left to do but thats all we need to do for now for the home page, so lets move on

to the slideshow page.

299
The Slideshow Page

As I mentioned, the slideshow page is going to be super simple. It is just going to be a modal that pops

up to display all of the users photos in a quick slideshow. To do this, all we need is a container for the

photos, a button to restart the slideshow and a button to close the page.

> Modify slideshow.html to reect the following

<ion-header>
<ion-toolbar danger>
<ion-title>Play</ion-title>

<ion-buttons start>
<button (click)="playPhotos()"><ion-icon
name="refresh"></ion-icon></button>
</ion-buttons>

<ion-buttons end>
<button (click)="closeModal()"><ion-icon
name="close"></ion-icon></button>
</ion-buttons>

</ion-toolbar>
</ion-header>

<ion-content>
<div class="image-container">
<img #imagePlayer id="imagePlayer" src = "" />
</div>
</ion-content>

You already know how the navbar and buttons work, so the only new thing here is the image player. All

300
were doing is adding an image element inside of the content area. Later on, we will add some code that

will cycle through all the photos the user has taken and change the src property to briey display each

photo. To do this we will need to grab a reference to the <img> element, so we create a local variable of

#imagePlayer that we will be able to grab from within our class denition later.

But for now, thats all we have to do - told you it was going to be simple.

Summary

We now have the templates set up for both of the pages in our application, so in the next lesson we can

start diving into some more interesting stu. In fact, it will probably be one of the most interesting lessons

because we will be learning how to integrate with the native Camera API and take photos!

301
Lesson 4: Taking Photos with the Camera

The primary goal of this lesson is to integrate the Camera to allow users to take photos, but we are going

to be doing quite a bit to make that work.

Of course we need to trigger the camera to take a photo, but we also need to:

Move the photo to permanent storage on the phone

Display the photo in our template

Display all the other photos

Allow the photo to be deleted

So this is going to be a pretty large lesson. In the getting started section for this application we set up Ionic

Native, which if you remember from the basics section is a service created by Ionic that wraps Cordova

plugins to make using them a bit easier.

Were going to be using Ionic Native in the lesson to use the Camera plugin, and the File API plugin.

Lets get started!

Creating a Photo Model

Before we start adding the photo functionality, we are going to create a model to represent our photo

object. When we create a photo we want to store a path to the where the photo is located, and the date

the photo was taken. Creating a data model will allow us to easily create photo objects, by doing this:

let photo = new PhotoModel('path/to/image', new Date());

rather than this:

let photo = {
image: 'path/to/image',
date: newDate
};

302
The dierence isnt that great, and its not at all necessary for our application to work, but it is a nice pattern

to follow, especially when your data starts getting more complex. For an example of a more complex data

model, and a more detailed explanation about why you might want to create one, check out the Quick

Lists lessons if you havent already.

> Modify photo-model.ts to reect the following:

export class PhotoModel {

constructor(public image: string, public date: Date){


this.image = image;
this.date = date;
}

This model is pretty straightforward, we just pass in the image and the date and set it in the constructor.

Now to make the model available in our home.ts le we will need to import it.

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {PhotoModel} from '../../providers/photo-model/photo-model';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

constructor(public nav: NavController) {

303
}

Creating a Simple Alert Service

Surprise! Ive got one more little thing for us to do before we get into the fun stu. Were going to be

triggering a lot of dierent alerts in this application because theres many dierent places where errors can

occur that the users needs to be notied about (taking the photo, moving the photo) and we will also be

alerting the user when things have gone well as well.

To create an alert, the syntax looks this:

let alert = this.alertCtrl.create({


title: "My Title",
message: "My Message",
buttons: [
{
text: 'Ok'
}
]
});

alert.present();

Which is quite a bit of code, and if we are going to be calling this multiple times in the same le its going

to get pretty messy. So were going to create another service, just like our data model, which will handle

creating the alert for us. Once we have created it, we will instead be able to create an alert by doing this:

let alert = this.simpleAlert.createAlert('Oops!', 'Something went wrong.');


alert.present();

> Modify simple-alert.ts to reect the following:

304
import {Injectable} from '@angular/core';
import {AlertController} from 'ionic-angular';

@Injectable()
export class SimpleAlert {

constructor(public alertCtrl: AlertController){

createAlert(title: string, message: string) {


return this.alertCtrl.create({
title: title,
message: message,
buttons: [
{
text: 'Ok'
}
]
});
}

Weve imported the AlertController service here so that we can create an alert, and then we have just

created a single function that takes in a title and a message, and returns an alert using those values. This

just creates the alert for us, we will still need to display it ourselves by presenting it wherever we need it.

To make this service available throughout our entire application we are going to declare the provider in our

root component.

305
> Modify app.ts to reect the following:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';
import {SimpleAlert} from './providers/simple-alert/simple-alert';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {
rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data, SimpleAlert]);

Weve both imported the SimpleAlert service here and weve added it to the providers array in the decorator

(as well as the Data service as, which we have done so that we can preview the application before nishing

implementing the data service). However, to use it in home.ts we will still need to import it there as well

(we just dont have to declare the provider again).

> Modify home.ts to reect the following:

import {Component} from '@angular/core';

306
import {NavController} from 'ionic-angular';
import {PhotoModel} from '../../providers/photo-model/photo-model';
import {SimpleAlert} from '../../providers/simple-alert/simple-alert';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

constructor(public nav: NavController, public simpleAlert: SimpleAlert) {

Notice that we are injecting SimpleAlert through our constructor just like we do with the NavController, but

we didnt with the photo model. This is because we are manually creating a new instance of PhotoModel

every time we use it, but with SimpleAlert we are just calling the same function from one instance over and

over again.

Taking a Photo with the Camera

Ok, weve got our photo model and alert service ready to use - we can nally focus on the fun stu which

is actually taking the photo. Well be using Ionic Native to use the Camera plugin, which means we will

need to rst import the Camera plugin.

> Add the following imports to home.ts

import {Camera} from 'ionic-native';


import {File} from 'ionic-native';

307
Now we can access the functionality through the Camera object that will be available in our component.

Im going to walk you through how to use it, but remember that you can nd the documentation for all

plugins available in Ionic Native here: http://ionicframework.com/docs/v2/native/Camera/

Before we add the code for taking a photo, we are going to add a few variables to our constructor and,

so we dont need to constantly modify our imports, import all of the things we will require for the rest of

the application. Were also going to be adding quite a few functions to this le, and some functions will

reference other functions. So that we dont run into any trouble with undened functions, were going to

add all of them now but just leave them blank. We will then step through implementing each function in

detail (not all in this lesson though).

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {NavController, ModalController, AlertController, Platform} from
'ionic-angular';
import {Camera} from 'ionic-native';
import {File} from 'ionic-native';
import {DaysAgo} from '../../pipes/days-ago';
import {Data} from '../../providers/data/data';
import {SimpleAlert} from '../../providers/simple-alert/simple-alert';
import {PhotoModel} from '../../providers/photo-model/photo-model';
import {SlideshowPage} from '../slideshow/slideshow';

declare var cordova;

@Component({
templateUrl: 'build/pages/home/home.html',
pipes: [DaysAgo]
})
export class HomePage {

308
loaded: boolean = false;
photoTaken: boolean = false;
photos: PhotoModel[] = [];

constructor(public nav: NavController, public dataService: Data, public


platform: Platform, public simpleAlert: SimpleAlert, public modalCtrl:
ModalController, public alertCtrl: AlertController) {

// Uncomment to use test data


/*this.photos = [
new PhotoModel('http://placehold.it/100x100', new Date()),
new PhotoModel('http://placehold.it/100x100', new Date()),
new PhotoModel('http://placehold.it/100x100', new Date())
]*/

this.loadPhotos();

document.addEventListener('resume', () => {

if(this.photos.length > 0){

let today = new Date();

if(this.photos[0].date.setHours(0,0,0,0) === today.setHours(0,0,0,0)){


this.photoTaken = true;
} else {
this.photoTaken = false;
}
}

309
}, false);

loadPhotos(): void {

takePhoto(): any {

createPhoto(photo): void {

removePhoto(photo): void {

playSlideshow(): void {

sharePhoto(image): void {

save(): void {

310
}

In the code above, weve added a member variable loaded to keep track of whether the photos have

been loaded from storage yet (which is something we will do in the next lesson), photoTaken to tell if a

photo has already been taken today and photos to hold all of our photo data. Since photos can only be

added when running on a real device, weve added some test data to the constructor which will set some

dummy data on this.photos so that you can test through the browser. If you need to do any testing

with photos present, just uncomment that section (make sure to remove it later though!).

We have injected and set up references to all the services we will require, including the data service we will

create later and the platform service.

Weve also added a weird resume listener here. The resume event will re whenever a person sends

your application to the background and then resumes using it again later. For example they have your

application open, switch to Facebook, and then back to yours. The reason were doing this is because

theres a bit of an edge case with our photoTaken variable. Imagine someone has taken their photo for

the day, but when they close the application they dont fully close it, it just goes to the background. When

they come back the next day the logic we will run later in our loadPhotos function wont run to determine

if the photo was taken or not, because the application is just being resumed not reloaded. So whenever

the application is resumed, we check if the date of the last photo taken is equal to todays date, and then

set the photoTaken variable according to that.

Now were going to implement the takePhoto function which will handle taking the photo, so lets create

a basic version of that now.

NOTE: We have added declare var cordova here so that TypeScript does not complain about not

knowing what it is when we are developing through the browser (since cordova will only be available when

running on a real device). It does not do anything apart from that.

> Modify the takePhoto function to reect the following:

311
takePhoto(): any {

if(!this.loaded || this.photoTaken){
return false;
}

if(!this.platform.is('cordova')){
console.log("You can only take photos on a device!");
return false;
}

let options = {
quality: 100,
destinationType: 1, //return a path to the image on the device
sourceType: 1, //use the camera to grab the image
encodingType: 0, //return the image in jpeg format
cameraDirection: 1, //front facing camera
saveToPhotoAlbum: true //save a copy to the users photo album as well
};

Camera.getPicture(options).then(

(imagePath) => {
console.log(imagePath);
},

(err) => {
let alert = this.simpleAlert.createAlert('Oops!', 'Something went
wrong.');
alert.present();

312
}
);

Before we execute the Camera code, we check a few conditions rst. If the variables we just recently

created indicate that the data has not nished loading yet, or that a photo has already been taken, then

the rest of the function will not nish executing. We also check to see if we are running on the cordova

platform, i.e. on a real device, and if we are not then it will also exit the function. Since you can only access

this plugin on a real device, it will just throw errors when it is not running on a device.

Then we set up some options to pass to the Camera plugin, these congure what exactly we want to do

and what we want returned. These values can congure things like whether to use the camera or the users

photo library, the format to return the image in, whether to use the back facing or front facing camera and

so on. Ive added comments next to each option in the code to indicate what they do.

Once weve created that options object, we call the getPicture method on the Camera object and pass

in the options. This will return a promise which, when resolved, will give us a path to the image on the

users device. Right now we are just logging out that value but were about to do a lot more with it. If an

error value is returned then we use our SimpleAlert service to display a message to the user.

The image that is taken with the camera, and the path that is returned, is in a temporary storage folder. So

the image will display for a little while but if we want to keep the photo around for a while its not going to

work, because eventually it will just be deleted. To solve this issue, we are going to have to take that photo

and move it somewhere else, and then we are going to store a reference to that location for the photo.

Were going to do this with the File plugin.

Moving the Photo to Permanent Storage

In order to use the File API plugin, were going to modify the takePhoto function to do the following:

Use the imagePath returned to nd the photo in temporary storage

313
Rename and move it to our own snapaday folder on the device (which will be in permanent storage)

Create a new photo object in the application based on the new location of the photo

So the takePhoto function is about to get a whole lot more complicated. Since there is so much code

in this function, I will add detailed comments directly into the code, but I will also discuss it afterward as

well.

> Modify the getPicture call in the takePhoto function to reect the following:

Camera.getPicture(options).then(

(imagePath) => {

//Grab the file name


let currentName = imagePath.replace(/^.*[\\\/]/, '');

//Create a new file name


let d = new Date(),
n = d.getTime(),
newFileName = n + ".jpg";

if(this.platform.is('ios')){

//Move the file to permanent storage


File.moveFile(cordova.file.tempDirectory, currentName,
cordova.file.dataDirectory, newFileName).then((success) => {

this.photoTaken = true;
this.createPhoto(success.nativeURL);
this.sharePhoto(success.nativeURL);

}, (err) => {

314
console.log(err);
let alert = this.simpleAlert.createAlert('Oops!', 'Something
went wrong.');
alert.present();

});

} else {
this.photoTaken = true;
this.createPhoto(imagePath);
this.sharePhoto(imagePath);
}

},

(err) => {
let alert = this.simpleAlert.createAlert('Oops!', 'Something went
wrong.');
alert.present();
}
);

Hopefully the comments in the code above make it reasonably easy to follow whats going on, but heres

a high level step-by-step of whats going on after the imagePath is returned from the camera:

1. Work out the current le name by removing everything before the last / in the imagePath

2. Create a new unique le name using the date so that we do not overwrite anything

3. If we are running on iOS then we move the photo out of the temp directory and into a permanent

directory.

4. Set the photoTaken variable to true, and pass the new path to the image to the createPhoto

315
and sharePhoto functions

After all of this we should have the photo stored in a permanent location, along with a path to the image

in its new location. Keep in mind that we havent actually created the createPhoto and sharePhoto

functions yet. Were going to create the createPhoto function now, but we will save the sharePhoto

function for a later lesson when we integrate the social sharing plugin.

The createPhoto function will take the path (the nativeURL) and keep a reference to it in the application.

> Modify the createPhoto function to reect the following:

createPhoto(photo): void {
let newPhoto = new PhotoModel(photo, new Date());
this.photos.unshift(newPhoto);
this.save();
}

As you can see its pretty simple. We pass in the path to the image on the device and the current date to

our Photo Model to create a new photo object, and then we add it to our this.photos array. Rather

than using the push method to add it to the array (which adds it to the end) we add it to the start of the

array with unshift. We do this because we want to display the photos in reverse order (most recent rst)

and doing it this way makes our lives easier later.

We also have a call to the save function, which will save our data into storage but we havent implemented

that functionality yet.

Updating the Template

We have photos being added to our this.photos array now, so now we can update our template to

loop through and display all of them. Since youre likely testing through the browser and cant add photos

through the camera, feel free to uncomment the test data in the constructor so that you can see the results

of the code in this section.

316
Lets update the template now.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar danger>
<ion-title>
<img src = "images/logo.png" class="logo" />
</ion-title>

<ion-buttons end>
<button (click)="playSlideshow()"><ion-icon
name="play"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content>

<ion-list no-lines>

<button ion-item detail-none *ngIf="!photoTaken" (click)="takePhoto()">


<img src="images/smile.png" />
</button>

<ion-item-sliding *ngFor="let photo of photos">

<ion-item>
<img [src]="photo.image" />
<ion-badge item-right light>0 days ago</ion-badge>

317
</ion-item>

<ion-item-options>
<button light (click)="removePhoto(photo)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

</ion-list>

</ion-content>

Theres two main additions to the template here. First we are using *ngFor to loop through our array of

photos and create an <ion-item-sliding> entry for all of them. Using let photo in let photo

of photos allows us to reference the specic photo that is being rendered in the loop by using photo,
e.g: photo.image to access the image path stored on the photo.

Since we can access the path of each photo, we can now also set the <img> element to display it. No-

tice that we use square brackets around [src], this allows us to set the src property to the expression

photo.image on the image element. This means that photo.image will rst be evaluated to whatever
value it stores (i.e. /path/to/image), and then set as the src. If we didnt use the square brackets, then

the src would literally be set to the string photo.image.

We also have a delete button here that passes a reference to the photo (which we created by using let

photo) through to the removePhoto function. Lets implement that function now so that the photo is
removed when the delete button is clicked.

> Modify the removePhoto function in home.ts to reect the following:

removePhoto(photo): void {

let today = new Date();

318
if(photo.date.setHours(0,0,0,0) === today.setHours(0,0,0,0)){
this.photoTaken = false;
}

let index = this.photos.indexOf(photo);

if(index > -1){


this.photos.splice(index, 1);
this.save();
}

The second part of this function is straight forward enough, we nd the photo in our photos array, remove

it, and trigger a save. The rst bit is a little trickier though. The issue we have is that if a user takes a photo,

and then deletes the photo they took, they should be able to take another photo (since now they dont

have a photo for that day). But if they delete an older photo, then they shouldnt get to take another photo.

So what we do is grab the date of the photo that was deleted, and compare it to todays date. If the photo

being deleted was taken today, then we set this.photoTaken to false to allow the user to take another

photo.

Summary

What a lesson! There was some pretty complex stu covered in this lesson but weve implemented the

core functionality of the application now. Theres still quite a bit left to do, but we can take photos with the

camera and see them displayed in a list which is pretty cool. To give it a try for yourself youll need to run

the application on a real device. If you dont know how to do that already, you can skip to the Testing and

Debugging section of this book to see how to get the app installed on your device, and then come back

319
to nish the rest of the application.

IMPORTANT: If you want to test taking a photo on a real device now, you will have to update the

loadPhotos function to reect the following:

loadPhotos(): void {
this.loaded = true;
}

You cant take a photo until the data has nished loading, so we can just fake that for now. In the next

lesson, well be looking at how to create a data service to store our photos data permanently, so that it

is available when the user comes back to the application the next time.

320
Lesson 5: Saving and Loading Photos

We can now take photos in the application and display those photos in a list, we can even delete them as

well. The problem is that the data stored in this.photos will be lost as soon as the application is closed.

Obviously the user isnt going to be very happy when they nd their sele missing the next time they try

to use the application, so in this lesson we are going to learn how to create a data storage service to both

save data to permanent storage, and to load it into the application.

Weve already got a lot of this process set up, weve already generated a Data provider which we have

imported into our home.ts le, and we even have a save function created that we are calling whenever

we want data saved. So all we really have to do is:

Implement the save function so that calls the data service

Modify the empty Data provider to provide save and load functionality

Load the photo data into the application when the application is opened

Although we have already imported the Data provider in home.ts, we still need to declare it as a provider.

We can do this in the @Page decorator for home.ts, but its often useful to declare a data service like this

in the root component so that you can more easily use it in other components as well.

> Modify app.ts to reect the following:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';
import {SimpleAlert} from './providers/simple-alert/simple-alert';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {

321
rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data, SimpleAlert]);

We now have providers set up for both our SimpleAlert service and our Data service.

Implementing the Data Service

Were going to add the code for data.ts now so that it will save into storage any data that it is passed. The

code for this service is actually surprisingly simple, so lets take a look at it rst and then talk through it.

> Modify data.ts to reect the following:

import {Storage, SqlStorage} from 'ionic-angular';


import {Injectable} from '@angular/core';

@Injectable()
export class Data {

storage: Storage;

constructor(){
this.storage = new Storage(SqlStorage, {name:'photos'});
}

322
getData(): Promise<any> {
return this.storage.get('photos');
}

save(data): void {
let newData = JSON.stringify(data);
this.storage.set('photos', newData);
}

Theres a couple imports at the top of this code block that you may not be familiar with, so lets take a look

at those:

import {Storage, SqlStorage} from 'ionic-angular';

Storage is Ionics generic storage service, and SqlStorage is used to dene the type of storage we want

to use. In the case of SqlStorage that means we will be storing data using a native SQLite database

(remember how we added the SQLite plugin before?). The SQLite database will only be available when

running natively on a device, so SqlStorage also uses WebSQL which is browser based to store data if

the SQLite database is not available.

Alternatively, you could use the LocalStorage service instead of **SqlStorage* which just stores data in

the browsers local storage memory, rather than hooking into the SQLite plugin. In general this is a bad idea

because the browser based local storage is not completely reliable and can potentially be wiped by the

operating system. Having your data wiped randomly is obviously not ideal, so you should use SqlStorage

whenever possible, which does not have this issue (it also allows you to store more data).

Lets take a look at what were doing in our constructor:

constructor(){
this.storage = new Storage(SqlStorage, {name:'photos'});

323
}

What were doing here is creating a reference to our database by instantiating a new instance of Storage.

We provide the storage type we want to use, and provide the name of our database. If the database does

not already exist, it will be created.

Now lets take a look at the getData function:

getData(): Promise<any> {
return this.storage.get('photos');
}

This function will allow us to retrieve the latest data that has been stored. The get method of our storage

will return a promise, but notice that we are not setting up the handler for when the promise nishes here,

instead we just return the result of the get method directly. This allows us to set up the handler from

wherever in the code this method is being called, which makes more sense for the ow of the application

(hopefully this will be made more clear shortly).

Then we have our save function, which handles actually saving the data into storage:

save(data): void {
let newData = JSON.stringify(data);
this.storage.set('photos', newData);
}

We are storing all the data as a single JSON encoded string, so we rst call the JSON.stringify function

and then store the data using the set method on our storage object.

Thats all there is to saving the data, which isnt really all that complex. Now we just need to handle loading

that data back into the application. Were going to do this by dening the loadPhotos function.

> Modify the loadPhotos function in home.ts to reect the following:

loadPhotos(): void {

324
this.dataService.getData().then((photos) => {

let savedPhotos: any = false;

if(typeof(photos) != "undefined"){
savedPhotos = JSON.parse(photos);
}

if(savedPhotos){

savedPhotos.forEach(savedPhoto => {
this.photos.push(new PhotoModel(savedPhoto.image, new
Date(savedPhoto.date)));
});

if(this.photos.length > 0){

let today = new Date();

if(this.photos[0].date.setHours(0,0,0,0) === today.setHours(0,0,0,0)){


this.photoTaken = true;
}
}

this.loaded = true;

});
}

325
The rst thing we are doing here is calling the getData() function we just implemented in our data service.

This will return all of the photo data as a JSON encoded string, so the rst thing we do is decode it into an

object we can work with, or an empty array if no photos are returned.

If there are photos loaded from storage, we use the data to recreate each photo using our photo model.

Then, once again, we do a little trickery to determine whether or not the user is allowed to take a photo

today. We simply check the date of the photo in the rst position and compare it to the current date, if they

match then we set the photoTaken variable to true.

Once all this has nished executing, we set the this.loaded ag to true to indicate that the photos are

loaded and ready to go. Now all we have left to do is make our save function actually make a call to the

data service.

Now lets move on to dening the save function.

> Modify the save function in home.ts to reect the following:

save(): void {
this.dataService.save(this.photos);
}

Now whenever the save function is called it will pass in all the current values in this.photos and save

them into storage.

Summary

That lesson was certainly a lot shorter, so hopefully you enjoyed a bit of a break after the last one. Saving

and loading data sounds like a pretty complex topic, but as you can see storing simple data is actually

quite easy.

In the next lesson were going to get back to doing something a little more fun, were going to nish of the

Slideshow page so that it shows a slideshow of all of the users photos, as well as implementing our own

custom Days Ago pipe.

326
Lesson 6: Creating a Custom Pipe and Flipbook of all Photos

In this lesson we will be creating our own custom pipe to display the dates photos were taken in a more

user friendly way, and we will also be nishing o a critical portion of our application which is the slideshow.

It wont be quite as complex as actually taking the photo was, but the slideshow is pretty much the point

of the whole application.

Creating a Custom Pipe

Weve already covered what a pipe is in the basics section of this book, but to refresh your memory a pipe

essentially allows us to modify some data before displaying it to the user.

The goal for us here is to create a label that displays how many days ago a photo was taken, e.g. 3 days

ago, 10 days ago. Right now, the only data we have stored on our photo that we can use is a date

object, unfortunately for us that looks something like this:

Sun Mar 06 2016 00:40:02 GMT+1000 (AEST)

which isnt the most user friendly format in the world. So a @Pipe is a perfect use case for this: we create

a pipe that takes in our ugly date format, converts it into the X days ago format, and then returns it.

Lets take a look at how to create the pipe rst, and then well go over how to use it.

> Modify days-ago.ts to reect the following:

import {Injectable, Pipe} from '@angular/core';

@Pipe({
name: 'daysAgo'
})
@Injectable()
export class DaysAgo {
transform(value, args) {

327
let now = new Date();
let oneDay = 24 * 60 * 60 * 1000;
let diffDays = Math.round(Math.abs((value.getTime() -
now.getTime())/(oneDay)));

return diffDays;
}
}

In the @Pipe decorator we supply a name of daysAgo, this means that this pipe can be used by using

daysAgo as the keyword in the template. When using a pipe, you always pass data into it, and this data
gets sent to the transform function and is passed in as the value. We will be passing the Date object

from our photo into this pipe, so thats what we will be working with here.

First we do a little mathematics magic to work out the dierence in days between the current date and the

date the photo was taken (Stack Overow gets the credit for this one, my solution would have been way

uglier than this), and then we return it. Whatever value is returned is what will actually be rendered out to

the user in the template.

If you recall from previous lessons, weve already imported this pipe into home.ts and weve declared it

as a pipe in the decorator for home.ts, so the only thing we need to do to use it is add it to the template.

> Modify the photo item in home.html to reect the following:

<ion-item>
<img [src]="photo.image" />
<ion-badge item-right light>{{photo.date | daysAgo}} days
ago</ion-badge>
</ion-item>

Notice that we have added the following:

328
{{photo.date | daysAgo}}

This will pass photo.date into the daysAgo pipe, and then whatever daysAgo returns will be displayed

to the user here. The end result will be a badge containing something like 5 days ago.

Creating a Slideshow of All Photos

Weve already created the template for the Slideshow, so now we need to create some logic so that all of

the photos are cycled through and displayed, and we will also need to add a way to open the slideshow

page (as well as restart the slideshow).

Lets start o by dening the playSlideshow function so that we can actually open the page.

> Modify the playSlideshow function in home.ts to reect the following:

playSlideshow(): void {

if(this.photos.length > 1){


let modal = this.modalCtrl.create(SlideshowPage, {photos:
this.photos});
modal.present();
} else {
let alert = this.simpleAlert.createAlert('Oops!', 'You need at least
two photos before you can play a slideshow.');
alert.present();
}

We display a modal page to users very similarly to how we create alerts. First we create a Modal, and then

we present it using the NavController. In this case we create a Modal using the SlideshowPage that we

have already created, and we also pass in a data object with it that contains all of the photos. This will

329
allow us to grab that data from the Slideshow Page.

Notice that we only trigger the modal if the user has more than 1 photo (because a slideshow with 0 or 1

photos isnt really a slideshow, right?), and if they dont an alert is displayed.

Now we are going to dene the class for the Slideshow Page. The class is reasonably small so Im going

to post all of the code in one block and then we will step through it afterwards.

> Modify slideshow.ts to reect the following:

import {NavController, NavParams, ViewController} from 'ionic-angular';


import {Component, ElementRef, ViewChild} from '@angular/core';

@Component({
templateUrl: 'build/pages/slideshow/slideshow.html',
})
export class SlideshowPage {

@ViewChild('imagePlayer') imagePlayer: ElementRef;

imagePlayerInterval: any;
photos: any;

constructor(public nav: NavController, public navParams: NavParams, public


viewCtrl: ViewController) {
this.photos = this.navParams.get('photos');
}

ngAfterViewInit(){
this.playPhotos();
}

330
closeModal(){
this.viewCtrl.dismiss();
}

playPhotos(){

let imagePlayer = this.imagePlayer.nativeElement;


let i = 0;

//Clear any interval already set


clearInterval(this.imagePlayerInterval);

//Restart
this.imagePlayerInterval = setInterval(() => {
if(i < this.photos.length){
imagePlayer.src = this.photos[i].image;
i++;
}
else {
clearInterval(this.imagePlayerInterval);
}
}, 500);
}

Theres a couple things here you may not be familiar with. Were importing the NavParams service here

which will allow us to grab the photo data that we added when creating the modal. You can see we grab

this in the constructor by using this.navParams.get('photos');. The other weird thing here is the

use of ViewController. We need to include this so that we can dismiss the modal (weve dened the

331
closeModal function here as well which is called by a button in our template).

The most important function here is the playPhotos function, this is what will loop through all of the

photos and change the image element on our page accordingly. First we grab a reference to the image

element by using the @ViewChild reference we set up before (which allows us to grab any element from

the template using a #localvariable attached to it), we clear any intervals that are currently running

(in case a user restarts the slideshow when it is still currently playing) and then we start looping through

all of the photos in this.photos. If there are still photos left to display, the image elements src property

is updated with the next photo, and if there isnt photos left to display then the interval is cleared. In the

code above the interval is set to run every 500ms or every 0.5 seconds, so you could adjust this to make

the slideshow slower or faster if you want (or perhaps even add an option for the user to control that).

The playPhotos function is triggered automatically by the use of the ngAfterViewInit function, which

automatically executes after the view is intialised. The reason we trigger the call here and not in the

constructor is because if we call it from the constructor the DOM might not be ready yet, and when we try

to get the element by its ID, it may not even exist yet.

Summary

This was another reasonably simple lesson, but we covered some cool stu here. Most of the main func-

tionality for the application has been implemented, now we just need to add the bells and whistles. We

will of course make it look a lot prettier, but in the next lesson we are going to look at incorporating local

notications and social sharing.

332
Lesson 7: Integrating Local Notications & Social Sharing

Weve nished creating a lot of the main functionality in the application: we can take photos and have them

displayed in a list, delete photos, load photos from storage, play a slideshow and so on. The features we

will be adding in this lesson are nice-to-haves that improve the user experience.

We will be adding local notications that will send a reminder to the user once a day to take their photo for

the day, and we will enable social sharing so that the user can share any photos they take with their friends.

Weve already installed both of these plugins in the getting started section so we just need to implement

the functionality - neither of these plugins are supported in Ionic Native at the time of writing this so we will

just be using the plugins directly like we did with the File API (fortunately, these plugins are much simpler!)

Lets jump right in.

Local Notications

Before we implement the local notications, it might be worth highlighting the dierence between push

notications and local notications. They both look and behave very similar, they allow you to notify the

user of things whether they currently have your application open or not and look like this:

333
The dierence is that push notications require an external server that handles pushing a notication out

to the device, whereas local notications are completely handled on the device itself. This means local

notications are great for things like alarms, scheduled notications, or in the case of our application a daily

reminder to take a photo. Push notications are better for being notied of things that happen outside the

users device, like someone sending them a message on Facebook.

To implement this functionality we are just going to add a little bit of code to our root component.

> Modify app.ts to reect the following:

import {Component} from "@angular/core";

334
import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';
import {SimpleAlert} from './providers/simple-alert/simple-alert';
import {LocalNotifications} from 'ionic-native';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {
rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {

if(platform.is('cordova')){

LocalNotifications.isScheduled(1).then( (scheduled) => {

if(!scheduled){

let firstNotificationTime = new Date();


firstNotificationTime.setHours(firstNotificationTime.getHours()+24);

LocalNotifications.schedule({
id: 1,
title: 'Snapaday',
text: 'Have you taken your snap today?',
at: firstNotificationTime,

335
every: 'day'
});

});

});
}
}

ionicBootstrap(MyApp, [Data, SimpleAlert]);

Since this constructor is one of the rst things that runs when we open the application, we put the call to

this plugin inside of the handler for platform.ready() to make sure that the plugin is ready to be used.

We then also check if we are running on the cordova platform like we did with the Camera, because this

plugin will only work when running on a device.

Then we get into the using the plugin itself, which we can access through LocalNotifications from

Ionic Native. We rst check if a notication with the id of 1 is already scheduled, if it is we do nothing, but

if its not then we create a new notication using the schedule method.

We simply supply a title and and a message that we want to display on the notication, and we also supply

an id so that we can identify the notication later (like we just did when checking to see if it was already

scheduled). We need to describe when to display the notication as well, so we create a time that is 24

hours from now (so that the rst notication displays at the same time tomorrow) and set the frequency to

every day, so that it will continue to show every day (there are other options you can use like every week

etc.).

Thats all there is to using this plugin, or at least thats all we need to do with it, there are a lot more

336
options so make sure to check out the documentation for the plugin if youre looking to do something

more complex.

Social Sharing

Now were going to add the social sharing plugin. This is a great plugin for sharing little bits of data across

many dierent social media networks and even things like SMS and email. It can be quite a bit of hassle

integrating with various dierent APIs manually (e.g. Facebook, Twitter etc.) and this plugin simplies that

immensely.

You can either share something to a specic platform, or you can trigger a generic share pop up and

the user can share to wherever they want. To implement this functionality, we are going to dene our

sharePhoto function which is already being called by our takePhoto function.

> Add the following import to the top of home.ts:

import {SocialSharing} from 'ionic-native';

> Modify the sharePhoto function in home.ts to reect the following:

sharePhoto(image): void {

let alert = this.alertCtrl.create({


title: 'Nice one!',
message: 'You\'ve taken your photo for today, would you also like to
share it?',
buttons: [
{
text: 'No, Thanks'
},
{
text: 'Share',

337
handler: () => {
SocialSharing.share('I\'m taking a selfie every day with
#Snapaday', null, image, null)
}
}
]
});

alert.present();

Weve created a few alerts before and each time we have used the Simple Alert service that we created.

Were not doing that here though because were creating a more complex alert. This one is going to have

two buttons, and one of those buttons will trigger a share using the social sharing plugin (the other will just

close the alert).

As you can see in the handler for the Share button, all we do is call the share method through the plugin

(which is accessible through window.plugins.socialsharing) and supply a message and the image.

Theres a bunch of dierent congurations you can use with this plugin so again, make sure to check out

the documentation for it.

Now when the user clicks Share they will get a pop up like in the image above, and will be able to choose

which platform they want to share to.

Summary

Both of the features we added to this application will help with growth and user engagement in the appli-

cation. Even if someone wanted to take a sele every day through the application, they would likely forget

without some kind of reminder. The ability for them to easily share their photos to social media will also

help spread the word about the application and hopefully drive more downloads.

338
The only thing we have left to do now is style our application so that it doesnt look so ugly, we will be

doing that in the next lesson.

339
Lesson 8: Styling

Were almost there, the application is basically completed now, but after this lesson the functionality will

be completed and it will look pretty too.

If you remember from the basics section, theres quite a few dierent ways we can add styles to the

application. We will be basically using all of these methods. We will be adding specic styles to each of

our components, we will be adding some generic styles in our core le, and we will be overriding some

SASS variables in the variables le. If you skipped over that part or are not entirely sure what Im talking

about here, Id recommend going back and reading about theming in the basics sections.

Lets rst add some generic styles to the application by modifying the core .scss le and the variables le.

> Add the following style to app.core.scss:

.logo {
max-height: 39px;
}

Were keeping this pretty simple here, all were doing is setting a max height for the logo so that it displays

nicely in the navbar. Next well do something a little more interesting by adding in some custom SASS

variables.

> Add the following to app.variables.scss

$colors: (
primary: #387ef5,
secondary: #32db64,
danger: #fa2d18,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

340
$item-ios-padding-left: 0;
$item-md-padding-left: 0;
$item-ios-padding-right: 0;
$item-md-padding-right: 0;
$item-ios-padding-top: 0;
$item-md-padding-top: 0;
$item-ios-padding-bottom: 0;
$item-md-padding-bottom: 0;

$list-ios-margin-bottom: 0px;
$list-ios-margin-top: 0px;
$list-md-margin-bottom: 0px;
$list-md-margin-top: 0px;

First, weve redened the default colors for the application which we are making use of in our templates.

Remember that these values can be added to the elements in our templates like this: <ion-navbar

danger *navbar>

Then we override a bunch the default SASS variables, which is basically just removing padding and margins

from the list and list item elements so that our photos take up all the available space.

Now lets move on to the component specic styles. We are going to be modifying our templates slightly

to include some custom classes, and dene those classes in the .scss le for each component. Lets

start with our home page.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar danger>
<ion-title>
<img src = "images/logo.png" class="logo" />
</ion-title>

341
<ion-buttons end>
<button (click)="playSlideshow()"><ion-icon
name="play"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content>

<ion-list no-lines>

<button ion-item detail-none *ngIf="!photoTaken" (click)="takePhoto()">


<img src="images/smile.png" class="list-photo" />
</button>

<ion-item-sliding *ngFor="let photo of photos">

<ion-item>
<img [src]="photo.image" class="list-photo" />
<ion-badge item-right light class="date-badge">{{photo.date |
daysAgo}} days ago</ion-badge>
</ion-item>

<ion-item-options>
<button light (click)="removePhoto(photo)"><ion-icon
name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>

342
</ion-list>

</ion-content>

> Modify home.scss to reect the following:

.list-photo {
width: 100%;
height: auto;
}

ion-content {
background-color: #222222;
}

.item {
border: none !important;
background-color: #222222;
}

.date-badge {
position: absolute;
right: 10px;
}

Again, theres nothing too exciting here. Were making sure our photo takes up all the available width,

setting a dark background colour on the content area and on the items themselves, and using some

absolute positioning to get our days ago badge to display where we want it.

Now lets take a look at the slideshow page.

343
> Modify slideshow.html to reect the following:

<ion-header>
<ion-toolbar danger>
<ion-title>Play</ion-title>

<ion-buttons start>
<button (click)="playPhotos()"><ion-icon
name="refresh"></ion-icon></button>
</ion-buttons>

<ion-buttons end>
<button (click)="closeModal()"><ion-icon
name="close"></ion-icon></button>
</ion-buttons>

</ion-toolbar>
</ion-header>

<ion-content>
<div class="image-container">
<img #imagePlayer id="imagePlayer" src = "" />
</div>
</ion-content>

> Modify slideshow.scss to reect the following:

.image-container {
position: absolute;
top: 0;
bottom: 0;
left: 0;

344
right: 0;
display: flex;
justify-content: center;
align-items: center;
}

#imagePlayer {
width: 100%;
height: auto;
vertical-align: middle;
}

The only styling were adding to this page is to get the photos to display in the center, both horizontally

and vertically (which isnt always the easiest task!). Weve done a bit of trickery with exbox to get that to

happen. Flexbox is a little more advanced, but if youre looking for a bit of an intro to it then take a look at

this tutorial.

If you take a look at your application now, it should look like this:

345
As you can see, we havent even added that much styling to the application but it looks a whole lot better

now.

346
Conclusion

Congratulations on making it through the Giist tutorial. Weve learned a lot through developing this appli-

cation, but the main take aways are:

How to integrate native plugins

How to use the Camera API

How to use the File API

How to use local notications

How to use modals

How to create a custom provider

How to store data permanently

Theres always room to take things further though, especially when youre trying to learn something. Fol-

lowing tutorials is great, but its even better when you gure something out for yourself. Hopefully you

have enough background knowledge now to start trying to extend the functionality of the application by

yourself, heres a few ideas to try out:

Create a dierent theme for the application [EASY]

Modify the Days Ago pipe to display today and 1 day ago instead of 0 days ago and 1 days

ago [MEDIUM]

Add settings to allow the user to control the playback speed of the slideshow [HARD]

Allow the user to congure whether or not they want daily notications and what time they want to

receive them [HARD]

Remember, the Ionic 2 documentation is your best friend when trying to gure things out.

What next?

You have a completed application now, but thats not the end of the story. You also need to get it running

on a real device and submitted to app stores, which is no easy task. The nal sections in this book will

347
walk through how to take what you have done here, and get it onto the app stores so make sure to give

that a read.

348
Chapter 6

Camper Mate

349
Lesson 1: Introduction

Camper Mate is an interesting application to build because it doesnt really have one specic purpose like

the other applications have had, its a bit of a utility toolbelt kind of app. It provides a bunch of dierent

features to users that might be useful for them when going on caravan or camping trips.

This gives us a chance to play around with some things that we havent been able to in the other applica-

tions, and it also presents some interesting challenges - one important example being that we will have

lots of dierent bits of data we need to save, rather than just one set of data, so our data service is going

to be more complex than the ones we have been creating in other applications.

The two most important concepts well be covering in this application are the integration and use of Google

Maps and the use of forms for capturing user input. Also, the rst application in this book, Quicklists, would

make an excellent addition to this application, so were even going to look at how can add the entire Quick

Lists application to a single tab in Camper Mate (its surprisingly easy).

The exact features in this application will be:

A map that the user can set their camp location on. When they have left their camp site they will be

able to click a button which will then show them how to get back to their camp site.

A form that will allow the user to store personal details relevant to camping (car and trailer registration

etc.)

A form that will allow the user to store details about the camp they are staying at (access codes,

WiFi password etc.)

The ability to create, modify and delete custom checklists (the functionality provided by Quick Lists)

and some of the main concepts we will be covering are:

Creating forms and capturing user data

Implementing Google Maps and creating a custom provider to handle that

Creating a tabs layout

Saving and retrieving multiple sets of data

Reusing components from another application

350
Heres a few screenshots to give you an idea of what it will look like in the end:

351
###Lesson Structure

352
1. Getting Ready

2. Creating a Tabs Layout

3. User Input and Forms

4. Implementing Google Maps and Geolocation

5. Saving and Retrieving Data

6. Reusing Components

7. Styling

Ready?

Now that you know what youre in for, lets get to building it!

353
Lesson 2: Getting Ready

In this lesson we are going to prepare our application for the journey ahead. We are going to of course

generate the application, and we are also going to set up all of the components and Cordova plugins we

will need. At the end of this rst lesson we should have a nice skeleton application set up with everything

we need to start diving into coding.

A good rule of thumb before starting any new application is to make sure you have the latest version of

Ionic and Cordova, so if you havent done it recently then make sure to run:

npm install -g ionic@beta cordova

or

sudo npm install -g ionic@beta cordova

before you continue.

Generate a new application

We will be using the blank starter template for this application which, as the name implies, is basically

an empty Ionic project. It comes with one page built in called home which we will repurpose in the next

lesson.

> Run the following command to generate a new application

ionic start campermate blank --v2

> Make the new project your current working directory by running the following command:

cd campermate

Your project should now be generated - now you can open up the project folder in your favourite editor.

You can take a look at how your application looks by running the following command:

354
ionic serve

which for now should look something like this:

355
###Create the Required Components

Were going to be creating a few pages for this application, we are going to reuse the automatically gener-

ated home page to create our tab layout (which will allow us to switch between pages), but we still need

to add pages for the location, my details and camp details tab.

> Run the following command to generate the Location page:

ionic g page Location

> Run the following command to generate the My Details page:

ionic g page MyDetails

> Run the following command to generate the Camp Details page:

ionic g page CampDetails

Also remember that any time we generate a new component, we need to import its .scss le into our

app.core.scss le. The Ionic CLI automatically generates pages for us, but it doesnt automatically import

the styles in app.core.scss.

> Modify app.core.scss to import the components styles:

@import "../pages/home/home";
@import "../pages/location/location";
@import "../pages/my-details/my-details";
@import "../pages/camp-details/camp-details";

Create the Required Services

As well as our tab pages, we are also going to create a few services. We will create a Data service for saving

and retrieving data, a Google Maps service to handle the Google Maps integration, and a Connectivity

service so that we can easily check if the user is online or oine.

356
> Run the following command to generate a Data provider:

ionic g provider Data

> Run the following command to generate a Google Maps provider:

ionic g provider GoogleMaps

> Run the following command to generate a Connectivity provider:

ionic g provider Connectivity

Add Required Platforms

Before you can build for certain platforms, you need to add them to your project. This is something we

will be doing way later on in this course, but you may as well just set them up now.

> Run the following command to add the iOS platform to your application

ionic platform add ios

> Run the following command to add the Android platform to your application

ionic platform add android

Add Required Cordova Plugins

This application will use a few dierent Cordova plugins. Remember, Cordova plugins can only be used

when running on a real device. Ill explain each plugin as we run through the commands for adding them.

> Run the following command to add the Geolocation plugin:

ionic plugin add cordova-plugin-geolocation

357
The Geolocation plugin will allow us to grab the users current location, and it also provides other methods

which provide the ability to do things like track the users location over time.

> Run the following command to add the Network Information plugin:

ionic plugin add cordova-plugin-network-information

This plugin will give us some information about the network the user is on, such as the type of connection

they have. This will allow us to more accurately determine if the user currently has an active Internet

connection or not.

> Run the following command to add the SQLite plugin:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

This plugin gives you access to native storage with an SQLite database. We are adding it to this application

because the Ionic local storage service can make use of this plugin to provide more stable data storage.

> Run the following command to add the In App Browser plugin:

ionic plugin add cordova-plugin-inappbrowser

This plugin makes a webview available to us that we can launch external websites in.

> Run the following command to add the Status Bar plugin:

ionic plugin add cordova-plugin-statusbar

We will be adding this plugin to all projects to give us control over the status bar in our application (the bar

at the top of the devices screen that contains the time, battery information and so on).

> Run the following command to add the Splash Screen plugin:

ionic plugin add cordova-plugin-splashscreen

This plugin allows us to control the splash screen (the fullscreen graphic that briey displays when you

open an app)

358
> Run the following command to add the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

This plugin is required for all applications, and helps to dene what resources should be allowed to be

loaded in your application. Without it, resources you try to load will fail.

As well as adding the plugin, you also need to dene a Content Security Policy in your index.html

le. We will be adding a very permissive policy which will essentially allow us to load any resources.

Depending on your application, you may look into providing a more strict policy, but an open policy is

good for development.

> Modify your www/index.html le to include the following meta tag:

<meta http-equiv="Content-Security-Policy" content="font-src * data:; img-src


* data:; default-src * 'unsafe-eval' 'unsafe-inline'">

> Run the following command to add the Crosswalk plugin:

ionic plugin add cordova-plugin-crosswalk-webview

This is another plugin that we will add to every application, but you may decide you want to leave it out.

By adding this plugin, when you build for Android Crosswalk will be used. Android has a lot of issues,

especially with older devices, because there is so many dierent software versions out there and dierent

versions have dierent browsers (remember, since we are building HTML5 applications it is actually a

browser engine powering and running our application). What Crosswalk does is bundle a modern browser

into your application so no matter what device you are running on your app will be powered by the same

browser, and the Crosswalk browser can improve performance a lot.

The only real downside to this is that it increases the size of your application by a signicant amount. In

general, I think its worth it and Id recommend you include it but you may leave it out if you like. For more

information take a look at the Crosswalk Project website: https://crosswalk-project.org/

359
Set up Images

When building this application we are going to be making use of a few images. Ive included these in your

download pack but you will need to set them up in the application you generate.

> Copy the images folder in the download pack for this application from www/images to your own

www folder

Summary

Thats it! Were all set up and ready to go, now we can start working on the interesting stu.

360
Lesson 3: Creating a Tabs Layout

In this lesson we are going to create the basic layout for the application, which will be a tabs layout. A tabs

page works a little dierently to most other pages you would have created, the page itself is pretty much

just a placeholder for the tabs, but the actual content of the tabs are other pages that you import into the

tabs page. Once you see it in action itll probably make more sense.

Initially, we are going to create three tabs to hold the various pages of our application, but eventually we

will be throwing in one extra tab which will hold the Quick Lists functionality.

Lets start o with our Home page which will be the page that holds all of our tabs.

> Modify home.html to reect the following

<ion-tabs>
<ion-tab [root]="tab1Root" tabTitle="Location" tabIcon="navigate"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="My Details" tabIcon="person"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Camp Details"
tabIcon="bookmarks"></ion-tab>
</ion-tabs>

As you can see, the layout here is pretty simple. Were using <ion-tabs> and setting up several tabs

with <ion-tab>. We are setting the root property on each of these tabs to an expression which we will

soon dene in our home.ts le. This expression will be the page that we want that tab to contain. We also

set a title and an icon which will be displayed on the tab interface.

Now lets take a look at our class denition.

> Modify home.ts to reect the following

import {Component} from '@angular/core';


import {LocationPage} from '../location/location';
import {MyDetailsPage} from '../my-details/my-details';
import {CampDetailsPage} from '../camp-details/camp-details';

361
@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

tab1Root: any = LocationPage;


tab2Root: any = MyDetailsPage;
tab3Root: any = CampDetailsPage;

constructor(){

Were importing all the pages we want to display in the tabs here, and then we are setting those expressions

we were using in the template. The rst tab now knows that it should display the LocationPage as its

content.

Our tabs layout is basically set up now (hooray!), its not break time yet though - were also going to set

up the layouts and classes for each of the three tabs. Lets start with the Location page. The location

page will contain a map as well as some buttons that will allow the user to set their location, and launch

directions so that they can get back home.

> Modify location.html to reect the following:

<ion-header>
<ion-navbar primary>
<ion-title><img src = "images/logo.png" class="logo" /></ion-title>

<ion-buttons start>

362
<button (click)="setLocation()"><ion-icon
name="pin"></ion-icon></button>
</ion-buttons>

<ion-buttons end>
<button (click)="takeMeHome()"><ion-icon
name="walk"></ion-icon></button>
</ion-buttons>

</ion-navbar>
</ion-header>

<ion-content>

<div #pleaseConnect id="please-connect">


<p>Please connect to the Internet...</p>
</div>

<div #map id="map"></div>

</ion-content>

The <ion-navbar> allows us to add a header bar to the top of our application that can hold buttons,

titles and even integrates directly with Ionics navigation system to display a back button when neces-

sary. We add the primary attribute to the navbar to style it with our primary colour, which is dened in

app.variable.scss (we will congure these later).

Inside of this navbar we use <ion-title>, which is typically used to display a text title for the current

page, to display our logo. We also use <ion-buttons> to create some buttons in the navbar. By sup-

plying the start attribute, the buttons will be placed on the left side of the navbar, and by supplying the

end attribute the buttons will be placed on the right side. We place one button in each position to trigger

363
the set location, and take me home functions.

Finally, we have the content area which just contains a <div> which we have attached #map to - this will

allow us to grab a reference to this node later on. This <div> will also serve as a container for our Google

Map later on. Weve also added another <div> here which will show a connection message to the user if

they dont have an Internet connection available (we will implement this later).

Now lets take a look at the class denition for the location page.

> Modify location.ts to reect the following:

import {NavController, Platform, AlertController} from 'ionic-angular';


import {Component, ElementRef, ViewChild} from '@angular/core';
import {Geolocation} from 'ionic-native';
import {GoogleMaps} from '../../providers/google-maps/google-maps';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/location/location.html',
providers: [GoogleMaps]
})
export class LocationPage {

@ViewChild('map') mapElement: ElementRef;


@ViewChild('pleaseConnect') pleaseConnect: ElementRef;

latitude: number;
longitude: number;

constructor(public nav: NavController, public maps: GoogleMaps, public


platform: Platform, public dataService: Data, public alertCtrl:
AlertController) {

364
}

ngAfterViewInit(): void {

setLocation(): void {

takeMeHome(): void {

Weve set up quite a few things here but not much is going on yet. Were importing Geolocation from Ionic

Native which we will use later, as well as our Google Maps and Data services (which we will create later

as well). We add GoogleMaps to the list of providers here because it is specic to this class, but we dont

need to add Data because we will be adding it to the list of generic providers in app.ts instead.

We inject all of the services we require in the constructor and set up references to them by adding the

public keyword, as well as declaring two additional variables, latitude and longitude, which we will use
to hold the users location. Finally, we add the two functions that the buttons in the template call, but we

will implement their functionality later.

The weirdest thing here is the use of @ViewChild. You can use this to grab a reference to an element in

the template. So if we do this:

@ViewChild('map') mapElement: ElementRef;

365
it will look in the template location.html template for an element that contains #map. If it nds it, it will

return an ElementRef which is a reference to that element. In this instance we are assigning the result of

that to mapElement. Later on we will pass this reference through to our Google Maps provider. It will be

important to do this inside of the ngAfterViewInit() method, as that function will only run once the

DOM (the elements on our page) have been fully loaded.

Now lets move on to the Camp Details page.

> Modify camp-details.html to reect the following:

<ion-header>
<ion-navbar primary>
<ion-title><img src = "images/logo.png" class="logo" /></ion-title>
</ion-navbar>
</ion-header>

<ion-content padding class="camp-details">

<ion-card>

<ion-card-header>
Camp Details
</ion-card-header>

<ion-card-content>
Update this form with the details of your current camp so you have an
easy reference for later.
</ion-card-content>

</ion-card>

<ion-list no-lines>

366
<ion-item>
<ion-label stacked>Gate Access Code</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Ammenities Code</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>WiFi Password</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Phone Number</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Departure Date</ion-label>
<ion-datetime displayFormat="DD/MM/YYYY"></ion-datetime>
</ion-item>

<ion-item>
<ion-label stacked>Notes</ion-label>
<ion-textarea type="text"></ion-textarea>
</ion-item>

367
</ion-list>

</ion-content>

The rst thing we do is create the navbar, but you already know how that works. We use an <ion-card>

to create a little header area for the page where we can describe what the page is for, but this is purely

decorational. Then we have a bunch of inputs inside of a list. Rather than using standard HTML like:

<input type="text" />

We instead use <ion-input> which Ionic provides, but it still allows for all the same types (and you will

also notice the last eld is actually a <ion-textarea>). We supply the stacked attribute which gives us

stacked labels on our input elds, but theres a whole bunch of dierent styles Ionic provides for inputs

by default. For now weve just added labels to the elds, but later on we will come back and add some

extra stu so that we can actually grab the values that a user inputs.

Also notice the use <ion-datetime> here, which is an in-built date and time picker that Ionic provides.

Theres not going to be much happening yet, but lets also create the class for this page now.

> Modify camp-details.ts to reect the following:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {FormBuilder, ControlGroup, Validators} from '@angular/common';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/camp-details/camp-details.html',
})
export class CampDetailsPage {

368
constructor(public nav: NavController, public formBuilder: FormBuilder,
public dataService: Data) {

saveForm(): void {

The only thing were doing here thats out of the ordinary is importing FormBuilder and Validators from

Angular 2. Just above I mentioned that we will be editing the inputs we created later to actually do some-

thing, and this is where Form Builder (and ControlGroup) comes in. Form Builder will allow you to create

and manage forms, and Validators can be attached to specic elds to ensure a valid value is input (i.e. if

it needs to be an email address, phone number etc.). Well be getting to that a little later though.

Weve also added a saveForm function which we will use later to save the values the user enters in the

form. Now lets move on to the My Details page, which is almost exactly the same as this one.

> Modify my-details.html to reect the following

<ion-header>
<ion-navbar primary>
<ion-title><img src = "images/logo.png" class="logo" /></ion-title>
</ion-navbar>
</ion-header>

<ion-content padding class="my-details">

<ion-card>

369
<ion-card-header>
My Details
</ion-card-header>

<ion-card-content>
Update this form with your details so you have an easy reference for
later.
</ion-card-content>

</ion-card>

<ion-list no-lines>

<ion-item>
<ion-label stacked>Car Registration</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Trailer Registration</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Trailer Dimensions</ion-label>
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Phone Number</ion-label>

370
<ion-input type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Notes</ion-label>
<ion-textarea type="text"></ion-textarea>
</ion-item>

</ion-list>

</ion-content>

Theres not much need to explain this one because, apart from having dierent input elds, it is exactly the

same as the Camp Details template. Lets move on to the class.

> Modify my-details.ts to reect the following:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {FormBuilder, ControlGroup, Validators} from '@angular/common';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/my-details/my-details.html',
})
export class MyDetailsPage {

constructor(public nav: NavController, public formBuilder: FormBuilder,


public dataService: Data) {

371
saveForm(): void {

Once again, this is exactly the same as the Camp Details page. Sorry for the boring nish, but thats all

we need to for now for our layout, in the next lesson were going to take a closer look at Form Builder and

get our inputs working properly.

IMPORTANT: If you want to test your application right now, you will need to declare the Data and Con-

nectivity providers that we are importing and injecting. We will be doing this later in the app.ts le so that

we only need to declare it once for our whole application, but if you want to view your application now you

will need to make the following changes to app.ts:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {Data} from './providers/data/data';
import {Connectivity} from './providers/connectivity/connectivity';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {
rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {

372
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data, Connectivity]);

373
Lesson 4: User Input and Forms

In this lesson were going to be looking at the functionality of the Camp Details and My Details pages,

which will involve making our forms functional. If youve completed some of the other applications in this

book, or have tried using inputs in Ionic 2 before, you may have used ngModel like this:

<ion-input type="text" [(ngModel)]="myField"></ion-input>

This sets up two way data binding on this input eld, which I discussed in more detail in the basics section,

but essentially it ties the value of this input to:

this.myField

in the class denition for the page. If you change the value of this.myField it will be reected in the

input, and if you change the value of the input it will be reected in this.myField. This is a pretty easy

way to go about managing user input, but if you have more large and complex forms it starts to become

easier to use Form Builder.

As well as making the code a bit nicer, by using Form Builder each of your inputs will become controls

which provide some more powerful functionality that you can hook into (if you check out the Giist appli-

cation you will see that we use a control to subscribe to changes on the input eld, which allowed us to

do all sorts of fancy things).

We can also make use of Validators with Form Builder, which allow us to tie a validation to a specic

input eld, which will check the input to see if it is allowed (e.g. a correctly formatted email address).

Lets jump right into implementing it so that you can see how it works. Well go through implementing this

functionality on the Camp Details page, and then replicate it on the My Details page.

> Modify the list in camp-details.html to reect the following:

<ion-list no-lines>

<form [formGroup]="campDetailsForm" (change)="saveForm()">

374
<ion-item>
<ion-label stacked>Gate Access Code</ion-label>
<ion-input formControlName="gateAccessCode"
type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Ammenities Code</ion-label>
<ion-input formControlName="ammenitiesCode"
type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>WiFi Password</ion-label>
<ion-input formControlName="wifiPassword" type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Phone Number</ion-label>
<ion-input formControlName="phoneNumber" type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Departure Date</ion-label>
<ion-datetime formControlName="departure"
displayFormat="DD/MM/YYYY"></ion-datetime>
</ion-item>

<ion-item>
<ion-label stacked>Notes</ion-label>

375
<ion-textarea formControlName="notes" type="text"></ion-textarea>
</ion-item>

</form>

</ion-list>

Our inputs are mostly the same except for a couple of important dierences. First, we have wrapped the

whole thing in a form tag:

<form [formGroup]="campDetailsForm" (change)="saveForm()">

We dene the formGroup property as campDetailsForm which we will use with the Form Builder very

shortly. We also listen for the (change) event and trigger the saveForm function when it is detected,

this means that the saveForm function will trigger every time a user changes and switches away from a

specic input, but it wont trigger after every single character they type (although we could do that if we

wanted to). Usually on a form you would instead listen for the (submit) event and handle the data then,

but we dont want the user to have to hit a Save button or anything like that, we want it to just save as

soon as they enter a new value.

The other important thing we have changed is that we have added formControlName to each of our

inputs, giving it a name (similar to what we would do with ngModel). Again, we will use this with the Form

Builder in just a moment.

Now were going to take a look at the class denition. First, we are going to modify the constructor:

> Modify the constructor in camp-details.ts to reect the following:

campDetailsForm: ControlGroup;

constructor(public nav: NavController, public formBuilder: FormBuilder,


public dataService: Data) {

376
this.campDetailsForm = formBuilder.group({
gateAccessCode: [''],
ammenitiesCode: [''],
wifiPassword: [''],
phoneNumber: [''],
departure: [''],
notes: ['']
});

Since we dened the formGroup as campDetailsForm in the template, we can assign a new Form

Builder group to it here. To create the group, we supply all of the formControlName names we added to

the inputs. Notice that we also supply an array that contains an empty string, this is used to initialse the

value of the input, for example:

gateAccessCode: ['54321']

would set the gateAccessCode input value to 54321. You can also supply a validator here if you like,

by doing something like this:

gateAccessCode: ['', Validators.required]

this will make the gateAccessCode eld a required eld. Thats all there is to setting up our form, now we

just need to implement the saveForm function.

> Modify the saveForm function in camp-details.ts** to reect the following:**

saveForm(): void {
let data = this.campDetailsForm.value;
//this.dataService.setCampDetails(data);
}

377
NOTE: We have commented out the call to the data service since we havent implemented it yet, otherwise

it will cause TypeScript to throw errors at us.

We can grab all the values from our form at any time by using this.campDetailsForm.value. This

will return an object that contains all of the values, which is exactly how we would eventually like to store

them. So we pass this object of values through to our data service to save (which we havent implemented

yet of course).

Remember, the saveForm function gets triggered every time the user makes a change to any input eld,

so whenever they make a change we read the values and send them o for saving.

Now we just need to reect this functionality in our My Details page. Again, its pretty much exactly the

same so Ill just paste the code below rather than explaining it step by step.

> Modify my-details.html to reect the following:

<ion-header>
<ion-navbar primary>
<ion-title><img src = "images/logo.png" class="logo" /></ion-title>
</ion-navbar>
</ion-header>

<ion-content padding class="my-details">

<ion-card>

<ion-card-header>
My Details
</ion-card-header>

<ion-card-content>
Update this form with your details so you have an easy reference for
later.

378
</ion-card-content>

</ion-card>

<ion-list no-lines>

<form [formGroup]="myDetailsForm" (change)="saveForm()">

<ion-item>
<ion-label stacked>Car Registration</ion-label>
<ion-input formControlName="carRegistration"
type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Trailer Registration</ion-label>
<ion-input formControlName="trailerRegistration"
type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Trailer Dimensions</ion-label>
<ion-input formControlName="trailerDimensions"
type="text"></ion-input>
</ion-item>

<ion-item>
<ion-label stacked>Phone Number</ion-label>
<ion-input formControlName="phoneNumber" type="text"></ion-input>
</ion-item>

379
<ion-item>
<ion-label stacked>Notes</ion-label>
<ion-textarea formControlName="notes" type="text"></ion-textarea>
</ion-item>

</form>

</ion-list>

</ion-content>

> Modify the constructor in my-details.ts to reect the following:

myDetailsForm: ControlGroup;

constructor(public nav: NavController, public formBuilder: FormBuilder,


public dataService: Data) {

this.myDetailsForm = formBuilder.group({
carRegistration: [''],
trailerRegistration: [''],
trailerDimensions: [''],
phoneNumber: [''],
notes: ['']
});

> Modify the saveForm function in my-details.ts to reect the following:

saveForm(): void {

380
let data = this.myDetailsForm.value;
//this.dataService.setMyDetails(data);
}

Forms arent exactly the most exciting thing in the world (well, for most people at least), but they are a

critical component of many mobile applications so its important to understand how they work. Being able

to use Form Builder can make your forms a lot more manageable and powerful, but sometimes a simple

[(ngModel)] is enough to do the job as well.

In the next lesson well be jumping into something a little more fun, and quite a bit more complicated, when

we implement Google Maps!

381
Lesson 5: Implementing Google Maps and Geolocation

Google Maps and mobile apps are a perfect match. The Google Maps API is an awesome piece of tech

by itself, but when you couple it with a device that is meant to be mobile, as in not stationary, it opens up

a wide range of possibilities. Theres a ton of cool apps out there today that utilise Google Maps to do all

kinds of things.

Even if maps arent the core feature of your application, they are often quite useful as supplementary

features as well (displaying the location of a business on a map for example).

In this lesson we will be implementing Google Maps on our Location page. Essentially what we want to

do is:

Display a map

Allow the user to set their current location on the map

Display a marker at their last set location

Enable the user to launch directions to take them back to their set location

Getting the Google Maps SDK set up in an application, and even using it, is pretty easy. You simply load

the Google Maps SDK script and then start interacting with the API. It gets a bit more complicated than

that though because of one main problem:

What if the user does not have an Internet connection?

Its not unreasonable to make the maps unavailable if the user does not have an Internet connection,

but how do we handle that gracefully? We dont want an error occurring and breaking the application

(because the Google Maps SDK hasnt been loaded) or otherwise causing the maps not to work, so we

need to consider the following:

What if the user does not have an Internet connection?

What if the user does not have an Internet connection initially but does later?

What if the user does have an Internet connection initially but doesnt later?

To handle all of these scenarios, the solution we want to implement will:

382
Wait until a connection is available before loading the Google Maps SDK, rather than straight away

If the connection becomes unavailable, disable the Google Maps functionality

If the connection becomes available again, enable the Google Maps functionality

To make our code a little bit cleaner we are going to abstract a lot of this functionality into the Google Maps

provider we generated earlier. This will also make it easier to reuse the same code in another applications

as well.

NOTE: We will be using the Google Maps JavaScript SDK in this lesson, but you should also be aware

that you can use their native SDK as well through this Cordova plugin: https://github.com/mapsplugin/

cordova-plugin-googlemaps

This lesson is going to be pretty big so lets jump right into it. Were going to start o by implementing a

Connectivity service, which is the other provider we generated earlier.

Connectivity Service

This is going to be a quick and simple little service that will allow us to easily check if the user has an

Internet connection or not. If they are on a device we will be able to make use of the network information

plugin we installed earlier (which is more accurate), but if the app is just running through a normal browser

then we use the onLine property to check for an Internet connection (which is less accurate).

> Modify connectivity.ts to reect the following:

import {Injectable} from '@angular/core';


import {Network} from 'ionic-native';
import {Platform} from 'ionic-angular';

declare var Connection;

@Injectable()
export class Connectivity {

383
onDevice: boolean;

constructor(public platform: Platform){


this.onDevice = this.platform.is('cordova');
}

isOnline(): boolean {
if(this.onDevice && Network.connection){
return Network.connection !== Connection.NONE;
} else {
return navigator.onLine;
}
}

isOffline(): boolean {
if(this.onDevice && Network.connection){
return Network.connection === Connection.NONE;
} else {
return !navigator.onLine;
}
}
}

This service is reasonably straight forward. We create a onDevice variable that uses Platform to check

if the application is running on a device. We then use that onDevice variable to see if we should check

for navigator.connection.type which makes use of the network information plugin, or just check

the navigator.onLine property.

Weve dened two functions isOnline and isOffline so we will be able to call these from other classes

that import this service. Technically you could just dene one of these functions (if isOnline returns false

384
then we know they are oine obviously), but I think its nice to have the option to be able to use either of

the two.

Thats all there is to this service, so lets move on to the Google Maps service.

Google Maps Service

This service is going to hold most of the logic for our maps functionality. Its a pretty big service so well

create a bit of a skeleton rst and then implement it function by function.

> Modify google-maps.ts to reect the following:

import {Injectable} from '@angular/core';


import {Connectivity} from '../../providers/connectivity/connectivity';
import {Geolocation} from 'ionic-native';
import {Observable} from 'rxjs/Observable';

declare var google;

@Injectable()
export class GoogleMaps {

mapElement: any;
pleaseConnect: any;
map: any;
mapInitialised: boolean = false;
mapLoaded: any;
mapLoadedObserver: any;
currentMarker: any;
apiKey: string;

385
constructor(public connectivityService: Connectivity) {

init(mapElement: any, pleaseConnect: any): any {

loadGoogleMaps(): void {

initMap(): void {

disableMap(): void {

enableMap(): void {

addConnectivityListeners(): void {

changeMarker(lat: number, lng: number): void {

386
}

Notice that weve imported the Connectivity service we just created and added it as a provider in the

decorator. We also import the Geolocation plugin from Ionic Native, and Observable from the RxJS

library. We also added declare var google just after the imports - this is so that the TypeScript

compiler leaves us alone. Since were dynamically loading the Google Maps SDK the compiler doesnt

know what google is and will throw errors at us if we dont declare the variable.

If youve read the basics section and completed the Giist application then you would know quite a bit

about Observables, but to recap an Observable is something that can emit multiple values over time, and

we can subscribe to it to listen for those values. Were going to use an Observable to indicate when

Google Maps has nished loading. To do this we will simply create an Observable which is accessible

from outside of this service, and we will manually make it emit a value when weve nished loading.

Finally we have a bunch of functions that we have created, lets go through implementing these one by

one.

> Modify the init function to reect the following:

init(mapElement: any, pleaseConnect: any): any {

this.mapElement = mapElement;
this.pleaseConnect = pleaseConnect;

this.mapLoaded = Observable.create(observer => {


this.mapLoadedObserver = observer;
});

this.loadGoogleMaps();

387
return this.mapLoaded;

We will be able to call this init function to trigger the map loading process from wherever we import

the service. First we create our observable and then we trigger the next function in this process which is

loadGoogleMaps.

Since we will call this function from location.ts we are able to pass in the references to the map and

pleaseConnect elements that we retrieved earlier with @ViewChild. We accept those as parameters
here and them up as member variables so we can access them from anywhere within the class.

Notice that this function returns this.mapLoaded which is our Observable. This means that we can

assign this function to a variable when we call it, and then subscribe to that variable to be notied when

the map has nished loading (you will see this in action later). A little later on in this loading process we

will emit a value on that observable.

Now lets implement the loadGoogleMaps function.

> Modify the loadGoogleMaps function to reect the following:

loadGoogleMaps(): void {

if(typeof google == "undefined" || typeof google.maps == "undefined"){

console.log("Google maps JavaScript needs to be loaded.");


this.disableMap();

if(this.connectivityService.isOnline()){

window['mapInit'] = () => {
this.initMap();
this.enableMap();

388
}

let script = document.createElement("script");


script.id = "googleMaps";

if(this.apiKey){
script.src = 'http://maps.google.com/maps/api/js?key=' +
this.apiKey + '&callback=mapInit';
} else {
script.src = 'http://maps.google.com/maps/api/js?callback=mapInit';
}

document.body.appendChild(script);

}
}
else {

if(this.connectivityService.isOnline()){
this.initMap();
this.enableMap();
}
else {
this.disableMap();
}

this.addConnectivityListeners();

389
}

This function looks a bit more complicated, but its reasonably straightforward. First we check if Google

Maps is loaded by checking for the existence of google and google.maps which would be available if

the SDK had already been loaded.

If the SDK has not been loaded yet, we trigger the loading process. Since the SDK hasnt loaded yet, we

rst call the disableMap function, which will indicate to the user that the map is not available. Then we

check if the user is online using our connectivity service, and if they are we inject the Google Maps SDK by

adding a script element to the application. Notice that there is a &callback=mapInit parameter in the

URL string. This allows us to trigger a function in our application once the Google Maps SDK has nished

loading, and in our case we call the initMap and enableMap functions once the load has nished (which

we will implement shortly).

If the SDK has already been loaded then we check if the user is online. If they are online we initialise and

enable the map, and if they arent online we disable the map.

The last line in this function calls the addConnectivityListeners function which we will also imple-

ment soon. This will listen for online and oine events so that we know when to enable and disable the

map, and also when to try loading the SDK again if the user was initially oine when they opened the

application.

Now onto the next function.

> Modify the initMap function to reect the following:

initMap(): void {

this.mapInitialised = true;

Geolocation.getCurrentPosition().then((position) => {

let latLng = new google.maps.LatLng(position.coords.latitude,

390
position.coords.longitude);

let mapOptions = {
center: latLng,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
}

this.map = new google.maps.Map(this.mapElement, mapOptions);


this.mapLoadedObserver.next(true);

});

The Google Maps SDK is loaded now, and this function will handle setting up a new map using the SDK.

We want to center the map at the users current location, so we rst make a call to getCurrentPosition

function of the Geolocation plugin. Once the promise this returns resolves it will pass through a position

object which will contain the users current latitude and longitude. We use these values, along with some

additional settings (zoom level and the map type) to create a new map instance.

The map will be created inside of the element that we passed the reference in for (#map). So after this

code runs, the Google Map will be added to our Location pages template.

At this point our map is ready to be interacted with, so we trigger the next function on our observer which

will cause it to emit a value. As I mentioned before, we will be subscribing to this observer so we will be

able to tell that the map has nished loading.

Although weve got the map loading now, theres still a few more functions we need to create.

> Modify the disableMap and enableMap functions to reect the following:

disableMap(): void {

391
if(this.pleaseConnect){
this.pleaseConnect.style.display = "block";
}

enableMap(): void {

if(this.pleaseConnect){
this.pleaseConnect.style.display = "none";
}

What we want to do here is show and hide an overlay on the Google Maps section so that when the user

is not connected to the Internet they will not be able to use the maps, and a message that says Please

connect to the Internet will display. The code above handles hiding and showing an element that will

display a message when the user gains or loses their Internet connection.

We will need to style the element so that it will overlay the map.

> Modify location.scss to reect the following:

#please-connect {
position: absolute;
background-color: #000;
opacity: 0.5;
width: 100%;
height: 100%;
z-index: 1;
}

392
#please-connect p {
color: #fff;
font-weight: bold;
text-align: center;
position: relative;
font-size: 1.6em;
top: 30%;
}

Now when the user does not have a connection, you should see a screen like this:

393
We still need to nish up a few things before we reach that point though. Next up we have the

addConnectivityListeners function.

394
> Modify the addConnectivityListeners function in google-maps.ts to reect the following:

addConnectivityListeners(): void {

document.addEventListener('online', () => {

console.log("online");

setTimeout(() => {

if(typeof google == "undefined" || typeof google.maps == "undefined"){


this.loadGoogleMaps();
}
else {
if(!this.mapInitialised){
this.initMap();
}

this.enableMap();
}

}, 2000);

}, false);

document.addEventListener('offline', () => {

console.log("offline");

this.disableMap();

395
}, false);

As I mentioned, this function will handle what to do when the users connection status switches from online

to oine or vice versa. Were listening for both the online and oine events here, which trigger when

the user goes online or oine.

When the user comes online we check if Google Maps has already been loaded, and if it hasnt we load it.

Otherwise we check if the map has already been initialised and initialise it if it hasnt and then we enable

the map. Notice that we use a setTimeout here which will cause this code to run 2 seconds after the

user comes online - in the past Ive had trouble with triggering this code instantly, so I give the connection

a little bit of time to settle rst.

When the user goes oine, all we do is disable the map. The last function we have to implement is the

changeMarker function.

> Modify the changeMarker function to reect the following:

changeMarker(lat: number, lng: number): void {

let latLng = new google.maps.LatLng(lat, lng);

let marker = new google.maps.Marker({


map: this.map,
animation: google.maps.Animation.DROP,
position: latLng
});

if(this.currentMarker){
this.currentMarker.setMap(null);
}

396
this.currentMarker = marker;

All the other functions have been pretty generic and could be used in just about every implementation of

Google Maps you use in your applications, but this one is specic to this application. Rather than having

an addMarker function, this function will add a marker and remove the previous marker. We only ever

want the user to have one camp location set so this makes sense in this case.

All we do is pass in the latitude and longitude of where we want to add a marker and then create a new

marker using those values. If there is an existing marker then we remove it rst and then set the current

marker to be the one we just added.

Now that we have our Google Maps service completely set up, we just have to use it!

Implementing Google Maps

Weve done quite a bit of work to get maps working already, but we still have a little way to go. Now we

are going to modify our Location page class denition to make use of the Google Maps service. Weve

already imported the service, added it to the providers array and created a reference to it so we can just

start using it right away.

> Modify ngAfterViewInit in location.ts to reect the following:

ngAfterViewInit(): void {

let mapLoaded = this.maps.init(this.mapElement.nativeElement,


this.pleaseConnect.nativeElement);

mapLoaded.subscribe(update => {
//this.maps.changeMarker(this.latitude, this.longitude);

397
});

First we trigger the map loading process by calling this.maps.init but we also assign that to the

mapLoaded variable because it returns the observable that we created. We then subscribe to that ob-
servable so that we can listen for when the map has nished loading, and when it does we call the

changeMarker function.

This wont work just yet because the latitude and longitude values will be done, so Ive commented it out.

Later though, we will be loading the latitude and longitude values that are saved in memory.

The map should be loaded and visible on the screen at this point, but it wont display correctly just yet.

First, you will need to add a couple of styles to your .scss le to display it properly.

> Modify location.scss to reect the following:

#please-connect {
position: absolute;
background-color: #000;
opacity: 0.5;
width: 100%;
height: 100%;
z-index: 1;
}

#please-connect p {
color: #fff;
font-weight: bold;
text-align: center;
position: relative;
font-size: 1.6em;

398
top: 30%;
}

.scroll {
height: 100%;
}

#map {
width: 100%;
height: 100%;
}

Next we are going to implement the setLocation function, which will set the users camp location to

their current location.

> Modify the setLocation function in location.ts to reect the following:

setLocation(): void {

Geolocation.getCurrentPosition().then((position) => {

this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;

this.maps.changeMarker(position.coords.latitude,
position.coords.longitude);

let data = {
latitude: this.latitude,
longitude: this.longitude
};

399
//this.dataService.setLocation(data);

let alert = this.alertCtrl.create({


title: 'Location set!',
subTitle: 'You can now find your way back to your camp site from
anywhere by clicking the button in the top right corner.',
buttons: [{text: 'Ok'}]
});

alert.present();

});

NOTE: Once again, we have commented out the call to the not yet implemented data service in this code

Once again, we are using the Geolocation plugin to grab the users current position. Once we get that we

change the this.latitude and this.longitude values to the users current location, and we also

call the changeMarker function with that position. We want this location to be remembered when the

user opens the application again so we create a data object for the position and send it o to our data

service to be saved (remember, we havent actually implemented that yet).

Once this process is complete, we trigger an alert to let the user know that their location was succesfully

set and what that means. Now we just have one more function to dene:

> Modify the takeMeHome function in location.ts to reect the following:

takeMeHome(): void {

if(!this.latitude || !this.longitude){

400
let alert = this.alertCtrl.create({
title: 'Nowhere to go!',
subTitle: 'You need to set your camp location first. For now, want to
launch Maps to find your own way home?',
buttons: ['Ok']
});

alert.present();
}
else {

let destination = this.latitude + ',' + this.longitude;

if(this.platform.is('ios')){
window.open('maps://?q=' + destination, '_system');
} else {
let label = encodeURI('My Campsite');
window.open('geo:0,0?q=' + destination + '(' + label + ')',
'_system');
}

The goal of this function is to show the user directions from their current location to their camp sites location.

First we check that the this.latitude and this.longitude values are set, and if they are not we

simply display an alert letting the user know they need to set their location rst.

If they do exist then we use the Google Maps and Apple Maps URL schemes to launch the maps application

401
with directions to the coordinates we supply. If we are running on iOS then we launch Apple Maps using

the maps:// scheme, and if we are running on Android then we launch Google Maps using the geo:

scheme. Now when the user triggers this function, a map should pop up with directions back to their

marked camp site.

Thats it! Weve nished our maps functionality. A user should now be able to view the map, set their

location, and trigger directions back to their camp site. We still need to take care of one more important

thing and thats saving the data so that it is available when the user comes back to the application. We

will be handling that, as well as saving the data from the forms, in the next lesson.

402
Lesson 6: Saving and Retrieving Data

If youve been through some of the other applications, then you will be pretty familiar with creating a Data

service. This one is a little dierent though because in the other applications we were only ever storing

one set of data, but in this application we are going to have to save:

Camp Coordinates

Camp Details

My Details

The idea is still more or less the same, but its going to look a little dierent. We already have our forms and

our maps page sending data to our Data service so we just need to handle saving it, and we also need to

make a few modications to load that data back in again. Lets start out by implementing the Data service.

> Modify data.ts to reect the following:

import {Storage, SqlStorage} from 'ionic-angular';


import {Injectable} from '@angular/core';

@Injectable()
export class Data {

storage: Storage;

constructor(){
this.storage = new Storage(SqlStorage, {name:'campermate'});
}

setMyDetails(data: Object): void {


let newData = JSON.stringify(data);
this.storage.set('mydetails', newData);
}

403
setCampDetails(data: Object): void {
let newData = JSON.stringify(data);
this.storage.set('campdetails', newData);
}

setLocation(data: Object): void {


let newData = JSON.stringify(data);
this.storage.set('location', newData);
}

getMyDetails(): Promise<any> {
return this.storage.get('mydetails');
}

getCampDetails(): Promise<any> {
return this.storage.get('campdetails');
}

getLocation(): Promise<any> {
return this.storage.get('location');
}

First, in our constructor we set up a reference to our storage service. We create an instance of the generic

Storage service and then use SqlStorage to set it up with a database name of campermate. By using

SqlStorage the Storage service will make use of the SQLite plugin when running on a device, alternatively

we couldve used LocalStorage to just use the browser storage (but remember, local storage is volatile

and can be wiped - so it shouldnt be used to reliably store data).

404
In other applications we would usually just have two functions, one for getting data and one for saving

data. In this application though we are setting three dierent sets of data, so we create three set functions

and three get functions. The set functions take in the data we pass it and store it (after converting it to a

JSON string), and then the get functions will retrieve those values from the database. The get functions

will return a promise which resolves with the data, rather than the data directly, so we will need to set up

a handler for that when we use it.

Now that we have our Data service set up, we just need to load in the data that is saved when we reopen

the application. So now we are going to make changes to the Location, My Details and Camp Details

pages as they all have some data they need loaded in.

Well start o with the location page.

> Modify ngAfterViewInit in location.ts to reect the following:

ngAfterViewInit(): void {

this.dataService.getLocation().then((location) => {

let savedLocation: any = false;

if(typeof(location) != "undefined"){
savedLocation = JSON.parse(location);
}

let mapLoaded = this.maps.init(this.mapElement.nativeElement,


this.pleaseConnect.nativeElement);

if(savedLocation){
this.latitude = savedLocation.latitude;
this.longitude = savedLocation.longitude;

405
mapLoaded.subscribe(update => {
this.maps.changeMarker(this.latitude, this.longitude)
});

});

What were doing here is rst grabbing the users location from memory, and then triggering the map load.

If there was a location stored in memory then we set the this.latitude and this.longitude values

to be those values, and we supply them to the changeMarker function call which will be triggered once

the map has nished loading. If there is no stored location values we just load the map.

Next lets take care of loading in the form data for our Camp Details page.

> Modify camp-details.ts to reect the following:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {FormBuilder, ControlGroup, Validators} from '@angular/common';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/camp-details/camp-details.html',
})
export class CampDetailsPage {

campDetailsForm: ControlGroup;

constructor(public nav: NavController, public formBuilder: FormBuilder,

406
public dataService: Data) {

this.campDetailsForm = formBuilder.group({
gateAccessCode: [''],
ammenitiesCode: [''],
wifiPassword: [''],
phoneNumber: [''],
departure: [''],
notes: ['']
});

this.dataService.getCampDetails().then((details) => {

let savedDetails: any = false;

if(typeof(details) != "undefined"){
savedDetails = JSON.parse(details);
}

let formControls: any = this.campDetailsForm.controls;

if(savedDetails){
formControls.gateAccessCode.updateValue(savedDetails.gateAccessCode);
formControls.ammenitiesCode.updateValue(savedDetails.ammenitiesCode);
formControls.wifiPassword.updateValue(savedDetails.wifiPassword);
formControls.phoneNumber.updateValue(savedDetails.phoneNumber);
formControls.departure.updateValue(savedDetails.departure);
formControls.notes.updateValue(savedDetails.notes);
}

407
});

saveForm(): void {
let data = this.campDetailsForm.value;
this.dataService.setCampDetails(data);
}

You can see weve added in another call to retrieve some data from the data service here and then were

doing some stu with it. Remember before how I said you an supply an initial value when creating your

group with Form Builder, i.e:

gateAccessCode: ['value here']

you might expect that you could just load in the data and then build your form using the saved values

as the initial values. Unfortunately, it doesnt quite work like this. The data being retrieved from mem-

ory is asynchronous, so although the data is retrieved very quickly there is some wait time involved.

The Form Builder group needs to be created instantly, otherwise you will receive errors. So instead

we create the Form Builder group right away, and then we grab a reference to the forms controls us-

ing this.campDetailsForm.controls. We can then access the updateValue method on each of

the controls we created to set the value to the saved value.

Now we just need to do the same for the My Details page.

> Modify my-details.ts to reect the following:

import {Component} from '@angular/core';


import {NavController} from 'ionic-angular';
import {FormBuilder, ControlGroup, Validators} from '@angular/common';
import {Data} from '../../providers/data/data';

408
@Component({
templateUrl: 'build/pages/my-details/my-details.html',
})
export class MyDetailsPage {

myDetailsForm: ControlGroup;

constructor(public nav: NavController, public formBuilder: FormBuilder,


public dataService: Data) {

this.myDetailsForm = formBuilder.group({
carRegistration: [''],
trailerRegistration: [''],
trailerDimensions: [''],
phoneNumber: [''],
notes: ['']
});

this.dataService.getMyDetails().then((details) => {

let savedDetails: any = false;

if(typeof(details) != "undefined"){
savedDetails = JSON.parse(details);
}

let formControls: any = this.myDetailsForm.controls;

if(savedDetails){

409
formControls.carRegistration.updateValue(savedDetails.carRegistration);
formControls.trailerRegistration.updateValue(savedDetails.trailerRegistration);
formControls.trailerDimensions.updateValue(savedDetails.trailerDimensions);
formControls.phoneNumber.updateValue(savedDetails.phoneNumber);
formControls.notes.updateValue(savedDetails.notes);
}

});

saveForm(): void {
let data = this.myDetailsForm.value;
this.dataService.setMyDetails(data);
}

Weve uncommented the calls to the data service in both the Camp Details and My Details classes above,

but we also need to uncomment it in the Location class as well.

> Modify the setLocation() function in location.ts to reect the following:

setLocation(): void {

Geolocation.getCurrentPosition().then((position) => {

this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;

this.maps.changeMarker(position.coords.latitude,

410
position.coords.longitude);

let data = {
latitude: this.latitude,
longitude: this.longitude
};

this.dataService.setLocation(data);

let alert = this.alertCtrl.create({


title: 'Location set!',
subTitle: 'You can now find your way back to your camp site from
anywhere by clicking the button in the top right corner.',
buttons: [{text: 'Ok'}]
});

alert.present();

});

Were also going to declare both our Connectivity and Data providers globally inside of our root compo-

nent.

> Modify app.ts to reect the following:

import {Component} from "@angular/core";


import {Platform, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';

411
import {Data} from './providers/data/data';
import {Connectivity} from './providers/connectivity/connectivity';

@Component({
template: '<ion-nav [root]="rootPage"></ion-nav>'
})
export class MyApp {
rootPage: any = HomePage;

constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}

ionicBootstrap(MyApp, [Data, Connectivity]);

and thats it! Any data that is entered in the application should remain there when the application is reloaded

now.

Were getting pretty close to nishing this application, we obviously still need to add some styling (although

it looks pretty groovy already), but in the next lesson were going to be adding one more little bonus feature

to this application.

412
Lesson 7: Reusing Components

I heard you like apps, so Im going to put an app in your app. Up until this point our application has only

had three tabs, but were about to add a whole new tab now. It just so happens that the rst application

in this book, QuickLists, would be a perfect companion to this application. Checklists can come in very

handy when youre going camping.

What were going to do in this lesson is essentially drag and drop the functionality from Quick Lists into

this application. Given the modular nature of Ionic 2 & Angular 2, its pretty easy to do. If youve already

created the Quick Lists application you can copy the code from there, but if not you can just copy the code

from the source code that came in your download package.

Its not going to be quite as easy as dragging and dropping, there will be a few conicts we run into but

it is pretty straightforward. Lets start out by copying over the les we need.

> Copy the checklist-model folder from QuickLists into the providers** folder in CamperMate**

> Copy the checklist page folder from QuickLists into the pages folder in CamperMate

Those two are easy enough, but now we run into a bit of trouble. We also want to copy over the HomePage

from QuickLists, but we already have a HomePage in CamperMate, so were going to make a bit of a

modication.

> Create a new folder called quicklistshome inside of the pages folder in CamperMate

> Copy the contents of the home folder from QuickLists (home.js, home.html, home.scss) into the

quicklistshome folder you just created

> Rename home.ts, home.html and home.scss to quicklistshome.ts, quicklistshome.html

and quicklistshome.scss

Hopefully that made sense, the end result is that you will now have also copied over the home page folder

from QuickLists, but everything will be called quicklistshome instead of just home so that we dont run

into any trouble with our existing home page. If all of that was a little confusing, just check the folder

413
structure in the example source code that came with this book.

Since weve made some changes to the le names, were also going to have to edit the code a little bit

too. Well have to change the class name of the QuickLists Home Page we just copied over to reect its

new name, and well also have to change where it gets its template from:

> Make the following modications to quicklistshome.ts:

import {Component} from '@angular/core';


import {NavController, AlertController} from 'ionic-angular';
import {ChecklistPage} from '../checklist/checklist';
import {ChecklistModel} from
'../../providers/checklist-model/checklist-model';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/quicklistshome/quicklistshome.html'
})
export class QuickListsHomePage {

checklists: ChecklistModel[] = [];


local: Storage;

constructor(public nav: NavController, public dataService: Data, public


alertCtrl: AlertController) {

this.dataService.getData().then((checklists) => {

let savedChecklists: any = false;

if(typeof(checklists) != "undefined"){
savedChecklists = JSON.parse(checklists);

414
}

if(savedChecklists){

savedChecklists.forEach((savedChecklist) => {

let loadChecklist = new ChecklistModel(savedChecklist.title,


savedChecklist.items);
this.checklists.push(loadChecklist);

loadChecklist.checklist.subscribe(update => {
this.save();
});

});

});

Notice that both the class name and the templateUrl have changed. Theres one important thing we ne-

glected to copy over and that is the Data service from the QuickLists application. This is because we

already have one for the CamperMate application, so instead of using a whole new data provider we are

just going to modify our existing one.

> Add the following two functions to data.ts

getData(): Promise<any> {
return this.storage.get('checklists');

415
}

save(data: any): void {

let saveData = [];

//Remove observables
data.forEach((checklist) => {
saveData.push({
title: checklist.title,
items: checklist.items
});
});

let newData = JSON.stringify(saveData);


this.storage.set('checklists', newData);

These two functions have a bit of a dierent naming style than the rest of the functions in data.ts but

keeping it this way saves us from having to make more code changes to the QuickLists functionality so

well keep it that way.

There isnt going to be much we need to in terms of styling, but our QuickLists application is using the

secondary colour on its navbars, but CamperMate uses primary so we will need to change that.

> Modify the navbar in checklist.html to reect the following:

<ion-navbar primary>

> Modify the navbar in quicklistshome.html to reect the following:

<ion-navbar primary>

416
Were also going to make another little code change that is in the general styling for the QuickLists appli-

cation.

> Modify app.core.scss to include the following style:

button {
border: none !important;
}

With that, we have all of the QuickLists functionality we need ready to use in our CamperMate application.

All we have to do now is add the tab for it.

> Modify home.html to include a fourth tab:

<ion-tabs>
<ion-tab [root]="tab1Root" tabTitle="Location" tabIcon="navigate"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="My Details" tabIcon="person"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Camp Details"
tabIcon="bookmarks"></ion-tab>
<ion-tab [root]="tab4Root" tabTitle="Checklists"
tabIcon="checkbox"></ion-tab>
</ion-tabs>

This will use tab4Root as the tab which we will of course also have to dene in our class denition.

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {LocationPage} from '../location/location';
import {MyDetailsPage} from '../my-details/my-details';
import {CampDetailsPage} from '../camp-details/camp-details';
import {QuickListsHomePage} from '../quicklistshome/quicklistshome';

@Component({

417
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

tab1Root: any;
tab2Root: any;
tab3Root: any;
tab4Root: any;

constructor(){
this.tab1Root = LocationPage;
this.tab2Root = MyDetailsPage;
this.tab3Root = CampDetailsPage;
this.tab4Root = QuickListsHomePage;
}

Now if you load up your application you should see a whole new tab:

418
As you can see, its pretty easy to reuse code youve created for other application with this modular style

project structure, and it would be even easier if we werent using components with the same name like we

did here.

419
Our application is looking pretty good already, but were going to apply a few more styles in the next lesson

to nish things o and get it looking great.

420
Lesson 8: Styling

This application is pretty stock standard, so theres not going to be too much customisation going on here

and this lesson is going to be super quick. But we do want to add a few little nal touches.

Lets start by modifying our colour variables.

> Modify app.variables.scss to reect the following:

$colors: (
primary: #5b91da,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

and then we just want to apply some general styles to our whole application.

> Modify app.core.scss to reect the following

@import "../pages/home/home";
@import "../pages/location/location";
@import "../pages/my-details/my-details";
@import "../pages/camp-details/camp-details";
@import "../pages/quicklistshome/quicklistshome";
@import "../pages/checklist/checklist";

.logo {
max-height: 39px;
margin-top: 6px;
}

421
ion-input, ion-textarea {
background-color: #F3F3F3;
border: 1px solid #cecece;
padding-left: 10px;
}

ion-textarea {
height: 200px;
}

textarea {
height: 180px;
}

button {
border: none !important;
}

The main thing we are tring to achieve here is to add a bit of styling to our input elds. Before doing this,

the input elds were white and so was the background, so you couldnt even see where the input elds

were. Now they will have a grey background colour and a bit of extra styling. Weve also added a bit

of styling to the textarea to expand its default height, and to also workaround a bug that exists currently

where the textarea doesnt grow with the users input.

The application should now look like this:

422
423
This is the single smallest lesson in the entirety of this book, but, were done! The CamperMate application

is now completely nished.

424
Conclusion

Congratulations on making it through the Camper Mate tutorial. Weve learned a lot through developing

this application, but the main take aways are:

Creating forms and capturing user data

Implementing Google Maps and creating a custom provider to handle that

Creating a tabs layout

Saving and retrieving multiple sets of data

Reusing components from another application

Theres always room to take things further though, especially when youre trying to learn something. Fol-

lowing tutorials is great, but its even better when you gure something out for yourself. Hopefully you

have enough background knowledge now to start trying to extend the functionality of the application by

yourself, heres a few ideas to try out:

Use Validators on the Phone Number and Date input elds to ensure valid data is input [MEDIUM]

Use local notications (see the Snapaday application for help) to notify the user the day before their

departure date [HARD]

Add a eld to Camp Details that allows the user to take a photo and display it there (see Snapaday

applicaiton for help) [HARD]

Remember, the Ionic 2 documentation is your best friend when trying to gure things out.

What next?

You have a completed application now, but thats not the end of the story. You also need to get it running

on a real device and submitted to app stores, which is no easy task. The nal sections in this book will

walk through how to take what you have done here, and get it onto the app stores so make sure to give

that a read.

425
Chapter 7

Camper Chat

426
Lesson 1: Introduction

This one is the big one, the pice de rsistance, the crme de la crme, its the last application we will

be building and I think it ts the bill. As with all the other applications in this book, there is no need to

complete the other applications before doing this one as everything will still be expalined, even if the same

thing has been explained in other applications, but the complexity is cranked up a notch in this one and I

wont be spending as long on the basics. So, if youre not that comfortable with Ionic 2 yet you might nd

one of the other applications easier to start on.

In this section we will be building Camper Chat, which will essentially be a live chat application. Users will

be able to log in with their Facebook account and chat all things caravan and camping with anybody else

who is using the application. The coolest thing about this application is the integration with PouchDB for

storing local data, and Cloudant for syncing that PouchDB data to a remote backend. This means that

the data can be available to users when they are oine, and when they come back online again the latest

updates will be fetched from the remote backend.

To give you a more precise denition, the exact features of the application will be:

Users can log in with a Facebook account to use the application

Users can leave messages that all other users can see and respond to (in real time)

The users Facebook display picture and name will be used in the application

Users can log out

Users can view an About page.

Given these requirements, we will be learning a bunch of dierent things including:

Navigation

Using a Sliding Menu

Using PouchDB to store local data

Using Cloudant to store remote data

Using the Facebook API for authentication and other features

Updating and displaying data in real time

427
and heres some screenshots to help you get an idea of what the application will look like:

428
###Lesson Structure

429
1. Getting Ready

2. Login Page and Sliding Menu Layout

3. Using Facebook for Authentication

4. Creating and Displaying Messages & Navigation

5. Creating a Local and Remote Backend with PouchDB and Cloudant

6. Styling

Ready?

Now that you know what youre in for, lets get to building it!

430
Lesson 2: Getting Ready

In this lesson we are going to prepare our application for the journey ahead. We are going to of course

generate the application, and we are also going to set up all of the components and Cordova plugins we

will need. At the end of this rst lesson we should have a nice skeleton application set up with everything

we need to start diving into coding.

A good rule of thumb before starting any new application is to make sure you have the latest version of

Ionic and Cordova, so if you havent done it recently then make sure to run:

npm install -g ionic@beta cordova

or

sudo npm install -g ionic@beta cordova

before you continue.

Generate a new application

We will be using the blank starter template for this application which, as the name implies, is basically

an empty Ionic project. It comes with one page built in called home which we will repurpose in the next

lesson.

> Run the following command to generate a new application

ionic start camperchat blank --v2

> Make the new project your current working directory by running the following command:

cd camperchat

Your project should now be generated - now you can open up the project folder in your favourite editor.

You can take a look at how your application looks by running the following command:

431
ionic serve

which for now should look something like this:

432
Create the Required Components

This application is going to have a few dierent pages, we are going to be using a login page, the main

home page and an about page. The home page has already been generated for us, so we will just need

to create the login page and the about page.

> Run the following command to generate a Login page:

ionic g page Login

> Run the following command to generate an About page:

ionic g page About

Also remember that any time we generate a new component, we need to import its .scss le into our

app.core.scss le. The Ionic CLI automatically generates pages for us, but it doesnt automatically import

the styles in app.core.scss.

> Modify app.core.scss to import the components styles:

@import "../pages/home/home";
@import "../pages/login/login";
@import "../pages/about/about";

Create the Required Services

As well as our pages, we are also going to create a data service which will handle storing and retrieving

our message data, as well as storing a few values that we will grab from the Facebook API.

> Run the following command to generate a Data provider:

ionic g provider Data

433
Add Required Platforms

Before you can build for certain platforms, you need to add them to your project. This is something we

will be doing way later on in this course, but you may as well just set them up now.

> Run the following command to add the iOS platform to your application

ionic platform add ios

> Run the following command to add the Android platform to your application

ionic platform add android

Install PouchDB

Since we will be using PouchDB in this project, we rst need to install it. You can do that in a similiar way

to how you install Ionic Native, simply run the following install command:

npm install pouchdb --save

We will also need to install typings for PouchDB so that the TypeScript compiler doesnt complain (since

it doesnt know what PouchDB is). If you do not already have the typings package installed, rst run:

npm install -g typings

and then:

typings install --global --save dt~pouchdb dt~pouchdb-adapter-websql


dt~pouchdb-browser dt~pouchdb-core dt~pouchdb-http dt~pouchdb-mapreduce
dt~pouchdb-node dt~pouchdb-replication

to install the PouchDB typings. If you are wondering how you might nd typings for other libraries, you

can run:

typings search [library name]

434
and it will return a table of any matching typings that are available. Then you just install them in this format

(using values from the returned table):

typings install source~name --global --save

Add Required Cordova Plugins

This application will use a few dierent Cordova plugins. Remember, Cordova plugins can only be used

when running on a real device. Ill explain each plugin as we run through the commands for adding them.

> Run the following command to add the SQLite plugin:

ionic plugin add https://github.com/litehelpers/Cordova-sqlite-storage

This plugin gives you access to native storage with an SQLite database. We are adding it to this application

because the Ionic local storage service can make use of this plugin to provide more stable data storage.

> Run the following command to add the In App Browser plugin:

ionic plugin add cordova-plugin-inappbrowser

This plugin makes a webview available to us that we can launch external websites in. We will be using

this plugin in this application as it is a dependency of the Facebook plugin we will be using. Adding the

Facebook plugin itself actually requires a bit more work than just running a simple command, so we will

be doing that later.

> Run the following command to add the Status Bar plugin:

ionic plugin add cordova-plugin-statusbar

We will be adding this plugin to all projects to give us control over the status bar in our application (the bar

at the top of the devices screen that contains the time, battery information and so on).

> Run the following command to add the Splash Screen plugin:

ionic plugin add cordova-plugin-splashscreen

435
This plugin allows us to control the splash screen (the fullscreen graphic that briey displays when you

open an app)

> Run the following command to add the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

This plugin is required for all applications, and helps to dene what resources should be allowed to be

loaded in your application. Without it, resources you try to load will fail.

As well as adding the plugin, you also need to dene a Content Security Policy in your index.html

le. We will be adding a very permissive policy which will essentially allow us to load any resources.

Depending on your application, you may look into providing a more strict policy, but an open policy is

good for development.

> Modify your www/index.html le to include the following meta tag:

<meta http-equiv="Content-Security-Policy" content="font-src 'self' data:;


img-src * data:; default-src * 'unsafe-eval' 'unsafe-inline'">

> Run the following command to add the Crosswalk plugin:

ionic plugin add cordova-plugin-crosswalk-webview

This is another plugin that we will add to every application, but you may decide you want to leave it out.

By adding this plugin, when you build for Android Crosswalk will be used. Android has a lot of issues,

especially with older devices, because there is so many dierent software versions out there and dierent

versions have dierent browsers (remember, since we are building HTML5 applications it is actually a

browser engine powering and running our application). What Crosswalk does is bundle a modern browser

into your application so no matter what device you are running on your app will be powered by the same

browser, and the Crosswalk browser can improve performance a lot.

The only real downside to this is that it increases the size of your application by a signicant amount. In

general, I think its worth it and Id recommend you include it but you may leave it out if you like. For more

information take a look at the Crosswalk Project website: https://crosswalk-project.org/

436
Set up Images

When building this application we are going to be making use of a few images. Ive included these in your

download pack but you will need to set them up in the application you generate.

> Copy the images folder in the download pack for this application from www/images to your own

www folder

Summary

Thats it! Were all set up and ready to go, now we can start working on the interesting stu.

437
Lesson 3: Login Page and Sliding Menu Layout

Were going to implementing a really common page ow in this application, and thats a log in page that

leads to the main application. In pretty much any application that requires users to be authenticated, there

is going to be some authentication screen shown rst, and only once the user successfully logs in will they

be able to access the main application.

In our case we are going to have a login page that will have a login with Facebook button, which will then

lead to a page with a sliding menu layout. Our login page is pretty simple (for now at least) so lets get

started by building that.

> Modify login.html to reect the following:

<ion-content padding>

<ion-row>
<ion-col>
<img src="images/logo.png" />
</ion-col>
</ion-row>

<ion-row>
<ion-col>
<p>Camper Chat allows you to talk to other <strong>caravaners,
RV'ers, roadtrippers and campers</strong> near you. Use
Camper Chat to give and receive tips, ask for help with your
flat tyre, or just have a friendly chat.</p>

<p>Log in with <strong>Facebook</strong> below to get started.</p>


</ion-col>
</ion-row>

438
<ion-row>
<ion-col>
<a (click)="login()"><img src="images/facebook-button.png" /></a>
</ion-col>
</ion-row>

</ion-content>

This denes the template for our login page, and if this isnt the rst application youve built with Ionic 2

(and hopefully it isnt) then you might notice weve left the navbar out of the template. We have no need

to display a title and we dont need to display a back button for navigation or anything like that, so its

perfectly ne to just leave it out.

Were not doing anything too crazy here, but we are using Ionics grid system, which consists of columns

and rows, to set up our layout. When using the grid system with <ion-row> and <ion-col> rows get

placed underneath one another, and cols within those rows appear side by side. This diagram should help

illustrate how that works:

439
This is a very simple example because we are just using the grid to display three sections underneath

each other, but you can also include multiple columns within rows and even dene how wide they are like

this:

<ion-row>
<ion-col width-10></ion-col>
<ion-col width-50></ion-col>
</ion-row>

Weve also attached a (click) handler to a Facebook login button image. Eventually we will have this

login function trigger the authentication with Facebook, but for now we are just going to have it trigger
a page change to the main page so that we can nish setting up our layout. Lets set up the class for this

component now so that we can set up this page transition.

> Modify login.ts to reect the following:

440
import {Component} from '@angular/core';
import {Platform, NavController, MenuController, AlertController} from
'ionic-angular';
import {Facebook} from 'ionic-native';
import {HomePage} from '../home/home';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/login/login.html',
})
export class LoginPage {

constructor(public nav: NavController, public platform: Platform, public


menu: MenuController, public dataService: Data, public alertCtrl:
AlertController) {
this.menu.enable(false);
}

login(): void {
this.getProfile();
}

getProfile(): void {
this.menu.enable(true);
this.nav.setRoot(HomePage);
}

As you can see in the code above, weve injected and set up our NavController and were using it to set

441
the rootPage to be the HomePage when the login function is called. This code is being run through

the getProfile() function because later we will be grabbing some information about the user from

Facebook after they have authenticated, but before we send them to the next page, but since we havent

implemented that yet we just want the code to ow right through to the success scenario.

The MenuController is used to programmatically interact with the sliding menu we will be adding to the

application. We will discuss that in more detail in a moment, but we dont want the menu to be used on

the login page so we use the enable function on the menu to disable it for this page. When we switch to

our Home Page we enable it again.

Weve also imported the Facebook plugin from Ionic Native which we will use later. Remember that to use

a a Cordova plugin you do not need to import anything, not even for the Facebook plugin, but if you want

to use it through Ionic Native then you will need to import it.

We want this login page to display rst, but if you take a look at the root component in app.ts you will see

that the home page is currently being set as the root page:

rootPage: any = HomePage;

So of course we want to change that, and we are also going to need to import a few things into our root

component to use later.

> Modify app.ts to reect the following:

import {Component, ViewChild} from "@angular/core";


import {Facebook} from 'ionic-native';
import {Nav, Platform, MenuController, ionicBootstrap} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import {AboutPage} from './pages/about/about';
import {LoginPage} from './pages/login/login';
import {Data} from './providers/data/data';

@Component({

442
templateUrl: 'build/app.html'
})
export class MyApp {

@ViewChild(Nav) nav: Nav;

rootPage: any = LoginPage;


homePage: any = HomePage;
aboutPage: any = AboutPage;

constructor(public platform: Platform, public dataService: Data, public


menu: MenuController) {

platform.ready().then(() => {

});

openPage(page): void {

logout(): void {

ionicBootstrap(MyApp, [Data]);

443
As you can see we have set the root page to be the Login Page and of course we need to import the Login

Page as well. Weve also done quite a bit more than that though. Were importing and setting up references

to our Data provider, which we will use for saving and accessing data later, the Menu Controller, which will

allow us to interact with the sliding menu we are creating. Also notice the use of @ViewChild here, this

allows us to grab a direct reference to our Nav component (the <ion-nav> that we will be adding in the

next step). This is necessary because you cant use the NavController inside of the root component, but

we need to be able to change the rootPage from here later so we instead grab the Nav directly.

Weve declared our data provider in the ionicBootstrap function, and have set up a couple of functions

that we will nish dening later. The openPage function will handle switching between dierent selections

from the sliding menu (which will be tied to the this.homePage and this.aboutPage variables we

have set up here), and the logout function will handle logging the user out of the application. Weve

imported Facebook from Ionic Native so that the logout function can interact with the Facebook API.

Also notice that we are using a templateUrl in the decorator which links to an app.html le. A lot of the

time we dont worry about creating a separate template le for the root component because it is so simple

(usually just the nav). Since we are using a sliding menu we are going to be dening the template for the

menu in its own app.html le, since it is going to be a bit bulkier than usual. Lets create that now.

> Create a le called app.html inside of your app folder and add the following code:

<ion-menu [content]="content">

<ion-content>
<ion-list no-lines>
<button ion-item (click)="openPage(homePage)">
<ion-icon name="chatbubbles"></ion-icon> Chat
</button>
<button ion-item (click)="openPage(aboutPage)">
<ion-icon name="information-circle"></ion-icon> About
</button>
<button ion-item (click)="logout()">

444
<ion-icon name="power"></ion-icon> Logout
</button>
</ion-list>
</ion-content>
</ion-menu>

<ion-nav id="nav" #content [root]="rootPage"></ion-nav>

Theres some really interesting stu going on here. This part of the code above will likely look pretty familiar

to you:

<ion-nav id="nav" #content [root]="rootPage"></ion-nav>

Except for one bit, and thats #content. The # syntax is used to dene local variables in the template, so

what we are doing here is assigning the <ion-nav> component to the local variable content. We then

use that variable in our <ion-menu> to set its content property:

<ion-menu [content]="content">

So what were doing here is essentially telling the menu what content it should attach itself to, which in

this case is our entire application. Everything else inside of <ion-menu> is just the content that the menu

will contain, and all were doing is creating a list of buttons that will serve as tabs for the user to click on.

Each of these tabs will call the functions that we created in our root component.

The nal piece of this layout puzzle is to create the home page, lets start o by dening the template.

> Modify home.html to reect the following:

<ion-header>
<ion-navbar primary>
<button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>

445
<img src = "images/logo.png" class="logo" />
</ion-title>
</ion-navbar>
</ion-header>

<ion-content class="home" #chat id="chat">

<ion-list no-lines>

<ion-item>
<ion-avatar item-left>
<img src="">
</ion-avatar>
<h2>Name here</h2>
<p>message here</p>
</ion-item>

</ion-list>

</ion-content>

<ion-footer>
<ion-toolbar>
<ion-input [(ngModel)]="chatMessage" type="text" placeholder="enter
message..."></ion-input>
<button (click)="sendMessage()" style="position:absolute; right: 0;
top: 0; margin: 0;"><ion-icon
name="chatbubbles"></ion-icon></button>
</ion-toolbar>
</ion-footer>

446
We have our navbar added back in this template and weve added a button with a menuToggle attribute

which will add a button to allow the user toggle the menu on and o (the menu can also be opened and

closed by swiping, which is why we needed to disable it on the login page even though we didnt add a

menu button).

Notice that we have created a local variable using #chat which is the <ion-content>, this is because

we are going to need to grab a reference to it later using @ViewChild just as I mentioned we would be

doing for the navigation component.

Inside of the content area we create a list with just a single <ion-item> for now (later we will use *ngFor

to display all of the available messages), and were also using <ion-avatar> which allows us to attach a

nicely styled picture to the item. Eventually we will also add the name of the user who posted the message,

and their message here as well.

Were also using an <ion-toolbar> which is pretty much the same as the navbar minus all the navigation

magic. Its a bar that we can add a title, buttons or whatever else we want to it, and in this case we are

aligning it to the bottom of the screen instead of the top. This allows us to have an area for our message

input which will stay stuck at the bottom of the screen even as the list scrolls.

Inside of the toolbar weve added an input eld which is has two way data binding set up on chatMessage

by using [(ngModel)]. This means that when this value changes, the value for this.chatMessage in

home.ts (which we are about to create) will also be updated (and vice versa). Then we just add a button

which will eventually send the message that is entered, and weve added a bit of inline styling to make it

oat to the right side of the toolbar.

Now lets create the class denition for our home.ts le.

> Modify home.ts to reect the following:

import {Component} from '@angular/core';


import {AlertController} from 'ionic-angular';
import {Facebook} from 'ionic-native';
import {LoginPage} from '../../pages/login/login';
import {Data} from '../../providers/data/data';

447
@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

chatMessage: string = '';


messages: any = [];

constructor(public dataService: Data, public alertCtrl: AlertController){

sendMessage(): void {

}
}

Pretty simple stu here, were importing and injecting our data service again and were also importing Face-

book from Ionic Native. We set up the this.chatMessage variable that our input eld from the template

links to and we also dene an empty array of messages which will eventually contain the messages to be

displayed on screen.

If you run your application using:

ionic serve

you should have two pages now that looks like this:

448
449
As is usually the case, it looks pretty ugly right now but the basic structure for the layout we need is there.

We have a login page with a login option, which leads to the main page with a sliding menu.

450
In the next lesson were going to start integrating the Facebook API so that we can have users logging in

for real (which will also provide us some juicy data to use in the application like the users name and prole

picture).

451
Lesson 4: Using Facebook for Authentication

In this lesson well be integrating the Facebook plugin from Ionic Native which will allow us to authenticate

users in our application, and it will also allow us to do a few other things like grabbing the users prole

information. Theres a ton more you can do with the Facebook API that I wont be covering though, so

make sure to check out the documentation.

Using social authentication like the Facebook API provides has a lot of advantages. It saves us from having

to create our own backend with an authentication system (which would involve creating a database, storing

user credentials securely, handling password resets and so on), it saves the user from having to create

another new account to use the application (they can just log in with one tap), and it provides a bunch of

useful data and integrations. This isnt to say that social login is always the best authentication method,

but it certainly has its advantages.

Lets walk through how to set up the Facebook plugin and then we can look at integrating into our appli-

cation.

IMPORTANT: The Facebook connect plugin will only work when running on a real mobile device, if you try

to run it through your desktop browser you will just receive errors. Instead of testing by running ionic

serve, you should instead use ionic run ios or ionic run android. If you are unsure how to get
an application on your device, make sure to read the Testing and Debugging section of this book

Setting up a Facebook App

As I mentioned in the previous lesson, this plugin is a little trickier than just running the normal plugin install

command. We are going to need to supply the plugin with an App Name and an App ID when we install

it. To do that, we need to create an application on Facebooks developers platform, so lets walk through

doing that.

First you will need to go to developers.facebook.com and sign up if you havent already. Once youve done

that you should see a screen like this:

452
Click on My Apps and then Add a New App. Select the iOS platform, and then on the next page just

click the Skip and Create App ID button in the top right corner. Fill in a Display Name (the name of your

application, which for now we will assume is CamperChat) and choose a category. Then click Create

App ID.

453
Now you should nd yourself on a screen like this:

454
and you may notice a eld there with an App ID, make note of that because we will be using it shortly.

Weve still got a bit more setting up to do rst though. Click on the Settings tab on the left, on this screen

we are going to add the platforms we are using which will be iOS and Android.

We can add the iOS platform now, but the Android platform requires a key hash before it will allow you

to save it. So we are going to leave that for now, but make sure to come back and add it later because

your Android version will not work without it. We will be covering how to create a keystore le and a key

hash for Android in the build chapters later on, so if you do want to do it right now feel free to skip ahead

to the Signing Android Applications section.

Click the Add Platform button and choose iOS. You will need to add a Bundle ID which should be in the

format of com.yourname.yourproject or com.yourcompany.yourproject or something similar.

You should also make sure to switch Single Sign On to Yes.

455
Youll need to make sure the Bundle ID you supply matches the one in the cong.xml le in your Ionic

project, so make sure the following line in your cong.xml le matches your Facebook Apps Bundle ID:

<widget id="com.yourname.yourproject" version="0.0.1"


xmlns="http://www.w3.org/ns/widgets"
xmlns:cdv="http://cordova.apache.org/ns/1.0">

You will also need to supply the same Bundle ID when you add the Android platform to your Facebook app

later as well (it should be added under Google Play Package Name).

Thats all there is to it for now, with our App Name and App ID in hand we are ready to install the plugin in

our application.

IMPORTANT: When your app is ready to go live, remember to switch the Facebook application out of

development mode by going to the App Review tab and switching Make Your App public? to Yes

Installing the Facebook Connect Plugin

To install the plugin in the application you will need to run the following command from your project direc-

tory:

ionic plugin add cordova-plugin-facebook4 --save --variable


APP_ID="123456789" --variable APP_NAME="myApplication"

making sure to replace the APP_ID and APP_NAME values with the ones from your own Facebook appli-

cation that you just created.

IMPORTANT: There is currently a build issue with this plugin on Android. This may or may not eect you

depending on which version you are using, but if when building for Android later the build fails you will

have to change the following line in platforms/android/project.properties:

cordova.system.library.1=com.facebook.android:facebook-android-sdk:4.4.0

456
Setting up Authentication

Weve got everything we need set up now, so we can jump into some more coding in our application.

Integrating the Facebook API with Ionic Native is actually really easy, but before we do that we are going

to set up our data service so that it can store some values from Facebook temporarily. The data service

will primarily be used for handling storing and retrieving our message data later, but since we just need to

store a couple of values from Facebook its easy to just tack them on here.

> Modify data.ts to reect the following:

import {Injectable} from '@angular/core';

@Injectable()
export class Data {

fbid: number;
username: string;
picture: string;

constructor() {

Now were going to modify our login page to authenticate with Facebook when the user clicks the login

button, rather than just going straight to the home page.

> Modify the login function in login.ts to reect the following:

login(): void {

Facebook.login(['public_profile']).then((response) => {

457
this.getProfile();

}, (err) => {

let alert = this.alertCtrl.create({


title: 'Oops!',
subTitle: 'Something went wrong, please try again later.',
buttons: ['Ok']
});

alert.present();

});

Since we have already imported Facebook from Ionic Native we can access the Facebook object now.

We call the login method and pass in an array of permissions we require. Were only after basic user infor-

mation so we only request permission for the users public prole, but there are many more permissions

you can request such as:

User friends

User interests

User bio

User location

Users tagged places

and a whole lot more, take a look at this page for a full list. The login method returns a promise, so we

set up a handler for the response and if it is successful we trigger the getProfile function we set up

previously which will launch the home page. If it is not successful then we create and display an alert to

458
the user.

We now know that a user has successfully authenticated with Facebook and that they should have access

to our application, but we still need to nd out a few details about them. In order for them to use the

application we will also need their name and prole picture, so to grab this we are going to modify the

getProfile function.

> Modify the getProfile function in login.ts to reect the following:

getProfile(): void {

Facebook.api('/me?fields=id,name,picture', ['public_profile']).then(

(response) => {

console.log(response);

this.dataService.fbid = response.id;
this.dataService.username = response.name;
this.dataService.picture = response.picture.data.url;

this.menu.enable(true);
this.nav.setRoot(HomePage);

},

(err) => {

console.log(err);

let alert = this.alertCtrl.create({


title: 'Oops!',

459
subTitle: 'Something went wrong, please try again later.',
buttons: ['Ok']
});

alert.present();

);

Now we are making a call to the api method that the Facebook plugin provides. This allows us to interact

with Facebooks Graph API which is how you do just about everything with the Facebook API - you can get

into some really complex stu but we just want to use it to grab the users id, full name and display picture.

The rst parameter that needs to be provided to the api call is the endpoint you want to hit, which for

us is /me because we want data about the currently logged in user, and we add a query string containing

all the data elds we want returned. We also provide an array of permissions we require again and then

handle the response.

If the response is successful we store those values we retrieved using our dataService, then we enable the

sliding menu and switch to the home page. If the response is not successful then we again show the user

an error message and keep them on the login page.

The only other bit of functionality we need to use from the Facebook API is the logout method. When a

user logs out of our application we want to terminate that session with Facebook as well (this wont log

them out of Facebook, it just means that they will need to reauthenticate with Facebook when using our

application again). To do that we are going to modify our own logout function.

> Modify the logout function in app.ts to reect the following:

logout(): void {

460
this.menu.close();
this.menu.enable(false);

this.nav.setRoot(LoginPage);

this.dataService.fbid = null;
this.dataService.username = null;
this.dataService.picture = null;

Facebook.logout();
}

First we handle stu on our end. We close the menu and disable it, and then switch back to the login page.

We reset all the Facebook data that was stored in our data service and then we call the logout method on

the Facebook API. The user would now be back on the login page in our application and unauthenticated.

Since there is a bit of waiting time in the application whilst the user is being authenticated (like when we

fetch the users prole) we want to indicate to the user that something is happening, and that the app isnt

just frozen. To do this we will use Ionics LoadingController service. This will allow us to stop interaction

with the application for a period of time.

> Modify login.ts to reect the following:

import {Component} from '@angular/core';


import {Platform, NavController, MenuController, AlertController,
LoadingController} from 'ionic-angular';
import {Facebook} from 'ionic-native';
import {HomePage} from '../home/home';
import {Data} from '../../providers/data/data';

@Component({

461
templateUrl: 'build/pages/login/login.html',
})
export class LoginPage {

loading: any;

constructor(public nav: NavController, public platform: Platform, public


menu: MenuController, public dataService: Data, public alertCtrl:
AlertController, public loadingCtrl: LoadingController) {

this.loading = this.loadingCtrl.create({
content: 'Authenticating...'
});

this.menu.enable(false);
}

login(): void {

this.loading.present();

Facebook.login(['public_profile']).then((response) => {

this.getProfile();

}, (err) => {

let alert = this.alertCtrl.create({


title: 'Oops!',
subTitle: 'Something went wrong, please try again later.',

462
buttons: ['Ok']
});

this.loading.dismiss();
alert.present();

});

getProfile(): void {

Facebook.api('/me?fields=id,name,picture', ['public_profile']).then(

(response) => {

console.log(response);

this.dataService.fbid = response.id;
this.dataService.username = response.name;
this.dataService.picture = response.picture.data.url;

this.menu.enable(true);
this.loading.dismiss();
this.nav.setRoot(HomePage);

},

(err) => {

463
console.log(err);

let alert = this.alertCtrl.create({


title: 'Oops!',
subTitle: 'Something went wrong, please try again later.',
buttons: ['Ok']
});

this.loading.dismiss();
alert.present();

);

Notice that we are now importing the LoadingController service and we set up an Authenticating

message in the constructor. We then to present the loading message (just like we would with an Alert or

a Modal) when the login method is called. Then we dismiss the loading message when either the users

prole information has been successfully retrieved, or when the login process fails.

Thats all there is to the Facebook integration in this application, but theres a ton more you can do with

the Facebook API. Id recommend looking at the plugin documentation and the Facebook Graph API to

get more of a sense of what is possible with Facebook.

In the next lesson, well be looking at how we can add some messages to our chat screen!

464
Lesson 5: Creating Messages & Navigation

This is obviously one of the most critical bits of functionality in our application, but its going to be pretty

straightforward to implement. The hard part will be the integration with PouchDB and Cloudant which we

will tackle later, for now we just want to create the ability for the logged in user to add messages to the

screen.

We will also be tieing up a few loose ends with our navigation - we have a menu with some options that

do nothing and an about page that has nothing on it.

Adding Messages

Weve already set up an input eld tied to the this.chatMessage variable in our home page class

denition and a button that triggers the sendMessage function, so all we have to do to enable creating a

message is to dene that sendMessage function. Well also have to modify the template to loop through

and display all of the available messages.

> Modify the sendMessage function in home.ts to reect the following:

sendMessage(): void {

let message = {
'_id': new Date().toJSON(),
'fbid': this.dataService.fbid,
'username': this.dataService.username,
'picture': this.dataService.picture,
'message': this.chatMessage
};

this.messages.push(message);
this.chatMessage = '';

465
}

All we are doing here is creating a message object with a few dierent properties. We add all of the

information we just recently grabbed from Facebook, along with the message the user entered. We also

add an id eld which is for the PouchDB & Cloudant integration well be working on in the next lesson,

so just ignore that for now.

Once weve created the message we just push it directly to the messages array and reset the chat message

input eld (so that the input eld is ready for the user to type their next message). We will be modifying

this in the next lesson so that the message is sent to our remote backend, not just pushed directly to the

messages array (nobody apart from the user who created it would see it otherwise!).

Now that we have the ability to push some data into our messages array, lets modify the template to

display it.

> Modify the list in home.html** to reect the following:**

<ion-list no-lines>

<ion-item *ngFor="let message of messages">


<ion-avatar item-left>
<img [src]="message.picture">
</ion-avatar>
<div>
<h2>{{message.username}}</h2>
<p>{{message.message}}</p>
</div>
</ion-item>

</ion-list>

Now were looping through and displaying all of our messages in their own <ion-item>. Remember that

the syntax is a shortcut for creating embedded templates, and ngFor will create an embedded template

466
for every message in messages and stamp it out with that specic messages values. Using the data that

is attached to each message we are now adding the users display picture and name to the message, along

with the message itself of course.

If you run the application now, you should be able to add some test messages to the application and see

them displayed (remember, you must test this on a device because Facebook login will not work through

ionic serve):

467
###Creating the About Page

Weve got our sliding menu set up, but the only button that actually works now is the Logout button. We

468
still need to create our about page and link to it, as well as linking the Chat button back to the main chat

screen. Lets start by implementing the about page.

> Modify about.html to reect the following:

<ion-header>
<ion-navbar primary>
<button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>
<img src = "images/logo.png" class="logo" />
</ion-title>
</ion-navbar>
</ion-header>

<ion-content padding class="about">


<p>Camper Chat allows you to talk to other <strong>caravaners, RV'ers,
roadtrippers and campers</strong> near you. Use Camper Chat to give
and receive tips, ask for help with your flat tyre, or just have a
friendly chat.</p>
</ion-content>

This is a really simple page and theres not really anything exciting here. Weve implemented the menuTo-

ggle button just as we did on the home page, and weve just added a bit of descriptive content.

Now we just need to set up the click handlers for the about page and the chat page in the menu. If you

take a look in app.html you will see that we have both of these buttons calling the openPage function:

<button ion-item (click)="openPage(homePage)">


<ion-icon name="chatbubbles"> Chat</ion-icon>
</button>
<button ion-item (click)="openPage(aboutPage)">

469
<ion-icon name="information-circle"> About</ion-icon>
</button>

one passes in a homePage parameter and the other aboutPage. Were going to set up our openPage

function now to handle this.

> Modify the openPage function in app.ts to reect the following:

openPage(page): void {
this.menu.close();
this.nav.setRoot(page);
}

First we close the menu since the user has made a selection (it would be annoying if the menu stayed

open) and then we use our nav component to change the root page. We set it to the page parameter that

was passed in, and since we have already set up homePage and aboutPagereferences:

homePage: any = HomePage;


aboutPage: any = AboutPage;

the appropriate page will be switched to when the function is called. Now the user can switch back and

forth freely between the about page and the chat page, and then they can also log out to go back to the

log in page.

Weve now got all the basic functionality of the application set up, the last big thing we need to tackle is

the integration of PouchDB and Cloudant, which we will be doing in the next lesson, and then we just need

to tidy things up and make it look pretty.

470
Lesson 6: Local and Remote Backend with PouchDB and Cloudant

Although weve already created most of the functionality for the application, this lesson is going to be the

biggest and hardest of them all. Rather than pushing the users message straight to the messages array,

we are going to store the message using PouchDB.

PouchDB is an in browser NoSQL database that was inspired by the CouchDB project. Its biggest feature

is that it allows for data storage oine, and it can automatically sync to a remote database when the

application comes back online. Like using the SqlStorage service that Ionic provides, using PouchDB will

ensure that your local data wont be randomly wiped out like it can be when using plain local storage.

Teaching NoSQL is far beyond the aims of this book, but to give you a really quick background, NoSQL

databases usually store data in a JSON like key value style format, instead of the relational style tables

that traditional SQL uses. The two approaches are completely dierent, the way you use them is dierent

and the way you need to think about them is dierent. So when it comes to using NoSQL you have to drop

a lot of the preconceived notions you would have from SQL (assuming youve used that in the past). The

data were storing is super simple though, so we dont need to worry about learning how to use a NoSQL

database properly.

So PouchDB will handle storing our data locally, but we are also going to be using Cloudant to create a

remote database. We will be syncing the local PouchDB database to the remote Cloudant database, so

that whenever the application is online it can fetch new data from Cloudant, and any new local data will

also be pushed to Cloudant. Fortunately, these two tools are designed to work with each other and setting

up remote syncing, which is usually a very complex task, is super simple.

Cloudant is a DBaaS (Database as a Service) so we are going to have to create an account (which is free

for low usage) to use it, as well as set up the database. Using something like Cloudant is really easy and

scales very well (since all the backend architecture is handled for you), but if you prefer you can easily use

something like CouchDB or Couchbase installed on your own server to sync with PouchDB. I wont be

covering how to set those up, but its not all that dierent.

471
Creating a Cloudant Database

Before we start jumping into the code, were going to set up our backend on Cloudant through IBM Bluemix.

Bluemix gives you access to IBMs Open Cloud Architecture and can be used to create, deploy and manage

cloud based applications. It provides a bunch of services you can use and one of those is Cloudant.

First you will need to create an IBM Bluemix account. Once you have created your account and logged in,

you should see a screen like this:

Choose the Create App option, choose Mobile, name your application and then click Finish. After that

you should see a screen like this:

472
Choose Cloudant NoSQL DB from the Services menu on the left and then click View your Data on the

Cloudant Dashboard. You should now be inside of the Cloudant dashboard. Click the Create Database

option in the top right to create a new database and call it camperchat or whatever you prefer.

Now select the newly created database to view more details, you should see a screen like this:

Click the API link in the top right to get a reference to your Cloudant Database URL, this is what we will

supply to PouchDB later so make a note of it. You should also go to the Permissions section and generate

an API key (making sure to give it Write and Replicate permissions) this can be used with PouchDB to

access your database, and its a better idea to use an API key which is revokable rather than the actual

username and password for your account. Make sure to take note of the password because once you

leave the screen you wont be able to nd it again.

Theres just one more thing we need to do in here. We need to enable CORS (Cross Origin Resource

473
Sharing) so that we are able to make requests to the database from our application. To do that go to

Account in the left menu, select CORS and then choose the All Domains (*) options.

You should now have everything you need set up on Cloudant, ready to be used with PouchDB.

Integrating PouchDB

Earlier on we created a Data provider which so far we are only using to store a few values from the Facebook

API. Now were going to extend that Data service to handle storing and retrieving data with PouchDB.

Lets get the basics set up rst and talk through it.

> Modify data.ts to reect the following:

import {Injectable} from '@angular/core';


import * as PouchDB from 'pouchdb';

@Injectable()
export class Data {

fbid: number;
username: string;
picture: string;
db: any;
data: any;
cloudantUsername: string;
cloudantPassword: string;
remote: string;

constructor(){

this.db = new PouchDB('camperchat');

474
this.cloudantUsername = 'YourAPIUsernameHere';
this.cloudantPassword = 'YourAPIPasswordHere';
this.remote = 'https://YOUR-URL-HERE-bluemix.cloudant.com/camperchat';

//Set up PouchDB
let options = {
live: true,
retry: true,
continuous: true,
auth: {
username: this.cloudantUsername,
password: this.cloudantPassword
}
};

this.db.sync(this.remote, options);

addDocument(message){

getDocuments(){

handleChange(change){

475
}

Weve already installed PouchDB with npm so to make it available in this service we have just added the

following line:

import * as PouchDB from 'pouchdb';

In the constructor we are just handling the set up of PouchDB and the sync to the remote Cloudant

database. First we create a new PouchDB, or get a reference to an already existing one:

this.db = new PouchDB('camperchat');

and then we dene a few variables which will be used to connect to the Cloudant database:

this.cloudantUsername = 'YourAPIUsernameHere';
this.cloudantPassword = 'YourAPIPasswordHere';
this.remote = 'https://YOUR-URL-HERE-bluemix.cloudant.com/camperchat';

Make sure to replace these values with your own from the Cloudant dashboard. Next we create an options

object to congure our connection to the Cloudant database and then we call the sync method:

this.db.sync(this.remote, options);

This will set up replication from our PouchDB database to the Cloudant database, and it will also set up

replication from the Cloudant database to the PouchDB database. So now if we add some data to our

PouchDB database it will automatically be reected in the remote Cloudant database, and if we change

or add some data in the remote Cloudant database it will automatically be reected in our local database.

After that we have just created three empty functions, which we are going to go through implementing

now.

addDocument

> Modify the addDocument function to reect the following:

476
addDocument(message) {
this.db.put(message);
}

To add a document (a data object in NoSQL terms is called a document, so think of a document as

a bit of data, not something like a Word document) to our PouchDB database we simply call put on our

database. So we will be able to pass this function any object and it will add it to the database.

getDocuments

> Modify the getDocuments function to reect the following:

getDocuments(){

return new Promise(resolve => {

this.db.allDocs({

include_docs: true,
limit: 30,
descending: true

}).then((result) => {

this.data = [];

let docs = result.rows.map((row) => {


this.data.push(row.doc);
});

this.data.reverse();

477
resolve(this.data);

this.db.changes({live: true, since: 'now', include_docs:


true}).on('change', (change) => {
this.handleChange(change);
});

}).catch((error) => {

console.log(error);

});

});

This function handles retrieving all of the documents from our database (remember, a document is just

what a data object is called). This is an asynchronous operation, so we wrap it in a promise which resolves

when the data is returned. This will allow us to use the getDocuments().then() syntax elsewhere in

the application. By calling allDocs every document will be retrieved, but we are supplying a few options

here:

include_docs: true,
limit: 30,
descending: true

The include_docs option here may seem a little confusing, but we need to specify this so that all the

data in our documents are returned (message, picture etc.). If this option is left out then only the id of

the document is returned. We also set a limit of 30 and a descending order so that only the 30 latest

478
documents (chat messages) are returned.

We pass the result of that operation through to our handler, and for each row that is returned we push it

into a this.data array. We want these results to be in reverse order though because we want to show

the latest message at the bottom so we ip the array with reverse. After this we resolve the promise we

created and pass back the data, and then we set up a changes listener.

The changes listener will call the this.handleChange function every time a change is detected in the

database (when another user has added a chat message for example), and the change itself will be passed

into that function. Well dene that now.

handleChange

> Modify the handleChange function to reect the following:

handleChange(change) {

let changedDoc = null;


let changedIndex = null;

this.data.forEach((doc, index) => {

if(doc._id === change.id){


changedDoc = doc;
changedIndex = index;
}

});

//A document was deleted


if(change.deleted){
this.data.splice(changedIndex, 1);

479
}
else {

//A document was updated


if(changedDoc){
this.data[changedIndex] = change.doc;
}

//A document was added


else {
this.data.push(change.doc);
}

What we want to do with the change that is passed in is reect it in our this.data array, but it gets a

little bit tricky. The change object that is sent back could either be a document that has been updated, a

new document, or a deleted document.

Detecting a deleted document is easy enough because it will contain the deleted property. But to see if

it is an update, we need to check if we already have a document with the same id (and update that one if

we nd it), if we dont then we know it is a new document (and need to add it to the array).

We have our Data provider completely set up now, but theres still a couple more things we need to do

to make use of it. We will need to store new data using the provider, rather than adding it directly to the

messages array like we are doing now, and we will also need to load in the latest messages data when the

application is opened. Lets do that now.

> Modify home.ts to reect the following:

480
import {AlertController} from 'ionic-angular';
import {Component, ViewChild} from '@angular/core';
import {Facebook} from 'ionic-native';
import {LoginPage} from '../../pages/login/login';
import {Data} from '../../providers/data/data';

@Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

@ViewChild('chat') chat: any;

chatMessage: string = '';


messages: any = [];

constructor(public dataService: Data, public alertCtrl: AlertController){

this.dataService.getDocuments().then((data) => {

this.messages = data;
this.chat.scrollTo(0, 99999, 0);

});

sendMessage(): void {

let message = {

481
'_id': new Date().toJSON(),
'fbid': this.dataService.fbid,
'username': this.dataService.username,
'picture': this.dataService.picture,
'message': this.chatMessage
};

this.dataService.addDocument(message);
this.chatMessage = '';

In the constructor we are now calling the getDocuments() function to load in the messages data, and

once we do we grab a reference to the scroll content using @ViewChild and scroll it to the bottom.

Finally, in the sendMessage function the only change weve made is to call the addDocument function

rather than pushing the message to the messages array.

Thats it! You should now have a fully functional chat application. It works, but its still pretty ugly. In the

next lesson we are going to make it look prettier and even set up some animations!

482
Lesson 7: Styling & Animations

In this lesson were going to modify our templates a bit to add some classes and we will create some

custom styles, as well as overwriting some of the app wide SASS variables. If youve completed some of

the other applications then youll probably know that theres usually not too much work to be done in these

lessons, and this one isnt that dierent, but we will be doing something a little bit dierent by including

some custom animations.

Animations, when done right, can do a lot to help make your application look and feel more high quality.

When done poorly they can look tacky and can also cause a big performance hit.

Basic Styling

Lets start o by adding some basic styling throughout the application to make it look a little nicer. First

were going to modify the variables le to make some application wide changes.

> Modify app.variables.scss to reect the following:

$colors: (
primary: #5b91da,
secondary: #32db64,
danger: #f53d3d,
light: #f4f4f4,
dark: #222,
favorite: #69BB7B
);

$list-ios-border-color: #fff;
$list-md-border-color: #fff;

$button-ios-border-radius: 0px;

483
Were not doing a whole lot here, just dening the colours we want to use, setting the default border colour

for Android and iOS and also setting the border radius for buttons on iOS to 0px (buttons on Android are

already square).

We also want to dene a few application wide styles in the core style le as well.

> Modify app.core.scss to reect the following:

@import "../pages/home/home";
@import "../pages/login/login";
@import "../pages/about/about";

.logo {
max-height: 39px;
margin-top: 6px;
}

ion-menu ion-content {
background-image: url('../../images/login-background.png');
background-size: cover;
background-position-x: 40%;
}

ion-menu scroll-content {
margin-top: 44px;
}

ion-menu .item {
background-color: transparent;
color: #fff;
}

484
Were modifying the position of the logo a bit, setting a background image that will cover the whole login

page, we add a margin to the menu so that the content doesnt overlap the status bar when running on a

device, and we make the menus items transparent.

Next we are going to modify our login page and home page to include some custom styling.

> Modify home.scss to reect the following:

.home ion-label {
white-space: normal;
}

.home ion-row {
padding: 0;
margin: 0;
}

.home ion-col {
padding: 0;
margin: 0;
}

.home toolbar-content {
padding: 0;
margin: 0;
}

Nothing exciting going on here, were just modifying the positioning of some of the layout elements so they

sit more nicely.

> Modify login.html to reect the following:

<ion-content padding class="login">

485
<ion-row>
<ion-col>
<img src="images/logo.png" class = "login-logo"/>
</ion-col>
</ion-row>

<ion-row class="login-description">
<ion-col>
<p>Camper Chat allows you to talk to other <strong>caravaners,
RV'ers, roadtrippers and campers</strong> near you. Use
Camper Chat to give and receive tips, ask for help with your
flat tyre, or just have a friendly chat.</p>

<p>Log in with <strong>Facebook</strong> below to get started.</p>


</ion-col>
</ion-row>

<ion-row>
<ion-col>
<a (click)="login()"><img src="images/facebook-button.png"
class="login-button" /></a>
</ion-col>
</ion-row>

</ion-content>

> Modify login.scss to reect the following:

.login {
background-image: url('../../images/login-background.png');

486
background-size: cover;
background-position-x: 40%;
text-align: center;
}

.login-description {
height: 60%;
line-height: 2em;
}

.login-description p {
color: #fff;
}

.login-button {
-webkit-box-shadow: 0px 6px 20px 0px rgba(0,0,0,0.18);
-moz-box-shadow: 0px 6px 20px 0px rgba(0,0,0,0.18);
box-shadow: 0px 6px 20px 0px rgba(0,0,0,0.18);
}

Weve had to add a couple of extra classes to the login pages template this time. In the .scss le we are

again setting a background image (the same one used on the login screen) but this time it will be applied

to the menu. We position the description content and also add a bit of a shadow to the Facebook login

button.

Thats it for the basic styling, the pages in your application should now look something like this:

487
488
We still have the most fun bit left to do though, and thats adding a couple of animations.

489
Creating Animations

One important thing to remember when animating elements is to use the translate3d property (even though

we are only animating through 2D space). By using this property rather than animating the left property

for example, the devices GPU (Graphics Processor Unit) is invoked. This is called a hardware accelerated

animation and is a lot smoother. You may nd that animations that run ne on your desktop browser dont

work so well on mobile devices, so its usually important to use hardware acceleration (although relying on

the GPU too much can also be disadvantageous, as it can lead to battery drain).

To create our animations we are going to dene our own keyframes. Basically, this allows you to specify

what certain properties should be at a specic stage during an animation. For example:

@-webkit-keyframes slideInSmooth {
0% {
-webkit-transform: translate3d(-100%,0,0);
}
100% {
-webkit-transform: translate3d(0,0,0);
}
}

What we are saying here is that at the start of the animation (0%) the element should be displaced 100%

to the left (the three parameters in translate3d represent the x, y, and z axis), so it will be just o screen.

Then at the end of the animation (100%) the element should be back to its normal starting position.

We could also dene a similar animation like this:

@-webkit-keyframes slideInSmooth {
0% {
-webkit-transform: translate3d(-100%,0,0);
}

50% {

490
-webkit-transform: translate3d(50%,0,0);
}

100% {
-webkit-transform: translate3d(0,0,0);
}
}

Now the element would start o to the left, then go to the right and then nally get back to its normal

position. You can dene as many of these intermediate steps as you like, the only important thing is that

you always have a 0% and 100% (alternatively, you can use from and to) otherwise the animation will be

invalid.

We can attach these animations to any element simply by creating a class that uses the animation and

attaching the class to any element. We arent going to be using these animations, but we will create some

similar ones to animate in both the users display picture and their message.

> Modify home.scss to include the following animations:

@-webkit-keyframes animateInPrimary {
0% {
-webkit-transform: translate3d(-100%,0,0);
}

100% {
-webkit-transform: translate3d(0,0,0);
}
}

@-webkit-keyframes animateInSecondary{

0% {

491
opacity: 0;
}

50% {
opacity: 0;
}

100% {
opacity: 1;
}
}

.animate-in-primary {
-webkit-animation: animateInPrimary;
animation: animateInPrimary;
-webkit-animation-duration: 750ms;
animation-duraton: 750ms;
}

.animate-in-secondary {
-webkit-animation: animateInSecondary ease-in 1;
animation: animateInSecondary ease-in 1;
-webkit-animation-duration: 750ms;
animation-duraton: 750ms;
}

Weve created two dierent animations here. The animateInPrimary animation will make the element start

o outside of the left bounds of the screen, and will slide in to its normal position evenly over the course

of the animation. The animateInSecondary animation will start fading in from being fully transparent to

full opaque halfway through the animation.

492
We assign these animations to two separate classes, both of which set an animation time of 750ms. Now

to use these, all we have to do is apply the animate-in-primary and animate-in-secondary classes to an

element.

> Modify the list in home.html to reect the following:

<ion-list no-lines>

<ion-item *ngFor="let message of messages">


<ion-avatar item-left class="animate-in-primary">
<img [src]="message.picture">
</ion-avatar>
<div class="animate-in-secondary">
<h2>{{message.username}}</h2>
<p>{{message.message}}</p>
</div>
</ion-item>

</ion-list>

Notice that weve added the primary animation to the avatar, and the secondary animation to the message

content area. If you load up the application now you should be able to see these elements animate in!

493
Conclusion

Congratulations on making it through the Camper Chat tutorial. Weve learned a lot through developing

this application, but the main take aways are:

Navigation

Using a Sliding Menu

Using PouchDB to store local data

Using Cloudant to store remote data

Using the Facebook API for authentication and other features

Updating and displaying data in real time

Theres always room to take things further though, especially when youre trying to learn something. Fol-

lowing tutorials is great, but its even better when you gure something out for yourself. Hopefully you

have enough background knowledge now to start trying to extend the functionality of the application by

yourself, heres a few ideas to try out:

Make the users device vibrate when a message is received by using a Cordova plugin [EASY]

Add an Invite Friends option using App Invites from the Facebook API [MEDIUM]

Add Channel Categories that will allow the user to switch between dierent chat rooms, e.g: Ve-

hicle Issues, General Chat, Camping [HARD]

Add the ability for a user to privately message another user [VERY HARD]

Remember, the Ionic 2 documentation is your best friend when trying to gure things out.

What next?

You have a completed application now, but thats not the end of the story. You also need to get it running

on a real device and submitted to app stores, which is no easy task. The nal sections in this book will

walk through how to take what you have done here, and get it onto the app stores so make sure to give

that a read.

494
Chapter 8

Testing & Debugging

495
Testing & Debugging

When creating your application you are, without a doubt, going to make some mistakes and run into some

errors. Its not always obvious where youve gone wrong either, so its important to know how you can go

about tracking down the problem.

This lesson is going to cover how best to debug Ionic 2 applications, but it wont cover what debugging

is in general or how to use debugging tools (e.g. viewing the source code, setting breakpoints, looking

at network requests and so on). If you are not familiar with browser based Javascript debugging, then I

highly recommend having a read through of this rst.

Browser Debugging

Your desktop browser will usually be your rst point of call when developing your application. Its great to

be able to debug directly through the browser because, especially with live reload, you can quickly see the

impact code changes have and iterate really quickly.

But its important to keep in mind that it is not representative of how the application will run on an

actual device. For the most part, how it behaves in the browser will be the same as the way it works on

a real device, but there can be dierences and in the case of any Cordova plugins, they wont work when

testing through a desktop browser.

A good approach is to do the majority of your testing in the browser, and once youre getting close to being

happy with how everything is working switch to testing on a real device to make sure everything still works

correctly.

iOS Debugging

In order to debug on an actual iOS device you will rst need to run the following command:

npm -g install ios-sim ios-deploy

496
This will install both the iOS Simulator (which will allow you to emulate an iOS device on your computer)

and ios-deploy will allow you to deploy the application directly to your device when it is attached via

USB.

Once that is set up, all you have to do is run:

ionic run ios

from within your project directory and the application will be deployed to your device (or to the emulator

if a device is not available). If it is the rst time you have run this command, you may need to run it twice

before it will work.

Once the application is running on your device, you can debug it using the Safari Dev Tools on your

computer. Simply open up Safari and then go to:

Develop > iPhone > index.html

If the Develop menu does not appear for you, you may rst have to enable it by going to Safari >

Preferences > Advanced > Show Develop menu in menu bar.

497
Once you open up index.html in Safari you should see a debugging screen like this:

IMPORTANT: This method will only work if you have a Mac. If you do not have a Mac then you can use

GapDebug to install and debug iOS applications. In order to do this you will need to generate an .ipa of

your application rst (we will discuss how to do this in the Building for iOS lesson) and install that directly.

Its also a good idea to test the nal build of your application using Test Flight.

Android Debugging

Debugging on Android is as simple as attaching the device you want to debug on and then running:

ionic run android

on your desktop browser (in Chrome) you will now be able to go to the following address:

chrome://inspect/#devices

498
and then click Inspect on the device you want to debug on, and you will see the debugging tools:

499
IMPORTANT: You will need to have USB debugging enabled on your device for this to work. Enabling

USB debugging is dierent for dierent devices, so just Google [YOUR DEVICE] enable usb debugging.

Tips & Common Errors

The ease of which you can debug applications usually comes from the debugging prowess you develop

over years of facing frustrating errors, but there are a few tips and gotchas I can oer that may help you

out when debugging Ionic 2 application.

500
Facing a white or blank screen at launch?

This one happens a lot and the reason is usually a startup error when running on a device. Sometimes you

will launch your application and just see a blank screen, and even if you open your debugging tools you

might not see any errors. This can be because an error occurs before the debugger has launched, which

is breaking the application. So if youre running into a problem like this, make sure to get the debugger up

and then hit the refresh button from the debugger, which in Safari looks like this:

This will reload the application with the debugger already active, which means youre not going to miss

any errors that occur right at the start. Nine times out of ten you will discover there is some Javascript

error and after xing that your application should work.

501
Debugger

Hopefully you know about how useful breakpoints are for debugging your application, but if not Ill reiterate

that you should read this.

You can just open up the Sources tab and put some breakpoints in manually, but you can also simply

add this little keyword:

debugger;

anywhere in your code, and it will cause your application to pause at that point (as long as you have the

Dev Tools open).

404 Errors

Is your app working ne through the browser but youre getting 404 errors when running on a device?

Make sure you have included the Whitelist plugin:

ionic plugin add cordova-plugin-whitelist

and that you have an appropriate Content Security Policy (CSP) meta tag dened in your index.html le:

<meta http-equiv="Content-Security-Policy" content="font-src * data:; img-src


* data:; default-src * 'unsafe-eval' 'unsafe-inline'">

Plugins not working on a device

Of course the rst thing to make sure youve done is to run the install command for the plugin:

ionic plugin add [plugin name]

It might sound silly but make sure to double check with the following command:

ionic plugin ls

502
this will list all plugins that are currently installed, and I still even forget to install plugins sometimes. If the

plugin is installed then you should also make sure that you are not trying to access the plugin too early. If

you try to access plugin functionality before the device is ready, i.e. before this res:

platform.ready().then(() => {

});

then it will not work. Finally, if youve exhausted all other options and it is still not working then you should

try removing the plugin:

ionic plugin rm [plugin name]

and then readding the plugin:

ionic plugin add [plugin name]

Debugging applications can be a dicult and frustrating task, especially when debugging on a real device,

but over time you tend to develop a good sense of where things may have gone wrong and the process

becomes a lot easier. If youre stuck on a particular error, you can always head over to the Ionic Forum for

help, just make sure to provide as much detail about the error and what youve tried as you can.

Installing your Application with GapDebug

If you are using PhoneGap Build and have a .ipa or .apk le you want to test then you will want to use

GapDebug. There are other ways to install these les - you could use iTunes to install the .ipa le or you

could use adb to install the .apk le - but it is so much easier with GapDebug and GapDebug also provides

you with some awesome debugging tools (although, if youre looking for some more advanced debugging

I would recommend using adb as well). To get GapDebug just head over to the website and download it.

Once you have it installed, it should be available in your system tray.

After you have installed GapDebug, make sure you read through the GapDebug First-Time Conguration

and Setup - there is a few things youll need to do before GapDebug will work properly with your iOS or

503
Android device.

Once you have everything set up you will be able to connect your device to your computer, open up

GapDebug, then just drag and drop the application le (.ipa or .apk) into the device through GapDebug.

Not only is this a super quick way to get the application on your device but it also provides you with

some extremely useful debugging tools (which are essentially the same as the browser debugging tools

you may already be familiar with). Once you have installed your application with GapDebug, just click the

application in GapDebug to reveal the debugging tools.

504
Chapter 9

Building & Submitting

505
Preparing Assets

At this point you should have your application built, working, and ready for submission to the app store.

Theres just a couple things we are going to do beforehand though to make sure our application is in ship

shape before shipping it o to the app stores.

You may be following along and building your own application whilst reading this book, but chances are

youve been building one of the example applications. In that case, Id still recommend you go through

these steps, or at least read them, so that you learn how the submission process works. I do however ask

that you stop short of actually submitting the application to app stores, Im happy for you to use bits and

pieces of code as you see t in your own applications, but you cant submit an exact copy of one of the

example applications to the app store.

Enable Production Mode

Enabling production mode is an Angular 2 concept. This will disable some features of Angular 2 which are

useful for development, but not required in production. All you have to do is add the prodMode ag to the

ionicBootstrap function in your app.ts le:

ionicBootstrap(MyApp, [], {
prodMode: true
});

Generate Icons and Splash Screens

Theres so many dierent devices out there with dierent screen sizes and resolutions, and we need to

cater to all of these when creating our applications icon and splash screen.

Nobody in their right mind would be excited by the chance to make 50 dierent versions of the same

le. Luckily theres a very easy, almost completely automated, option provided by Ionic. We can use the

506
ionic resources command to generate these for us. Although Ionic does a lot for you but it cant quite
yet design your graphical resources for you, so theres a little set up work we need to do.

Design your icon

The Ionic resources tool only requires a single 192 x 192 icon as a base, but since the app store requires

a larger icon anyway its best to design it at 1024 x 1024 rst (or even 2048 x 2048). For the most part,

scaling down large images into small icons works well, but if you want a really crisp looking icon it may be

worth designing a smaller version of your icon specically.

> Create a 192 x 192 icon called icon.png** and save it inside of the resources folder (overwriting the

existing icon)**

Design your splash screen

Icons are great because they are always square. Splash screens unfortunately come in all sorts of shapes

and sizes. This means that youre either going to have to make your design exible, or make dierent

designs for each individual splash screen size.

Whats a exible design? Well, thats just what Ive decided to call a design that will play nicely with

Ionics resource tool. If you design the splash screen at 2208 x 2208 then all of your splash screens should

all automatically generate nicely.

The problem is though that there is always going to be a degree of cropping that occurs - some screens are

tall and some screens are wide. To avoid important parts of your design getting chopped o you should

make sure to place the important parts of the design near the center of the image, and the bits near the

edges should be unimportant (e.g just part of a back- ground image).

> Grab the splash-screen-template.psd or splash-screen-template.png le from your download

pack. Create your own 2208 x 2208 splash screen based on this template and save it as splash.png

in the resources folder along with the icon.png le you created previously.

507
Run the resources tool

Now that you have the base icon and splash screen created, you can create all of the rest of the splash

screens and icons by running the following command:

ionic resources

Set the Bundle ID and App Name

An important step before building is to set your App Name and the Bundle ID in your cong.xml le. If

you take a look at the rst few lines of this le you will see this:

<?xml version='1.0' encoding='utf-8'?>


<widget id="io.ionic.starter" version="0.0.1"
xmlns="http://www.w3.org/ns/widgets"
xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>V2 Test</name>
<description>An Ionic Framework and Cordova project.</description>
<author email="hi@ionicframework" href="http://ionicframework.com/">Ionic
Framework Team</author>

You should update these details to reect your own, including the id which is currently io.ionic.starter.

This will need to match the values you use to sign your application in the next lessons, and should be set

to something like com.yourname.yourproject. You will likely also want to set the version to 1.0.0 when

you are releasing your application for the rst time.

Set Cordova Preferences

This is not an essential step but you can also provide some preferences in your cong.xml le which will

aect how it is built. There are some defaults included in the cong.xml le like this:

<preference name="webviewbounce" value="false" />

508
<preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />
<preference name="android-minSdkVersion" value="16" />
<preference name="BackupWebStorage" value="none" />

But there are a ton more preferences you can specify, take a look here for more information.

Minify Assets

This is also an optional step but an important one I think. You want to keep your nal build sizes down

as much as possible and one way to help do that is to minify all of your images using a tool like TinyPNG.

Just run your images through the tool and save them back into your project.

509
Signing iOS Applications on a Mac or PC

iOS and Android applications need to be signed. The idea is similar to a normal signature, in that it

proves you authorize the action being taken and that you have the authority to do so. Signing works

slightly dierently for both platforms.

iOS applications will require a .p12 le which is made up of a key and either a development or distribution

certicate. As well as the .p12 le, you will also need to create a provisioning prole. The purpose of

the .p12 le is to identify you as an iOS developer, and the provisioning prole identies the application,

services and devices that are allowed to install the application. It is quite a process to get all these things

in order, but if you just follow this guide step by step you shouldnt have too many problems.

If you have a Mac you can use XCode to handle some of this process automatically, but I will be going

through everything step by step so that you can use alternate methods to build if you do not have a Mac.

Before you get started you will need to be enrolled in the iOS Developer Program.

Signing iOS Applications on a Mac

If you have a Mac you should follow these steps, if not skip this section and start reading Signing iOS

Applications on Windows. You should make sure you have XCode installed before you begin.

Generate a Certicate Signing Request

Open Keychain Access

Go to Keychain Access > Preferences:

510
* Go to the Certicates tab and make sure both options are switched o * Now go to Keychain Access

> Certicate Assistant > Request a Certicate From a Certicate Authority * Fill in the details in this

window and select Saved to disk and Let me specify key pair information:

511
Click continue and choose a location to save the le on your machine On the next screen choose 2048

bits and RSA:

The certicate signing request will now be saved in the folder you specied and you will also notice that a

public and private key pair will be created in Keychain Access:

Create a Certicate

To complete these next steps you will need to log in to the Apple Member Center and go to the Certicates,

Identiers & Proles section:

512
* Choose Certicates under iOS Apps * Click the + button in the top right corner:

Next you must choose what type of certicate you want to generate. If you are creating a certicate

for testing applications then you should choose iOS App Development but if you are preparing an

application for distribution on the App Store then you should choose App Store and Ad Hoc.

513
You will also notice some other options on this screen. You will need to create separate certicates if

you want to use things like the Apple Push Notications service or WatchKit, but we wont require any of

those.

Select your certicate type and click Continue

It will now ask you for the certicate signing request you just created with Keychain Access, click

continue and then upload the signing request. Once you have selected the .certSigningRequest

le click Generate.

You will now be able to download your certicate. Download it to somewhere safe and then open it to

install it.

514
Create an Identier

If youre managing your app using XCode then you dont necessarily need to worry about these step as

XCode can use a Wildcard App ID, but you can also create your own App ID manually (the Bundle ID

created here will match what is in your cong.xml le). Heres how you do it:

Click on App IDs and then click the + icon

* Fill in the App ID Description and then supply an Explicit App ID like the following:

This should match the id that you supply in your cong.xml le.

Go to the bottom of the screen and click Continue

On the following screen click Submit

515
The App ID should now be registered and available to you to use in provisioning proles, which we will do

in the next step.

Create a Provisioning Prole

Before you create a provisioning prole you will need to add devices that the application can run on.

Click on Devices and then click the + button in the top right:

* Supply the name and UDID (you can nd this for your device in iTunes) of the device you want to register

and then click Continue. Follow the prompts to nish.

Now we can create the provisioning prole.

*Go to the provisioning proles screen and click the + icon in the top right

* Choose iOS App Development if you are creating a provisioning prole for testing, or App Store if you

are creating a provisioning prole for distribution. Click Continue. * Select the App ID you just created

and click Continue:

516
* Select the certicate you wish to use and click Continue:

* Select all the devices you want to run the application on and click Continue: * Provide a name for the

provisioning prole and click Generate:

517
You should now be able to download your Provisioning Prole.

Generating a .p12

A .p12 le (along with the provisioning prole) is required along with the provisioning prole for services

like PhoneGap Build. Since you have a Mac this step is not really relevant to you, however you can still

use a service like PhoneGap Build if you would like (I wouldnt recommend it). If you do need to create a

.p12 le on your Mac, heres how you do it:

Open Keychain Access

Back in the rst step when we created the certicate signing request, I mentioned that a public and

private key pair would be generated in Keychain Access. Find this in the Keys section:

You will see now that the private key also has the certicate you created in the iOS Member Center attached

to it.

518
Highlight both the private key and the certicate, right click and choose Export 2 items:

* You will then be asked where you would like to save the le and you may choose to enter a password for

the .p12 le or leave it blank. You may also be asked to enter in the Admin password for your computer.

Congrats! You now have your .p12 le.

Signing iOS Applications on Windows

To complete the following steps you will need to download and install OpenSSL. Visit the OpenSSL website:

https://www.openssl.org/source/

and download the version appropriate for your system.

Generate a Certicate Signing Request

Once you have OpenSSL set up we are going to use it to generate a Certicate Signing Request. This will

be used in the iOS Member Center to create either our development or distribution certicate.

Change your directory to the OpenSSL bin directory in the command prompt, e.g:

cd c:/OpenSSL-Win64/bin

Generate a private key

openssl genrsa -out mykey.key 2048

Use that key to generate a certicate signing request

openssl req -new -key mykey.key -out myCSR.certSigningRequest -subj


"/emailAddress=you@yourdomain.com, CN=Your Name, C=AU"

519
Make sure to replace the email address, name and country code with your own.

Create a Certicate

To complete these next steps you will need to log in to the Apple Member Center and go to the Certicates,

Identiers & Proles section:

* Choose Certicates under iOS Apps * Click the + button in the top right corner:

Next you must choose what type of certicate you want to generate. If you are creating a certicate

for testing applications then you should choose iOS App Development but if you are preparing an

application for distribution on the App Store then you should choose App Store and Ad Hoc.

520
You will also notice some other options on this screen. You will need to create separate certicates if

you want to use things like the Apple Push Notications service or WatchKit, but we wont require any of

those.

Select your certicate type and click Continue

It will now ask you for the certicate signing request you just created with OpenSSL, click continue

and then upload the signing request. Once you have selected the .certSigningRequest le click

Generate.

You will now be able to download your certicate. Download it to somewhere safe and then open it to install

it (for convenience, you should place it in your OpenSSL bin folder (e.g. OpenSSL-Win64/bin because

this is where we will be running some commands later).

521
Create an Indentier

Click on App IDs and then click the + icon

* Fill in the App ID Description and then supply an Explicit App ID like the following:

This should match the id that you supply in your cong.xml le.

Go to the bottom of the screen and click Continue

On the following screen click Submit

The App ID should now be registered and available to you to use in provisioning proles, which we will do

in the next step.

522
Create a Provisioning Prole

Before you create a provisioning prole you will need to add devices that the application can run on.

Click on Devices and then click the + button in the top right:

* Supply the name and UDID (you can nd this for your device in iTunes) of the device you want to register

and then click Continue. Follow the prompts to nish.

Now we can create the provisioning prole.

Go to the provisioning proles screen and click the + icon in the top right

* Choose iOS App Development if you are creating a provisioning prole for testing, or App Store if you

are creating a provisioning prole for distribution. Click Continue. * Select the App ID you just created

and click Continue:

* Select the certicate you wish to use and click Continue:

523
* Select all the devices you want to run the application on and click Continue * Provide a name for the

provisioning prole and click Generate

You should now be able to download your Provisioning Prole (again, download it into that same OpenSSL

bin folder to make things easier in this next step).

524
Generating a .p12

Finally, to create the .p12 le we will use the certicate we downloaded from the Member Center and a

couple more commands.

Run the following command to generate a PEM le:

openssl x509 -in ios_development.cer -inform DER -out app_pem_file.pem


-outform PEM

Use the key you generated right back at the beginning and the PEM le you just created to create

your .p12 le:

openssl pkcs12 -export -inkey mykey.key -in app_pem_file.pem -out app_p12.p12

Theres a lot of things that can go wrong here and its easy to mess up. So if you have any trouble I

would suggest just restarting the whole process and slowly and carefully make sure you type everything

out correctly.

Congrats! You now have your .p12 le. Since you have both your provisioning prole and .p12 le now, if

you are using PhoneGap Build you will be able to attach these les to your application when you upload

it. Once you have done that you should be able to generate a signed .ipa le that can be installed on your

device (as long as you added your device and have followed all these steps correctly!).

We will discuss exactly how to build with PhoneGap Build shortly.

525
Signing Android Applications on a Mac or PC

As I mentioned before, Android is a little dierent to iOS (and a bit easier fortunately). iOS requires that

you create certicates and provisioning proles for both development and production applications.

Android however, will let you install a debug application on a testing device without having to go through

this process. You will only need to sign your application when you want to release it on Google Play.

Android requires that you create a keystore le to sign your application with.

Signing an Android Application

It is considerably easier to sign an Android application and, as I mentioned before, you dont even need

to sign the application if you just want to test it (only if you are releasing it to Google Play). To sign our

Android application we will need to create a keystore le.

To sign applications we will be using a tool called keytool that comes with the Android SDK. If you do not

have the Android SDK set up on your machine already you can follow this guide:

http://ionicframework.com/docs/ionic-cli-faq/#android-sdk

Once you have the Android SDK set up, you can follow these steps to create a keystore le.

Run the following command to generate a keystore le with an alias of alias_name (you should

change this)

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name


-keyalg RSA -keysize 2048 -validity 10000

When you enter this command you will be prompted for a password - choose one and make sure to store

or remember it. You will then be asked a series of questions, most of which you can leave blank if you

wish. Finally, you will be asked for a keystore password.

You will now have a keystore le that you can use to sign your application (we will discuss how that works

526
later).

IMPORTANT: In order to update your application on the app store later you will need both the keystore

le, along with the alias name and password. If you lose any of these you will not be able to update the

application.

Generating a Key Hash

If you are using Facebook functionality that requires creating a Facebook application which you would have

done if you completed the CamperChat application in this book (only available in the Expert package) then

you will need to create a Key Hash from your keystore le to supply to Facebook for Android.

Doing that is simple enough, all you need to do is run the following command:

keytool -exportcert -alias alias_name -keystore my-release-key.keystore |


openssl sha1 -binary | openssl base64

Make sure to replace alias_name with your own keystore les alias name and my-release-key.keystore

with the path to your keystore le.

Once you have done this your Key Hash will be output to the terminal, and then you can simply copy it

over to the Android platform settings in your Facebook application.

527
Building for iOS & Android Using PhoneGap Build (without MAC)

There are two ways you can incorporate Cordova / PhoneGap into your applications, you can either use a

locally installed version through the command line or you can use the PhoneGap Build cloud service.

The choice should be simple:

If you have a Mac: use Cordova locally

If youre not building an iOS application: use Cordova locally

If you do not have a Mac and want to create iOS applications: use PhoneGap Build

The PhoneGap Build service will allow you to create 1 private application for free. You can keep deleting

and reusing this allowance to build multiple applications if you like, but if you ever want to manage multiple

applications in PhoneGap Build at the same time then you will need to purchase a subscription.

IMPORTANT: Unless you t into that 3rd option (No Mac but want to build for iOS) you should SKIP

THIS LESSON

PhoneGap Build is a great service, but you should always use a local installation of Cordova where possible.

The advantages of using Cordova locally are:

Build times are quicker since you dont need to the PhoneGap Build server and then download the

resulting .ipa (and .apk for Android) les

You dont need to use your data to download and upload the les and you wont require an Internet

connection for development

You have access to a wider range of plugins

You have more control over the application (you have direct access to the native wrapper, rather

than just the web les)

PhoneGap Build requires payment if you want more than 1 private repository

I did mention that PhoneGap Build can be used to build iOS applications without a Mac. It can also be

used to create Android applications without the Android SDK. This is why it doesnt matter what operating

system you are using, since all of the compilation happens on PhoneGap Builds servers.

528
When using PhoneGap Build you are able to upload your application code (purely web based code, i.e:

HTML, CSS and JavaScript) and have compiled application les returned for iOS and Android (as well as

for Windows Phone). The general process looks something like this:

1. Build your application

2. Create a cong.xml le that tells PhoneGap Build how your application should be built

3. Zip up your application

4. Upload to PhoneGap Build

5. Download the resulting native packages

Compilation and the integration of SDKs are handled on their end, which makes this approach much

easier initially than getting everything set up correctly on your machine (but harder in the long run). Most

importantly it makes it possible to create iOS applications without a Mac.

By this point you should have an application nished and ready to build, so lets walk through how to do

that with PhoneGap Build.

Building with PhoneGap Build

When uploading to PhoneGap Build we only want to include the built web les, that is everything inside of

the www folder in your project. All the rest of the les and folders are mainly for conguring Cordova and

the build process which we dont need when using PhoneGap Build.

Theres a couple of little changes we need to make before uploading to PhoneGap Build.

Create a cong.xml le

As you may already be aware, your project contains a cong.xml le that is used for conguring how your

application is built with Cordova. We still need this le, but the one required by PhoneGap Build is a bit

dierent.

529
WARNING: The next code block is huge and hard to read in this format, so if you prefer you can just copy

the code from the example PhoneGap Build application that came with your download pack.

> Create a le at www/config.xml and add the following

<?xml version="1.0" encoding="UTF-8" ?>


<!-- Change the id to something like com.yourname.yourproject -->
<widget xmlns = "http://www.w3.org/ns/widgets"
xmlns:gap = "http://phonegap.com/ns/1.0"
id = "com.example.project"
versionCode = "10"
version = "1.0" >

<!-- versionCode is optional and Android only -->

<name>App Name</name>

<description>
Description
</description>

<author href="http://www.example.com" email="you@yourdomain.com">


Your Name
</author>

<plugin name="cordova-sqlite-storage" source="npm" />


<plugin name="com.phonegap.plugin.statusbar" source="pgb" />
<plugin name="org.apache.cordova.splashscreen" source="pgb" />
<plugin name="com.indigoway.cordova.whitelist.whitelistplugin"
source="pgb" />

530
<preference name="prerendered-icon" value="true" />
<preference name="target-device" value="universal" />
<preference name="android-windowSoftInputMode" value="stateAlwaysHidden"
/>

<icon src="resources/android/icon/drawable-ldpi-icon.png"
gap:platform="android" gap:qualifier="ldpi"/>
<icon src="resources/android/icon/drawable-mdpi-icon.png"
gap:platform="android" gap:qualifier="mdpi"/>
<icon src="resources/android/icon/drawable-hdpi-icon.png"
gap:platform="android" gap:qualifier="hdpi"/>
<icon src="resources/android/icon/drawable-xhdpi-icon.png"
gap:platform="android" gap:qualifier="xhdpi"/>
<icon src="resources/android/icon/drawable-xxhdpi-icon.png"
gap:platform="android" gap:qualifier="xxhdpi"/>
<icon src="resources/android/icon/drawable-xxxhdpi-icon.png"
gap:platform="android" gap:qualifier="xxxhdpi"/>

<icon src="resources/ios/icon/icon.png" gap:platform="ios" width="57"


height="57"/>
<icon src="resources/ios/icon/icon@2x.png" gap:platform="ios" width="114"
height="114"/>
<icon src="resources/ios/icon/icon-40.png" gap:platform="ios" width="40"
height="40"/>
<icon src="resources/ios/icon/icon-40@2x.png" gap:platform="ios"
width="80" height="80"/>
<icon src="resources/ios/icon/icon-50.png" gap:platform="ios" width="50"
height="50"/>
<icon src="resources/ios/icon/icon-50@2x.png" gap:platform="ios"
width="100" height="100"/>

531
<icon src="resources/ios/icon/icon-60.png" gap:platform="ios" width="60"
height="60"/>
<icon src="resources/ios/icon/icon-60@2x.png" gap:platform="ios"
width="120" height="120"/>
<icon src="resources/ios/icon/icon-60@3x.png" gap:platform="ios"
width="180" height="180"/>
<icon src="resources/ios/icon/icon-72.png" gap:platform="ios" width="72"
height="72"/>
<icon src="resources/ios/icon/icon-72@2x.png" gap:platform="ios"
width="144" height="144"/>
<icon src="resources/ios/icon/icon-76.png" gap:platform="ios" width="76"
height="76"/>
<icon src="resources/ios/icon/icon-76@2x.png" gap:platform="ios"
width="152" height="152"/>
<icon src="resources/ios/icon/icon-small.png" gap:platform="ios"
width="29" height="29"/>
<icon src="resources/ios/icon/icon-small@2x.png" gap:platform="ios"
width="58" height="58"/>

<gap:splash src="resources/android/splash/drawable-land-ldpi-screen.png"
gap:platform="android" gap:qualifier="land-ldpi"/>
<gap:splash src="resources/android/splash/drawable-land-mdpi-screen.png"
gap:platform="android" gap:qualifier="land-mdpi"/>
<gap:splash src="resources/android/splash/drawable-land-hdpi-screen.png"
gap:platform="android" gap:qualifier="land-hdpi"/>
<gap:splash src="resources/android/splash/drawable-land-xhdpi-screen.png"
gap:platform="android" gap:qualifier="land-xhdpi"/>
<gap:splash
src="resources/android/splash/drawable-land-xxhdpi-screen.png"
gap:platform="android" gap:qualifier="land-xxhdpi"/>

532
<gap:splash
src="resources/android/splash/drawable-land-xxxhdpi-screen.png"
gap:platform="android" gap:qualifier="land-xxxhdpi"/>
<gap:splash src="resources/android/splash/drawable-port-ldpi-screen.png"
gap:platform="android" gap:qualifier="port-ldpi"/>
<gap:splash src="resources/android/splash/drawable-port-mdpi-screen.png"
gap:platform="android" gap:qualifier="port-mdpi"/>
<gap:splash src="resources/android/splash/drawable-port-hdpi-screen.png"
gap:platform="android" gap:qualifier="port-hdpi"/>
<gap:splash src="resources/android/splash/drawable-port-xhdpi-screen.png"
gap:platform="android" gap:qualifier="port-xhdpi"/>
<gap:splash
src="resources/android/splash/drawable-port-xxhdpi-screen.png"
gap:platform="android" gap:qualifier="port-xxhdpi"/>
<gap:splash
src="resources/android/splash/drawable-port-xxxhdpi-screen.png"
gap:platform="android" gap:qualifier="port-xxxhdpi"/>

<gap:splash src="resources/ios/splash/Default-568h@2x~iphone.png"
gap:platform="ios" width="640" height="1136"/>
<gap:splash src="resources/ios/splash/Default-667h.png" width="750"
gap:platform="ios" height="1334"/>
<gap:splash src="resources/ios/splash/Default-736h.png" width="1242"
gap:platform="ios" height="2208"/>
<gap:splash src="resources/ios/splash/Default-Landscape-736h.png"
gap:platform="ios" width="2208" height="1242"/>
<gap:splash src="resources/ios/splash/Default-Landscape@2x~ipad.png"
gap:platform="ios" width="2048" height="1536"/>
<gap:splash src="resources/ios/splash/Default-Landscape~ipad.png"
gap:platform="ios" width="1024" height="768"/>

533
<gap:splash src="resources/ios/splash/Default-Portrait@2x~ipad.png"
gap:platform="ios" width="1536" height="2048"/>
<gap:splash src="resources/ios/splash/Default-Portrait~ipad.png"
gap:platform="ios" width="768" height="1024"/>
<gap:splash src="resources/ios/splash/Default@2x~iphone.png"
gap:platform="ios" width="640" height="960"/>
<gap:splash src="resources/ios/splash/Default~iphone.png"
gap:platform="ios" width="320" height="480"/>

<!-- Default Icon and Splash -->


<icon src="resources/icon.png" />
<gap:splash src="resources/splash.png" />

<access origin="*"/>

</widget>

This le is specic for the Quicklists application - notice how weve got a few lines that include plugins?

The way plugins are included with PhoneGap Build is dierent to Cordova. You may remember at the start

of building each application we run a bunch of commands like this:

ionic plugin add [plugin name]

to set up the plugins. This sets up the plugin locally in the project, but to use a plugin with PhoneGap Build

we need to specic it in our new cong.xml le like this:

<plugin name="cordova-sqlite-storage" source="npm" />


<plugin name="com.phonegap.plugin.statusbar" source="pgb" />
<plugin name="org.apache.cordova.splashscreen" source="pgb" />
<plugin name="com.indigoway.cordova.whitelist.whitelistplugin"
source="pgb" />

534
Although most are, not all plugins are available for PhoneGap Build. When adding the plugins for a project,

just search for the PhoneGap Build installation instructions, most plugins will include information on what

you need to include for PhoneGap Build. So if you are building any of the applications in this book with

PhoneGap Build, you should look in the Getting Ready section and make sure to include any plugins we

are using in this cong.xml le.

Also make sure you remember to replace the id at the top with your own Bundle ID, i.e: com.yourproject.yourname.

Copy the Resources

The other main dierence is the way the resources (splash screens and icons) are included. We also need

to add references to these to our cong.xml le and we need to copy over our resources because currently

they live outside of the www folder (so they wouldnt be included in our upload)

Copy the resources folder inside of the www folder

As well as copying over the resources, you should also save a blank le with the name .pgbomit inside

of the resources folder. This tells PhoneGap Build that the les in this folder should only be used for the

splash screens and icons, and shouldnt be made available in the application (which would take up a lot

of space).

Upload to PhoneGap Build

Now that weve got everything ready, you just need to zip up the contents of the www folder (NOT the

www folder itself) and upload it to PhoneGap Build. If you do not already have an account you will have

to create one at build.phonegap.com.

Once you have an account simply create a new app and upload the .zip archive you just created and click

Ready to Build. The iOS build will fail initially but the Android version should succeed. This is because

you dont need to sign an Android application during development, but you do for iOS.

When youre building your release version, or if you want to download the iOS version, you will have to

535
upload the signing keys you created in the last chapters. This means you will need to attach the .p12 le

and the Provisioning Prole to your iOS build, and the keystore le to your Android build.

Once the applications have nished building, you will be able to download your .ipa le for iOS and a .apk

le for Android. We will discuss how to get these on the app stores in just a moment.

536
Submitting to the Apple App Store

The time has nally come to send our application o to apple! Before submitting your application make

sure that you familiarise yourself with the App Store Review Guidelines - if you dont comply with these

guidelines your application may be rejected.

NOTE: Do not follow these steps until you have created your own application - You should not upload any

example applications from this book.

To submit an application to the App Store you will need to create an App Store Listing and of course upload

your application.

Creating an App Store Listing

Lets start by walking through how to create an App Store Listing. Theres quite a few things you need to

do but its pretty straight forward.

Log into iTunes Connect

Go to My Apps

Select the + icon on the left and then choose New iOS App

Fill out the details on this prompt and then click Create:

537
If youre using XCode to submit your application you can use Xcode iOS Wildcard App ID, or otherwise

you can choose the specic Bundle ID you created for your application in the iOS Certicates lesson. The

SKU does not need to be anything specic, it is just for your own reference and the Bundle ID Sux

should match the id you specied in your cong.xml le.

You should now see your application in the dashboard:

538
* Open your new application in iTunes Connect and you should see a dashboard like this:

539
* Fill out all the information on this page (including multiple screenshots for the dierent sized devices)

NOTE: You will notice a Build section on this page. You will have to come back to this section after you have

uploaded your application (which we will do in the next section) and select the build you have uploaded.

Click the Pricing tab and ll out the following information:

540
If you want to release a paid application you will need to have accepted additional agreements within

iTunes Connect.

Uploading the Application

Theres a few ways to do this and the way you do it will depend on what format your application is in and

what operating system you are using. To submit an iOS application for distribution through the app store

you will need your app signed with a distribution certicate, and again, the way you do this will depend on

the method you are using.

Once you upload your application you should be able to see it in the Build section in iTunes Connect and

will be able to attach it to your app store listing.

Submitting an app using XCode

If you have a Mac then you can use XCode to submit the application, which is a pretty straightforward way

to do it. If you do not have a Mac and instead have a .ipa le generated from using PhoneGap Build you

should skip to the Submitting an app using Application Loader section.

Before continuing you should run the following command within your project directory:

ionic build ios

Converting a .xcodeproj le to .xcarchive

If you have a .xcodeproj le (which will be generated when you run the build command) you will rst need

to generate a .xcarchive le from it. To do that then you will need to follow these steps:

Open your .xcodeproj le (located in platforms/ios/snapaday.xcodeproj) in XCode by double

clicking it

Go to Product > Scheme > Edit Scheme and make sure that the archive is set to a Release

conguration:

541
* Make sure you have iOS Device or Generic iOS Device selected in the top toolbar, not an emulator:

* Choose Product > Archive

Uploading a .xcarchive le

If you want to submit a .xcarchive rst double click it to open this screen in XCode (this screen should

also automatically open after you Archive your app):

542
First you will want to choose your archive and then click the Validate button to make sure that everything

is set up correctly. You should be given the option to choose your account:

and then you will see your application displayed. Click Validate and if everything is set up correctly you

should see this prompt:

543
If validation does not work, make sure:

You have set up your application in iTunes Connect

You have followed the instructions in the iOS Certicates lesson

The id in your cong.xml matches the Bundle ID Sux in iTunes Connect

Once you have successfully validated your project click Done and then choose Submit to App Store

or Upload to App Store:

You will now go through that same process again, except this time you will choose Submit. Once you click

Submit your application will begin uploading to iTunes Connect:

544
Submitting an app using Application Loader

If you do not have a Mac then your only option to submit an application is to submit the built and signed

.ipa le. Remember, this .ipa le should be signed with a distribution certicate instead of a development

certicate if you are submitting it to the app store.

If you do not already have a signed .ipa le, make sure to read the PhoneGap Build lesson.

You can use a program called Application Loader to submit a .ipa le to iTunes Connect, but unfortunately

this program is only available on Macs. Literally the only thing you cant do when building iOS applications

on a Windows machine is upload the nal le, how frustrating.

There are ways around this though, and my two favourites are:

Borrow a friends Mac. You will only need it for about 5 minutes, so if you know anyone who has a

Mac just shove your .ipa le on a USB stick, download Application Loader on their Mac and upload

545
your application

Macincloud.com allows you to log in remotely to a Mac. This service does cost, but if you just buy

some prepaid credits its pretty cheap and since you will only ever be on there for a few minutes at

a time the credit will last ages (this is what I did before I got a Mac).

Once youve gured out how youre going to access Application Loader, open it up and log into your iOS

Developer account then choose Deliver Your App:

* Upload the .ipa le that is signed with a distribution certicate and then click Next

Your application will now begin uploading to iTunes Connect:

546
Submit for Review

Once you have uploaded your application, either through XCode or Application Loader, you will need to

nish up your app store listing in iTunes Connect. Go back to your application in iTunes Connect and go

to the Build section:

You should now see a + icon as shown above. Click this, select the build you just uploaded and hit Done:

547
Double check everything in your listing, then go back to the top of the page, hit Save and then Submit

for Review:

to submit your application to Apple. Now you just have to cross your ngers and wait! The Apple review

process usually takes around 5-10 days, which is a frustratingly long time. Theres nothing you can do

about it though but sit back and wait. Just make sure that you are complying with all of Apples rules and

guidelines so that your app does not get rejected (otherwise you will have to x it and wait another 5-10

days!).

548
Submitting to Google Play

If youve been through the nightmare of signing an iOS application and submitting it to the Apple App store

then you are in for a treat. Submitting to Google Play is super easy by comparison. Before you get started,

you will have to sign up as a Google Play Developer.

Remember, before submitting to Google Play you must sign your .apk with a keystore le.

IMPORTANT: If you are using the Crosswalk plugin, then you will have two .apk les generated when you

build. The process for submitting is still mostly the same, but make sure you read the note at the end of

this lesson about how you can upload both .apk les with the same submission.

Creating a Build for Android

Unlike with iOS, it doesnt matter whether you have a Mac or PC, you will be able to use the same method

for both. However, if you are using a PC and are also building an iOS application then that means you will

have used PhoneGap Build to do that, since you are already using PhoneGap Build then it makes sense to

use it for Android to. So I would recommend using PhoneGap Build to build for Android if you dont have

a Mac, unless you are only building for Android.

If you are using PhoneGap Build then you will already have a signed .apk le, in which case you can skip

to the next section Submitting your Application to Google Play. If you do not already have a signed .apk

le make sure to follow these steps.

> Create a le at platforms/android/release-signing.properties and add the following:

storeFile=snapaday-release.keystore
keyAlias=snapaday

This le tells the build process how we want the application to be signed. Here you will supply the keystore

le you generated in the signing lesson, as well as the alias of the keystore. The rst line should be the

path to where the keystore le is stored, for simplicity I move the keystore le to the same location as this

le, however you can also specify a dierent path if you want. The second line is the alias name.

549
Once you have that le in place, all you need to do is run the following command:

ionic build android --release

and your .apk le (or les if you are using Crosswalk, more on that soon) will be generated for you at:

platforms/android/build/outputs/apk/

Submitting Your Application to Google Play

Log in to the Google Play Developer Console

Click +Add New Application:

* Fill in the information on this prompt and then click Upload APK

550
* You should see a page like the following:

* Click Upload your rst APK to Production and upload the signed .apk le you created in Lesson 2

* You should now see the page updated like this:

551
* Now click on Store Listing and ll in all the information including screenshots and promotional graphics

for your application then click Save Draft

* Next go to Content Rating and create a content rating for your application then click Save Draft

552
* Go to Pricing & Distribution and ll out the information there then click Save Draft

Once you have lled out all of the screens just hit Publish App and your application will be submitted!

553
Unlike the Apple App store, your application should be available on Google Play within hours.

Uploading Multiple APKs with Crosswalk

The interesting thing about using Crosswalk is that it creates two separate .apk les, which .apk le is

required for each user depends on which device they have. If you take a look in your platforms/an-

droid/build/outputs/apk/ folder, you will nd the following two release .apk les:

android-armv7-release.apk

android-x86-release.apk

This leaves us with a bit of a dilemma - which one do you submit to the app store?

You may read on forums about combining the two .apk les into one, but fortunately the problem is quite

easy to solve.

Although it may not seem like it you can actually upload two dierent .apk les, both the armv7 and x-86

versions, for your app submission (although the ability to do this is hidden slightly).

If youve uploaded apps to the app store before, you may know that whenever you upload a new version

of the app it needs to have a higher version number than the previously uploaded application (which we

can set in the cong.xml le). This is how you update your application after already having submitted it,

you increase the version number in the cong.xml le, rebuild, and then resubmit.

Im going to show you how you can upload multiple .apk les for the same submission though - just follow

these steps:

Click on the Upload APK button to upload your rst .apk le, either android-armv7-release.apk or

android-x86-release.apk it doesnt matter which.

Once that has nished uploading click [Switch to Advanced Mode] in the top right corner of the

screen:

554
Once you have switched to advanced mode, click the upload button again and upload your second .apk

le.

You should now have both .apk les uploaded, and be able to see them listed. You will notice that they

have the same version number but a dierent version code - one version code must be higher than the

other in order for this to work - fortunately crosswalk handles this for us automatically.

555
Updating on the App Stores

You thought you were done? Almost, but theres one more important topic we need to cover.

Once youve got your application live on the app store, it probably wont be too long before you want to

make an update - to add some new feature or perhaps to x a bug that found its way into the production

application.

Fortunately, updating your application is really easy. All you have to do is modify your cong.xml le and

bump up the version number:

<widget id="com.yourname.yourproject" version="1.0.1"


xmlns="http://www.w3.org/ns/widgets"
xmlns:cdv="http://cordova.apache.org/ns/1.0">

In the example above Ive updated the version to 1.0.1 but you could also use 1.1.0 or 2.0.0, it doesnt

matter just as long as the version number is higher than the one you submitted previously.

Then all you have to do is rebuild your application using the exact same steps as you used before, except

obviously it will be a bit easier this time because you dont have to generate the certicates and so on

again. In fact its important that you use the same details to sign the application again, otherwise it wont

work.

You also wont have to recreate your app store listings of course. In the case of iOS you will just need to

log in to manage your existing application in iTunes Connect and upload and then submit a new build. You

will nd an option to add a new version:

556
and you should choose the iOS option. Enter in the version number that matches the new version in your

cong.xml le and update any elds in the listing that need to be updated. Upload your new build using

XCode or Application Loader just like before, and once again add the new build to the app store listing:

557
Once you have uploaded the new build you can submit it for review again.

For Android you will just need to go to your existing application in the Google Play developer console and

update any elds that need updating. You should then go to the APK section of the listing and upload your

new .apk le (make sure to upload both .apk les if you are using Crosswalk) and then hit Publish now to

Production.

New builds you submit will still need to go through the review process, so for iOS this is going to take

about a week again and for Google Play it will be a few hours.

If youve made it through this course (and even if you havent yet) I would like to give you a huge

Thank you!

Not only have you invested your time and money into learning HTML5 mobile application development

(which I think is a very wise choice), youve also invested time and money into me. I love teaching devel-

opers HTML5 mobile development and it means a lot to me to know that you trust in my abilities enough

to buy this book, and by doing that youre also allowing me to invest more time into creating even more

content for mobile developers.

I assume if youve completed this course then youre probably familiar with my blog, but if youre not make

sure to check it out. I release free HTML5 mobile development tutorials there every week, and if youre

looking to expand your Ionic skills even more theres plenty to check out.

I hope you enjoyed this course and have taken a lot away from it. Im always looking for feedback on how

to continually improve though so please send me an email with any feedback you have.

If you need help with anything related to HTML5 mobile development feel free to get in touch. I respond

to as many requests for help as I can but I receive a lot of emails so I cant always get to them all. If you

want more personal and guaranteed support, I also oer consulting services.

Also feel free to send me an email just to let me know what youre working on, I always like hearing what

fellow HTML5 mobile developers are up to!

558

You might also like