Building Native-Like PhoneGap Apps

Christophe Coenraets

http://coenraets.org
@ccoenraets

Holly Schinsky

http://devgirl.org
@devgirlfl

You

Agenda

What's This?

A PhoneGap App!


Pluggable Data Adapters

App
Data Interface
Ajax/JSON
Memory
LocalStorage
WebSQL

cordova plugin add org.apache.cordova.dialogs

navigator.notification.alert(message,
                             callback,
                             title,
                             buttonName);

Slow

Click Me

click: 0ms

Fast

Click Me

touchend: 0ms

https://github.com/ftlabs/fastclick

iOS 8 status: https://github.com/ftlabs/fastclick/issues/262

Your web app doesn't look like this anymore

Single Page Application

<html>
<head>
    <title>My App</title>
    <script src="app.js"></script>
</head>
<body></body>
</html>

Multi-Page vs Single-Page Architecture

Multi-Page Single-Page
UI Generation Tier Server-Side Client-Side
Language Java, .NET, PHP, RoR JavaScript
Offline Support Limited Yes
Page Transitions Browser Developer
Performance Laggy Fast

New Challenges

Building HTML with JavaScript

var html = '<div class="topcoat-navigation-bar">' + '<div class="topcoat-navigation-bar__item left quarter">' + '<a class="topcoat-icon-button--quiet back-button" href="#">' + '<span class="topcoat-icon topcoat-icon--back"></span>' + '</a>' + '</div>' + '<div class="topcoat-navigation-bar__item center half">' + '<h1 class="topcoat-navigation-bar__title">Geometrixx</h1>' + '</div>' + '</div>' + '<div class="page-body scroller">' + '<img class="small-pic" src="../assets/img/' + product.smallPic + '" />' + '<h1 class="product-name">' + product.name + '</h1>' + '<h2 class="category">' + product.category + '</h2>' + '<img src="../assets/css/images/star' + product.rating + '.png" />' + '<h2 class="price">' + product.price + '</h2>' + '<a class="topcoat-icon-button topcoat-button--cta">' + '<span class="topcoat-icon icon-facebook"></span>' + '</a> ' + '<a class="topcoat-icon-button topcoat-button--cta">' + '<span class="topcoat-icon icon-twitter"></span>' + '</a>' + '</div>';

Using Templates

<div class="topcoat-navigation-bar"> <div class="topcoat-navigation-bar__item left quarter"> <a class="topcoat-icon-button--quiet back-button" href="#"> <span class="topcoat-icon topcoat-icon--back"></span> </a> </div> <div class="topcoat-navigation-bar__item center half"> <h1 class="topcoat-navigation-bar__title">Geometrixx</h1> </div> </div> <div class="page-body scroller"> <img class="small-pic" src="assets/img/{{this.smallPic}}"/> <h1 class="product-name">{{this.name}}</h1> <h2 class="category">{{this.category}}</h2> <img src="assets/css/images/star{{this.rating}}.png"/> <h2 class="price">{{this.price}}</h2> <a class="topcoat-icon-button topcoat-button--cta facebook-button"> <span class="topcoat-icon icon-facebook"></span> </a> <a class="topcoat-icon-button topcoat-button--cta twitter-button"> <span class="topcoat-icon icon-twitter"></span> </a> </div>

Templates

Provide Structure to Your Application
MV*

Model

var Product = function() {

    this.url = "/product";

    this.validate = function() {

    };

});

View

var ProductView = function () {

    this.initialize = function () {
    };

    this.render = function() {
    };

}

Controller

var JSONPAdapter = function(url) {

    this.findById = function(id) {
        return $.ajax({url: url + "/" + id, dataType: "jsonp"});
    }

    this.findByName = function(searchKey) {
        return $.ajax({url: url + "?name=" + searchKey, dataType: "jsonp"});
    }

}

Use Native Scrolling

#scroller { overflow: auto; -webkit-overflow-scrolling: touch; position: absolute; top: 48px; bottom: 0px; left: 0px; right: 0px; }

Use CSS Transitions
+
Hardware Acceleration

Left Center Right


class="left"
Left Center Right


class="center"
Left Center Right


class="right"

Slow

.page { position: absolute;top: 0;left: 0;width: 100%;height: 100%; } .left { left: -100%; } .center { left: 0; } .right { left: 100%; } .transition { transition-duration: .25s; }

Fast

.page {
    position: absolute;top: 0;left: 0;width: 100%;height: 100%;
    transform: translate3d(0, 0, 0);
}
.left {
    transform: translate3d(-100%, 0, 0);
}
.center {
    transform: translate3d(0, 0, 0);
}
.right {
    transform: translate3d(100%, 0, 0);
}
.transition {
    transition-duration: .25s;
}

https://github.com/ccoenraets/PageSlider

Frameworks

AngularJS

  1. MVC architecture for client app
  2. Dependency Injection
  3. Directives
  4. Data binding
  5. Templates

app.js

angular.module('myApp', ['controllers', 'services'])

index.html

<body ng-app="myApp"></body>

services.js

angular.module('services', ['ngResource']) .factory('Product', function ($resource) { return $resource('http://localhost/product/:id'); }) .factory('Offer', function ($resource) { return $resource('http://localhost/offer/:id'); }) });

controllers.js

angular.module('controllers', ['services']) .controller('ProductList', function ($scope, Product) { $scope.products = Product.query(); $scope.deleteItem = function(product) { product.$delete({id: product.id}, function() { $scope.products = Product.query(); }); }; });

product-list.html

<div ng-repeat="product in products">

    <a href="#/product-detail/{{product.id}}">
        {{product.name}}
        <img ng-if="product.pic" ng-src="{{product.pic}}">
    </a>

    <a ng-click="deleteItem(product)">Delete</a>

</div>

Routing

myApp.config(function($stateProvider) { $stateProvider .state('app', { url: "/app", templateUrl: "templates/app.html" }) .state('app.product-list', { url: "/products", templateUrl: "templates/product-list.html", controller: ProductList }) .state('app.product-details', { url: "/products/:productId", templateUrl: "templates/product.html", controller: ProductDetails }) });
  1. UI Layer on top of AngularJS
  2. UI Components
  3. UI Components are AngularJS Directives
  4. UI Patterns
  5. CLI built on top of Cordova

Performance

Native Focused

Beautifully Designed

Ionicons

Over 500 font-icons
ionicons.com

Lists

<div class="list"> <div class="item item-divider"> Candy Bars </div> <a class="item" href="#"> Butterfinger </a> <a class="item" href="#"> Kit Kat </a> </div>

Complex Lists

  • AngularJS Directive
  • Buttons exposed by swiping
  • Reorder
  • Delete
<ion-list> <ion-item ng-repeat="item in items" option-buttons="buttons" class="item-thumbnail-left"> <img ng-src="{{ item.pic }}"> <h2>{{ item.name }}</h2> <p>{{ item.quote }}</p> </ion-item> </ion-list>

Tabs

  • Nested views
  • Each tab has its own nav history
<ion-tabs tabs-type="tabs-icon-only"> <ion-tab title="Home" icon="ion-star"> <ion-nav-view></ion-nav-view> </ion-tab> <ion-tab title="Reviews" icon="ion-down"> <ion-nav-view></ion-nav-view> </ion-tab> <ion-tab title="Chat" icon="ion-chat"> <ion-nav-view></ion-nav-view> </ion-tab> </ion-tabs>

Side Menu

<ion-side-menu side="left"> <header class="bar bar-header"> <div class="title">Projects</div> </header> <ion-content has-header="true"> <div class="list"> <a href="#/work" class="item"> Work </a> <a href="#/home" class="item"> Home </a> </div> </ion-content> </ion-side-menu>

Slide Box

<ion-slide-box> <ion-slide>Slide 1</ion-slide> <ion-slide>Slide 2</ion-slide> <ion-slide>Slide 3</ion-slide> </ion-slide-box>

Action Sheet

$ionicActionSheet.show({ titleText: 'Modify your album', buttons: [ { text: 'Share' }, { text: 'Move' }, ], destructiveText: 'Delete', cancelText: 'Cancel', buttonClicked: function(index) { console.log('BUTTON CLICKED', index); return true; } });

Pull to Refresh

<ion-content> <ion-refresher on-refresh="refreshData()"> </ion-refresher> <!-- content --> </ion-content>