Building hybrid mobile apps

with

An introduction to

Welcome!

I'm Tom: a software developer with 10 years experience building web and mobile apps.

My professional career includes:

Co-founded startup, built web-based touch-screen apps.

Head of mobile web at award-winning mobile app development agency.

Web-based consultancy, specialising in JavaScript development and training.

Welcome!

Tools of the trade:

Find me everywhere as @fiznool

Welcome!

Course overview:

ES6: a recap

Introduction to Angular 2

Wrap-up

Introduction to TypeScript

Workshop: building an Ionic 2 app

Recap

ES6

A long overdue, major update to JavaScript.

Ratified last year, major browsers are implementing features now.

Polyfills are available for older browsers that do not yet support ES6 features.

ES6 / ES2015

let is the new var, const is a var for one-off assignment.

Both correctly scope to blocks, unlike var.

ES6: let & const

let a = 'bob';

if (1 < 2) {
  let a = 'jim';
  console.log(a);  // 'jim'
}

console.log(a);  // 'bob'
var a = 'bob';

if (1 < 2) {
  var a = 'jim';
  console.log(a);  // 'jim'
}

console.log(a);  // 'jim'

Syntactic sugar for strings.

Features: string interpolation, newlines and more.

ES6: Template Strings

const str = `I am a string`;

const multiline = `I am a string...
                   on multiple lines!`

const food = 'pizza';

const like = `I like ${food}`;

Syntactic sugar for OO prototypes.

Features: inheritance, super calls, constructors, instance methods, statics, getters/setters.

ES6: Classes

class Animal {
  speak() {
    console.log('I have no voice.');
  }
}

class Dog extends Animal {
  speak() {
    console.log('woof.');
  }
}

ES6: Classes

class Dog extends Animal {
  constructor(name) {
    super(name);
    this.legs = 4;
  }

  get legs() {
    return this._legs;
  }

  set legs(val) {
    this._legs = val;
    console.log(`I have ${val} legs`);
  }

  static family() {
    return 'Canis';
  }
}

const dog = new Dog('Boris');

Shorthand function with lexical scoping of this.

ES6: Arrow Functions

class Brewery {
  constructor(beers) {
    this.beers = beers;
    this.beers.forEach(beer => {
      this.bottle(beer);
    });
  }

  bottle(beer) {
    console.log(`bottling ${beer}...`);
  }
}

const bathales = new Brewery([
  'Gem', 'Wild Hare', 'Barnsey']);

Replacement for arguments keyword.

ES6: Rest Parameters

class Brewery {
  constructor(...beers) {
    this.beers = beers;
    this.beers.forEach(beer => {
      this.bottle(beer);
    });
  }

  bottle(beer) {
    console.log(`bottling ${beer}...`);
  }
}

const bathales = new Brewery(
  'Gem', 'Wild Hare', 'Barnsey');

A promise represents a value or error that will occur in the future.

ES6: Promises

const p = new Promise(resolve => {
  setTimeout(() => {
    resolve('hello');
  , 200);
});

p.then(msg => {
  console.log(msg);
});

In many cases, they are a better async alternative to callbacks.

If the property name and variable are the same,
objects can now be created using shorthand notation.

ES6: Enhanced Objects

const data = 'some data';

const obj = { data };

console.log(obj.data);    // 'some data'

Export JavaScript primitives and objects, and import them in other places.

ES6: Modules

// obj-utils.js

export function assign(target, ...sources) {
  return Object.assign(target, ...sources);
}

export const proto = Object.prototype;


// app.js

import { assign } from './obj-utils';

assign(obj1, obj2, obj3);

ES6: Further Reading

A superset of JavaScript which adds compile-time type checking.

Compiles down to regular JavaScript.

Adopted as the preferred way to write Angular 2 apps.

Basic Types

let name: string;
let age: number;
let ownsCar: boolean;
let childrenNames: string[];
let favouriteThing: any;

name = 'Bob';    // OK
name = 2;        // Compiler error

age = 30;        // OK
age = 'Thirty';  // Compiler error

ownsCar = true;  // OK
ownsCar = 'no';  // Compiler error

childrenNames = ['Sandy', 'Douglas']; // OK
childrenNames = [21, false, 'Jim'];   // Compiler error

favouriteThing = 'pizza';    // OK
favouriteThing = 42;         // OK

Interfaces

interface Person {
  name: string,     // required
  age: number,      // required
  hasCar?: boolean  // optional
}

const bob: Person = {  // OK
  name: 'Bob',
  age: 30,
  hasCar: true
};

const bobless: Person = {  // OK
  name: 'Bob',
  age: 30
};

const jim = {  // Compiler error (no age)
  name: 'Jim'
};

Functions

function add(a: number, b: number): number {
    return a + b;
}

let result: number = add(1, 2);    // OK
let result: string = add(1, 2);    // Compiler error
let result: number = add(1, '2');  // Compiler error



// Use 'void' if returning nothing.
function print(msg: string): void {
  console.log(msg);
}

// Or, omit the return type altogether.
function print(msg: string) {
  console.log(msg);
}

Classes

class Greeter {
  // property
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return `Hello, ${this.greeting}`;
  }
}

const greeter = new Greeter('world');
greeter.greet();

Classes

class Animal {
  private name: string;

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

const lion = new Animal('Simba');
lion.name;  // Compiler error

Private properties & methods prevent access from outside the class:

Classes

class Animal {
  constructor(public name: string) {}
}

const lion = new Animal('Simba');
lion.name;  // 'Simba'

Parameter properties are a shorthand if you are setting a property from a constructor:

Generics

class Animal<T> {
  constructor(public name: T) {}
}

const lion = new Animal<string>('Simba');
lion.name;  // 'Simba'

Define the type at runtime.

Generics

interface Response {
  status: number,
  data: any
}

function get(url: string): Promise<Response> {
  return new Promise((resolve, reject) => {
    $.ajax(url, {
      success: data => resolve({ status: 200, data }),
      error: (jqXHR, err) => reject({ status: 500, data: err })
    });
  });
}

Often used for promises:

Decorators

function Sealed() {
  return function(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
  };
}

@Sealed()
class Animal {
  constructor(public name: string) {}
}

const dog = new Animal('Benji');
// dog will now be sealed

Modify behaviour of classes.

Further Reading

Angular 2 is a major upgrade to 1.x.

A complete rewrite; it's so different you should treat it as a new framework.

Focus on performance, speed and interop across desktop and mobile.

Advocates Component-based architecture
for authoring applications.

In Angular 2, everything you see on screen is rendered by a Component.

<nav></nav>
<footer></footer>
<directory></directory>
<menu>
</menu>

Components

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

@Component({
  selector: 'lion',
  template: '<p>I am a Lion. Roar!</p>
})
export class LionComponent {}

Use <lion> custom element to render:

<lion></lion>

Components

// lion.component.ts

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {}

Best practice: move template to separate file with templateUrl

<!-- lion.component.html -->
<p>I am a lion. Roar!</p>

Templates

// lion.component.ts

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  status = 'fierce';
}

Interpolate variables with {{ }}

<!-- lion.component.html -->
<p>I am a lion, I'm very {{ status }}.</p>

Templates

// lion.component.ts

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  status = 'fierce';
  shouldShow = true;
}

Evaluate variables with [ ]

<!-- lion.component.html -->
<p [hidden]="shouldShow">I am a lion, I'm very {{ status }}.</p>

Templates

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  status = 'fierce';
  speak() {
    console.log('roar!');
  }
}

Bind events with ( )

<!-- lion.component.html -->
<p (click)="speak()">I am a lion, I'm very {{ status }}.</p>

Templates

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  status = 'fierce';
}

Guard against undefined properties with ?

<!-- lion.component.html -->
<p>I am a lion, see me {{ actions?.speak }}.</p>

Note: this is different to Angular 1.x, which will
automatically catch undefined properties.

Templates

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  status = 'fierce';
  shouldShow = false;
}

Conditionally render with *ngIf

<!-- lion.component.html -->
<p *ngIf="shouldShow">I am a lion, and I'm {{ status }}.</p>

Note: this is analogous to ng-if from Angular 1.x

Templates

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {
  traits = ['fierce', 'brave', 'powerful'];
}

Repeat with *ngFor

<!-- lion.component.html -->
<p>I am a lion, and I am:</p>
<ul><li *ngFor="let t of traits">{{ t }}</li></ul>

Note: this is analogous to ng-repeat from Angular 1.x

Dependency Injection

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

@Component({
  selector: 'lion',
  templateUrl: 'lion.component.html'
})
export class LionComponent {

  constructor(public fb: FormBuilder) {}

  createForm() {
    this.fb.group(...);
  }

}

Inject dependencies in the constructor.

Providers

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

@Injectable()
export class LionService {

  constructor(public http: Http) {}

  getLions() {
    this.http.get(...);
  }

}

Singleton instances (aka services).

Injectable into components.

Forms

<form>
  <div class="form-group">
    <label for="username">Username</label>
    <input type="text" name="username" [(ngModel)]="user.username">
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" name="password" [(ngModel)]="user.password">
  </div>
</form>

Template-driven: uses ngModel inside component template to bind form elements.

interface User {
  username: string, password: string
}

@Component({ ... })
export class UserComponent {
  user: User;
}

Forms

<form [formGroup]="userForm">
  <input type="text" formControlName="username">
  <input type="password" formControlName="password">
</form>

Model-driven: uses FormBuilder inside component to bind form elements.

@Component({ ... })
export class UserComponent {
  userForm: FormGroup;

  constructor(public fb: FormBuilder) {}

  createForm() {
    this.userForm = this.fb.group({
      username: [''],
      password: ['']
    });
}

Pipes

Transform data for display.

@Pipe({
  name: 'timeago'
})
@Injectable()
export class Timeago {
  transform(value) {
    return new timeago().format(value);
  }
}



@Component({
  template: '<p>{{ published | timeago }}</p>'
})
export class FeedComponent {
  published = Date.now();
}

Modules

Combine components, providers and pipes
into logical units of functionality.

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

End of Part 1

Hybrid mobile app framework based on Angular.

Build using web technologies.

Compile for iOS, Android and Windows Phone through Cordova.

$ npm install -g ionic cordova

Workshop

We'll be building a RSS reader.

App will speak to an external API to load articles.

Installing iOS SDK

Install dependencies:

  • Install XCode from Mac Store
  • Run the following commands in the terminal:
$ xcode-select --install

$ npm install -g ios-sim ios-deploy
  • Open the iOS simulator and let it install.

Deploy to simulator

  • Run the following commands in the terminal:
$ ionic platform add ios

$ ionic emulate ios
  • The app will open in the simulator.

Deploy to device

  • Run the following command in the terminal:
$ ionic build ios
  • In Finder, open the xcodeproj file in the platforms/ios folder. This will open Xcode.
  • In Xcode, go to Preferences -> Accounts. Click the + button to add an Apple ID and sign in.

Deploy to device

  • In Xcode, click on the folder icon in the top left hand corner, underneath the play button.

     
  • Click on the project 'Feedstar'.

     
  • In the middle pane, under 'Signing', choose a Team from the dropdown box.

Deploy to device

  • Connect your iPhone to the mac via a USB cable.
     
  • In the top left hand corner, choose your iPhone from the dropdown box.
     
  • Ensure your iPhone is unlocked, and press Play.
     
  • If there are any errors, follow the instructions and try again!

Installing Android

Install dependencies:

Deploy to device

$ ionic platform add android

$ ionic run android

In the terminal, type

The app will be deployed to the phone, and will automatically open.

Hands-on with Ionic 2

By Tom Spencer

Hands-on with Ionic 2

A hands-on introduction to Ionic 2.

  • 160