Node v6
Starting with v0.19 Astro must be used with Node v6+!

PhoneGap Day Workshop

In this workshop you will build a simple mobile shopping app for iOS called Velo. By the end you will have changed the way the website is presented in an app, added a few native features and integrated the Cordova barcode scanner plugin. Before you start, make sure you've installed all the prerequisites by following the quick start guide.

1. Install and Run the Sample Site

Run the following commands:

git clone --branch phonegapday
cd astro-tutorial
npm install
cd site
npm install

Great, you should now have everything ready to get started. We wrote a sample website for you to experiment with. To run the server, run:

node index.js

Go to the demo site at http://localhost:5000 to check it out. Leave Node running - you'll need the site running in the steps ahead.

(If you get stuck, you can checkout the final code sample at the end of this tutorial).

2. Open and Run the App

First, open up the iOS project in XCode and leave that in the background.

$ open ../ios/velo.xcworkspace

Next, open app/app.js in your favourite text editor (XCode isn't great at formatting Javascript). We're going to point the app to the sample site you have running. Change the baseUrl to your IP address.

// Enter your site url here
var baseUrl = 'http://<Your IP Address>:5000/';

Now build and run the app on Xcode to check it out (running it in the simulator is fine). You should see your website running in the app.

3. Make your website "app-aware"

The next step will be to make your website "app-aware" by this we mean, changing how the website looks when within the context of an app. Basically, we're going to remove HTML header and footer.

In your editor of choice, open the HandleBars partial site/views/layouts/main.hbs. Next, insert this right before the closing </head> tag:

<script src=""></script>
        var el = document.documentElement;
        var className = 'inApp';

You can now access the Astro JavaScript API on the window.Astro object. We're also adding a inApp class to the HTML document so that we can style the app slightly differently when viewed in an app.

4. Add the Header Bar

We want to leverage a native header bar for our application. In app.js, add the HeaderBarPlugin. Make sure you add a comma after the second last plugin.

) {

Now you're ready to use the HeaderBarPlugin. Your next step is to initialize the plugin. Initializing a plugin returns a promise.

// Initialize plugins
var headerPromise = HeaderBarPlugin.init();

Put the following code snippet under the promise block that calls layout.setContentView. This will create a basic (but blank) header bar.

// Add the header bar once `layoutPromise` and `headerPromise` are fulfilled.
Promise.join(layoutPromise, headerPromise, function(layout, headerBar) {
    // Set up the header bar


What we've done is added a basic header bar to the layout of our app. Because the header bar and main navigation view depend on each other for various things, we need to do a little more work to hook the two together. Below, you'll see that we're able to assign icons to the header bar when we navigate in our main view. We also ensure that when the back button is pressed we navigate the main view backwards.

// Delete this code

// Replace it with this code
Promise.join(mainNavigationPromise, headerPromise, function(mainNavigationView, headerBar) {
    // Now, let's hook up the main navigation to the header bar

    // Navigate to the base url and set the icons
        header: {
            leftIcon: {
                id: 'barcodeScanner',
                imageUrl: 'file:///barcodeScanner.png'
            centerIcon: {
                id: 'logo',
                imageUrl: 'file:///velo.png'
            rightIcon: {
                id: 'cart',
                imageUrl: 'file:///cart.png'

    // When the back button is pressed navigate back
    headerBar.on('click:back', function() {

Now re-run the app in XCode to check out your new header bar. You should be able to navigate to an item and then tap the back button to go back to the beginning.

5. Add the Barcode Scanner

The tutorial already has the Cordova Barscanner libraries included, so we can take advantage of it right away. If you want see how to add your own Cordova plugins later you can visit the Astro Cordova Documentation.

Insert the following code right after your last snippet where you listened to the click:back event.

headerBar.on('click:barcodeScanner', function() {
    window.cordova.plugins.barcodeScanner.scan(function(result) {
        if (result.cancelled) {

        var barcodeMapping = {
            'notfound': '/city-bike/',
            '9781472322531': '/road-bike/',
            '011091212197'  : '/commuter/',
            '5024095212198': '/road-bike/'

        var resultURL = baseUrl + (result.text in barcodeMapping ? barcodeMapping[result.text] : barcodeMapping['notfound']);

        console.log('We got a barcode\n' +
            'Result: ' + result.text + '\n' +
            'Format: ' + result.format + '\n' +
            'Cancelled: ' + result.cancelled);
    }, function(error) {
        console.log('Scanning failed: ' + error);


6. Add a Flyout Drawer

Again, in app.js, let's include the DrawerPlugin.

) {

Now that you have the DrawerPlugin, let's initialize it below the HeaderBarPlugin from before.

// Initialize plugins
var headerPromise = HeaderBarPlugin.init();
var drawerPromise = DrawerPlugin.init();

Under the Promise statement that adds the header bar to the layout, set the main content area of our DrawerPlugin to the AnchoredLayoutPlugin instance with the following code snippet

// Set the drawer's content area once `drawerPromise` and `layoutPromise` are fulfilled.
Promise.join(drawerPromise, layoutPromise, function(drawer, layout) {

At the bottom of the file, delete the Promise which sets the layout as the application's main view. In its place, tell the Application to use the DrawerPlugin instance as its main view with the following code snippet:

// Delete this code
layoutPromise.then(function(layout) {

// Replace it with this code
drawerPromise.then(function(drawer) {

Now you have a DrawerPlugin instance, but it does not yet have any drawers. The DrawerPlugin plugin exposes three content areas: the left drawer, the right drawer and the main content area. Let's create a WebViewPlugin that we'll add to one of the drawer's content areas.

// Initialize plugins
var headerPromise = HeaderBarPlugin.init();
var drawerPromise = DrawerPlugin.init();
var cartWebViewPromise = WebViewPlugin.init();

At the bottom of the file, underneath the last Promise to set the DrawerPlugin instance to the main view, add code to navigate the webview to the cart and assign the view to the right drawer:

// Navigate the cart webview to the right url
cartWebViewPromise.then(function(webView) {
    webView.navigate(baseUrl + "/cart");
    // Don't navigate when links are pressed

// Set the right drawer view to the cart web view instance once the promises have been fulfilled.
var rightDrawerPromise = Promise.join(cartWebViewPromise, drawerPromise, function(cartWebView, drawer) {
    var rightDrawer = drawer.initRightMenu(cartWebView);
    // We want the right drawer later, so we will return it
    return rightDrawer;

Now if you run the app in XCode you'll have a right drawer! To check out your right drawer, swipe the header bar left. Not ideal, but don't worry, next we're going to show the right right drawer by tapping on the 'cart' icon.

7. Show and Hide the Drawer

Now, let's hook up the 'cart' button in the header bar to open and close the right drawer.

The HeaderBarPlugin instance emits events when buttons are pressed. The event name is has a format of click:<id>. If you remember from when we created the HeaderBar we assigned ids to the buttons. We gave the cart an id of cart, so we have to listen for the click:cart event. Add this code at the bottom.

// Bind the `rightIconClick` event once the promises have been fulfilled.
Promise.join(rightDrawerPromise, headerPromise, function(rightDrawer, header) {
    header.on('click:cart', function() {

Save the app.js file and re-run the app in Xcode. Tap the bag icon in the header bar to open and close the right drawer.

8. Add Cart Interaction

In this section we're going to make the right drawer open when a user adds an item to their cart.

Because we added the script earlier, you have access to the window.Astro object. Next you will add some code to the site/public/js/cart-add.js file that will trigger an event when a user adds an item to their cart. Attach a handler to the cart button click event with the code below:

$(".js-add").click(function() {

This handler triggers a custom addToCartClicked event through Astro. By binding addToCartClicked through Astro, we are now able to listen for this event in our app.js file. At the bottom of the app.js file, after the last Promise to toggle the right drawer, listen for the addToCartClicked event and open the drawer with the code below:

// Add a handler on the main web view to open drawer when 'addToCartClicked' event happens.
                function(mainNavigationView, rightDrawer, cartWebView) {
    mainNavigationView.on('addToCartClicked', function() {
        // Open the right drawer;
        // Let the cart view know that something has been added

Save the app.js file and re-run the app in XCode. Congrats on finishing the tutorial! You can checkout the final tutorial code to see how your code compares.