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 Java JDK8
- Install Android SDK (scroll down to command line tools)
- Add SDK packages
- Setup environment variables
- Ensure computer can 'see' phone
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