Table of Contents

Introduction

Overview

This page contains code snippets for use in the talk 6 Minute Apps with NativeScript: A Race Against Time

Find the source code for this page, and the full code used in the presentation on Github:

My First App: Hello World of NativeScript

Demo: Creating an app

Create the first app:

tns create app1

Run in the iOS simulator:

tns run ios

App 2: Informational app

Main page

Demo: Adding text to an app

First, we'll create a page and add some text.

<Page>
  <ScrollView>
    <StackLayout>

      <!-- Title -->
      <Label text="Welcome to Tekmo!" class="title" />

      <Label textWrap="true" text="Tekmo is a small online retailer specializing in retro video games sales and services. We're based in Louisville, KY." />

      <!-- Sub Title -->
      <Label text="Our Mission" class="sub-title" />

      <Label textWrap="true" text="We exist to bring our customers the best in retro gaming at an affordable price. We love to revisit and replay the games that shaped our childhoods, and believe that through sharing our love, we can help shape another generation of retro gaming geeks." />

      <!-- Sub Title -->
      <Label text="History" class="sub-title" />

      <Label textWrap="true" text="In the early 90's, it all started with Rescue Pups. This multi-player vintage platformer brought the Rambo-style side-scrolling gun fights to life in our living room. True, it was impossible to beat with the 3 lives you got by default, but that's why everyone knew the 50 lives code by heart: up, up, down, down, left, right, left, right, B, A, Start. And if you wanted a friend to play along, you could throw in the Select, Start at the end." />

      <Label textWrap="true" text="After Rescue Pups, it was the classics like Super Marshmallow Man and Vampire Valkyrie. Do you remember them? We do. And we loved it! We hosted sleep overs every weekend throughout the summer, stayed up too late, got in trouble, pretended to fall asleep, then crept downstairs in the middle of the night to continue the fun." />

      <Label textWrap="true" text="As we grew older, the games we played did too, but our love for the originals never stopped, and our passion for sharing these games with our friends and family grew." />

      <Label textWrap="true" text="After many year, we all started having kids of our own, and we found ourselves wanting to raise our kids in our footsteps (without the midnight gaming marathons, of course). We wanted our kids to relive the adventure. Relive the thrills. Relive the classics. Tekmo was born." />

      <Label textWrap="true" text="We remembered how cool it was to get 50 lives, so now our kids could enjoy playing through Rescue Pups for hours." />

      <Label textWrap="true" text="And then there was Vampire Valkyrie: a side-scrolling adventure into the depths of Transylvania seeking out Dracula and his minions. After gathering your supplies in local towns, you embarked on a journey through the countryside to rescue trapped village people. We never forget to bring your stake and darn your garlic. And when we finally meet Dracula face-to-face, holy water didn't save us. And now, the fate of the world is on another 7 year old's shoulders." />

      <Label textWrap="true" text="Lastly, Super Marshmallow Man is the one that pushed us over the edge with its iconic landscapes filled with clouds and wonderous sky scenes. We regularly play through the 12 worlds of Mallow Kingdom while avoiding the hungry Chompers with our friends and families: the best part is still watching someone get too close to the flames and melt!" />    

    </StackLayout>
  </ScrollView>
</Page>

Now, let's style our app with CSS.

Demo: Adding some style

Add a file named main-page.css. Add the styling:

/* Color the background */
Page {
    background-color: lightblue;
}

/* Give text some room around the edges */
Label {
    margin-left: 10;
    margin-right: 10;
    margin-bottom: 10;
}

/* Make the title stand out */
.title {
    font-size: 30;
    horizontal-align: center;
    margin: 20;
}

/* Sub titles are a bit smaller */
.sub-title {
    font-size: 20;
}

App 3: Data submission

Main Page

Demo: Navigating to another page

Add a <Button /> to the main page of the app.

<Page>
    <StackLayout>
        <Label text="Welcome to Tekmo!" class="title" />

        <Button id="contact-us" text="Contact Us" tap="onContactUsTap" />
    </StackLayout>
</Page>

Wire the button to navigate to the Contact Us page.

var frameModule = require("ui/frame");

exports.onContactUsTap = function (args) {
    console.log("Navigating to the Contact Us page.");
    frameModule.topmost().navigate("contact-us");
};

Building the Contact Us page

Demo: Navigating to another page

Add a Contact Us page with content.

<Page loaded="onLoaded">
    <StackLayout>
        <!-- title -->
        <Label text="Contact Us" class="title" />

        <!-- data submission -->
        <Label textWrap="true" text="Contact us by submitting a message below." />

        <TextField id="subject" hint="Enter a subject..." />
        <TextView id="message" hint="Enter a message..." />

        <Button text="Submit" tap="onTap" />

    </StackLayout>
</Page>

Add a Contact Us JavaScript page and code.

var httpModule = require("http");
var dialogModule = require("ui/dialogs");
var page;

// capture a reference to the current page after it loads
// we'll need this later to access the text boxes on the screen
exports.onLoaded = function onLoaded(args) {
    page = args.object;
};

exports.onTap = function onTap(args) {

    // step 1: get data out of text field and text view
    var subject = page.getViewById("subject").text;
    var message = page.getViewById("message").text;

    console.log("Submitting: " + subject + ", " + message);

    // step 2: submit data to Tekmo

    // step 2.1: assemble data to send
    var data = JSON.stringify({
        "subject": subject,
        "message": message
    });

    // step 2.2: send the request toa remote web service
    httpModule
        .request({
            url: "https://nstweet.brosteins.com/api/message",
            method: "POST",
            headers: { "Content-Type": "application/json" },
            content: data })
        .then(function(response) {
            // success: tell the user
            console.log("Received a response: HTTP " + response.statusCode);
            dialogModule.alert("Thank you for your feedback!");
        }, function(e) {
            // error: oh snap
            console.log("Error occurred: " + e);
            dialogModule.alert("We couldn't send your message right now. Try again later.");
        });
};

App 4: Product listing

Product page

Demo: Using a grid layout

Add a simple grid to the Products page.

<Page>
  <ScrollView>
    <GridLayout rows="*,*,*,*" columns="*,*" width="300" height="600">

        <!-- highlighted product -->    
        <StackLayout row="0" col="0" colSpan="2" class="tile highlight">

        </StackLayout>

        <!-- Couch Commander -->
        <StackLayout row="1" col="0" class="tile">

        </StackLayout>

        <!-- Mummy Madness -->
        <StackLayout row="1" col="1" class="tile">

        </StackLayout>

        <!-- Pyro Robots -->
        <StackLayout row="2" col="0" class="tile">

        </StackLayout>

        <!-- Rescue Pups -->
        <StackLayout row="2" col="1" class="tile">

        </StackLayout>

        <!-- Vampire Valkyrie -->
        <StackLayout row="3" col="0" class="tile">

        </StackLayout>
    </GridLayout>
  </ScrollView>
</Page>

Add some styling to the app.css file.

/* Give text some room around the edges */
Label {
    margin-left: 10;
    margin-right: 10;
    margin-bottom: 10;
}

/* Make the title stand out */
.title {
    font-size: 30;
    horizontal-align: center;
    margin: 20;
}

/* Sub titles are a bit smaller */
.sub-title {
    font-size: 20;
}

/* Give the page a default background color */
Page {
    background-color: #EFEFEF;
}

/* color each tile's background */
.tile {
    background-color: #FFFFFF;
    margin: 2;
}

We should add a title for each product we're selling

Demo: Adding a title to the tiles

Add stack layouts and labels for each game title

<Page>
  <ScrollView>
    <GridLayout rows="*,*,*,*" columns="*,*" width="300" height="600">

        <!-- highlighted product -->    
        <StackLayout row="0" col="0" colSpan="2" class="tile highlight">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Super Marshmallow Man" textWrap="true" />
            </StackLayout>

        </StackLayout>

        <!-- Couch Commander -->
        <StackLayout row="1" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Couch Commander" textWrap="true" /> 
            </StackLayout>

        </StackLayout>

        <!-- Mummy Madness -->
        <StackLayout row="1" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Mummy Madness" textWrap="true" /> 
            </StackLayout>

        </StackLayout>

        <!-- Pyro Robots -->
        <StackLayout row="2" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Pyro Robots" textWrap="true" /> 
            </StackLayout>

        </StackLayout>

        <!-- Rescue Pups -->
        <StackLayout row="2" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Rescue Pups" textWrap="true" /> 
            </StackLayout>

        </StackLayout>

        <!-- Vampire Valkyrie -->
        <StackLayout row="3" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Vampire Valkyrie" textWrap="true" /> 
            </StackLayout>

        </StackLayout>
    </GridLayout>
  </ScrollView>
</Page>

Now, style the titles.

/* Give text some room around the edges */
Label {
    margin-left: 10;
    margin-right: 10;
    margin-bottom: 10;
}

/* Make the title stand out */
.title {
    font-size: 30;
    horizontal-align: center;
    margin: 20;
}

/* Sub titles are a bit smaller */
.sub-title {
    font-size: 20;
}

/* Give the page a default background color */
Page {
    background-color: #EFEFEF;
}

/* color each tile's background */
.tile {
    background-color: #FFFFFF;
    margin: 2;
}

/* solid band across the top of each tile */
.tile-title {
    background-color: #99ccff;
}

/* the title of each game */
.tile-title Label {
    font-size: 14;
    color: black;
    margin-top: 5;
}

Now, let's add an image and a price.

Demo: Adding a game image and price

Add an <Image /> tag to each of the tiles.

<Page>
  <ScrollView>
    <GridLayout rows="*,*,*,*" columns="*,*" width="300" height="600">

        <!-- highlighted product -->    
        <StackLayout row="0" col="0" colSpan="2" class="tile highlight">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Super Marshmallow Man" textWrap="true" />
            </StackLayout>

            <!-- tile content goes here -->
            <Image src="res://supermarshmallowman" />
            <Label textWrap="true" text="Escape from certain death in this wild adventure!" />
            <Label text="$34.99" class="price" />

        </StackLayout>

        <!-- Couch Commander -->
        <StackLayout row="1" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Couch Commander" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://couchcommander" />
            <Label text="$24.99" class="price" />
        </StackLayout>

        <!-- Mummy Madness -->
        <StackLayout row="1" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Mummy Madness" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://mummymadness" />
            <Label text="$32.99" class="price" />
        </StackLayout>

        <!-- Pyro Robots -->
        <StackLayout row="2" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Pyro Robots" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://pyrorobots" />
            <Label text="$19.99" class="price" />
        </StackLayout>

        <!-- Rescue Pups -->
        <StackLayout row="2" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Rescue Pups" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://rescuepups" />
            <Label text="$9.99" class="price" />
        </StackLayout>

        <!-- Vampire Valkyrie -->
        <StackLayout row="3" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Vampire Valkyrie" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://vampirevalkyrie" />
            <Label text="$21.99" class="price" />
        </StackLayout>
    </GridLayout>
  </ScrollView>
</Page>

And style the images to be smaller, also coloring the price green.

/* Give text some room around the edges */
Label {
    margin-left: 10;
    margin-right: 10;
    margin-bottom: 10;
}

/* Make the title stand out */
.title {
    font-size: 30;
    horizontal-align: center;
    margin: 20;
}

/* Sub titles are a bit smaller */
.sub-title {
    font-size: 20;
}

/* Give the page a default background color */
Page {
    background-color: #EFEFEF;
}

/* color each tile's background */
.tile {
    background-color: #FFFFFF;
    margin: 2;
}

/* solid band across the top of each tile */
.tile-title {
    background-color: #99ccff;
}

/* the title of each game */
.tile-title Label {
    font-size: 14;
    color: black;
    margin-top: 5;
}

/* make the price stand out and align to the right */
.price {
    color: #009933;
    text-align: right;
}

/* shrink images a bit */
Image {
    width: 80;
    height: 80;
}

The highlighted tile has text scrolling out, so let's re-organize.

Demo: Refactoring the highlighted tile

Lastly, let's make the highlighted tile look fancy.

<Page>
  <ScrollView>
    <GridLayout rows="*,*,*,*" columns="*,*" width="300" height="600">

        <!-- highlighted product -->    
        <StackLayout row="0" col="0" colSpan="2" class="tile highlight">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Super Marshmallow Man" textWrap="true" />
            </StackLayout>

            <!-- align content horizontally image on the left, content on the right -->
            <StackLayout orientation="horizontal">

                <!-- image on the left -->
                <Image src="res://supermarshmallowman" />

                <!-- content is on the right in a vertical orientation -->
                <StackLayout>
                    <Label textWrap="true" text="Escape from certain death in this wild adventure!" />
                    <Label text="$34.99" class="price" />
                </StackLayout>
            </StackLayout>
        </StackLayout>

        <!-- Couch Commander -->
        <StackLayout row="1" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Couch Commander" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://couchcommander" />
            <Label text="$24.99" class="price" />
        </StackLayout>

        <!-- Mummy Madness -->
        <StackLayout row="1" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Mummy Madness" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://mummymadness" />
            <Label text="$32.99" class="price" />
        </StackLayout>

        <!-- Pyro Robots -->
        <StackLayout row="2" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Pyro Robots" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://pyrorobots" />
            <Label text="$19.99" class="price" />
        </StackLayout>

        <!-- Rescue Pups -->
        <StackLayout row="2" col="1" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Rescue Pups" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://rescuepups" />
            <Label text="$9.99" class="price" />
        </StackLayout>

        <!-- Vampire Valkyrie -->
        <StackLayout row="3" col="0" class="tile">

            <!-- solid band at top of tile with title -->
            <StackLayout class="tile-title">
                <Label text="Vampire Valkyrie" textWrap="true" /> 
            </StackLayout>

            <!-- tile contents here -->
            <Image src="res://vampirevalkyrie" />
            <Label text="$21.99" class="price" />
        </StackLayout>
    </GridLayout>
  </ScrollView>
</Page>

And style.

/* Give text some room around the edges */
Label {
    margin-left: 10;
    margin-right: 10;
    margin-bottom: 10;
}

/* Make the title stand out */
.title {
    font-size: 30;
    horizontal-align: center;
    margin: 20;
}

/* Sub titles are a bit smaller */
.sub-title {
    font-size: 20;
}

/* Give the page a default background color */
Page {
    background-color: #EFEFEF;
}

/* color each tile's background */
.tile {
    background-color: #FFFFFF;
    margin: 2;
}

/* solid band across the top of each tile */
.tile-title {
    background-color: #99ccff;
}

/* the title of each game */
.tile-title Label {
    font-size: 14;
    color: black;
    margin-top: 5;
}

/* make the price stand out and align to the right */
.price {
    color: #009933;
    text-align: right;
}

/* shrink images a bit */
Image {
    width: 80;
    height: 80;
}

/* the first top element should be more prominent
   override the existing tile styles */
.highlight .tile-title {
    font-weight: bold;
    background-color: #6699ff;
}

.highlight .tile-title Label {
    font-size: 18;
}

.highlight .price {
    font-weight: bold;
    color: red;
}

.highlight Image {
    width: 100;
    height: 100;
}

Demo: Using a grid layout


App 5: I caught 'em all

My kids love Pokemon, so I had to build them a pokedex app. Let's do it together.

I want to scroll through / navigate between Pokemon. Let's add some navigation.

Demo: Using a grid layout

Add a basic grid to the screen.

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
    <GridLayout rows="3*,2*,45*" columns="*,*">

      <!-- 
        left nav
        if you tap anything, the onTap event is called 
        -->
      <Image id="prevNav" row="0" col="0" rowSpan="2"
        src="~/images/left.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="prevNavText" row="0" col="0" tap="{{ onTap }}" 
        orientation="horizontal" horizontalAlignment="center" class="nav nav-prev">

        <!-- Custom font from font-awesome of a Left Arrow -->
        <Label text="&#xf053;" class="font-awesome nav-arrow" />

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="prevId" text="{{ prevId }}" class="nav-id" />
        <Label id="prevNavName" text="{{ prevName }}" class="nav-name" />
      </StackLayout>

      <!-- 
        right nav 
        if you tap anything, the onTap event is called 
        -->
      <Image id="nextNav" row="0" col="1" rowSpan="2"
        src="~/images/right.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="nextNavText" row="0" col="1" tap="{{ onTap }}"
        orientation="horizontal" horizontalAlignment="center" class="nav nav-next">

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="nextNavName" text="{{ nextName }}" class="nav-name" />
        <Label id="nextId" text="{{ nextId }}" class="nav-id" />

        <!-- Custom font from font-awesome of a Right Arrow -->
        <Label text="&#xf054;" class="font-awesome nav-arrow" />
      </StackLayout>

      <!--
        center-aligned Pokemon name
        tapping on the name, calls a text-to-speech
      -->
      <StackLayout orientation="horizontal" row="1" colSpan="2" horizontalAlignment="center"
        tap="{{ onSayName }}">

        <!-- data-bound name and id -->
        <Label id="name" text="{{ name }}" />
        <Label id="id" text="{{ id }}" />

        <Button text="&#xf04b;" class="font-awesome play" tap="{{ onSayName }}" />
      </StackLayout>


    </GridLayout>
</Page>

Add the page load event to bind to our data source.

var viewModel = require("./pokedex-view-model");
var page;

function onLoaded(args) {
    page = args.object;
    page.bindingContext = viewModel.pokedexViewModel;

    //
    // the pokedex view model has several properties
    // - image of the pokemen, i.e. 001.png
    // - name
    // - description
    // - id number
    // - height
    // - category
    // - weight
    // - stats (HP, Attack, Defense, Special Attack, Special Defense, and Speed)
    // - prev pokemon name & id
    // - next pokemon name & id   
    // - navigate to prev/next pokemon
    // - say the name
    //
}
exports.onLoaded = onLoaded;

Data binding images

Now, let's add the data bound image of the Pokemon.

Demo: Adding a data bound image

Add a data bound image is pretty easy, just add the tag and {{ }} syntax.

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
    <GridLayout rows="3*,2*,45*" columns="*,*">

      <!-- 
        left nav
        if you tap anything, the onTap event is called 
        -->
      <Image id="prevNav" row="0" col="0" rowSpan="2"
        src="~/images/left.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="prevNavText" row="0" col="0" tap="{{ onTap }}" 
        orientation="horizontal" horizontalAlignment="center" class="nav nav-prev">

        <!-- Custom font from font-awesome of a Left Arrow -->
        <Label text="&#xf053;" class="font-awesome nav-arrow" />

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="prevId" text="{{ prevId }}" class="nav-id" />
        <Label id="prevNavName" text="{{ prevName }}" class="nav-name" />
      </StackLayout>

      <!-- 
        right nav 
        if you tap anything, the onTap event is called 
        -->
      <Image id="nextNav" row="0" col="1" rowSpan="2"
        src="~/images/right.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="nextNavText" row="0" col="1" tap="{{ onTap }}"
        orientation="horizontal" horizontalAlignment="center" class="nav nav-next">

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="nextNavName" text="{{ nextName }}" class="nav-name" />
        <Label id="nextId" text="{{ nextId }}" class="nav-id" />

        <!-- Custom font from font-awesome of a Right Arrow -->
        <Label text="&#xf054;" class="font-awesome nav-arrow" />
      </StackLayout>

      <!--
        center-aligned Pokemon name
        tapping on the name, calls a text-to-speech
      -->
      <StackLayout orientation="horizontal" row="1" colSpan="2" horizontalAlignment="center"
        tap="{{ onSayName }}">

        <!-- data-bound name and id -->
        <Label id="name" text="{{ name }}" />
        <Label id="id" text="{{ id }}" />

        <Button text="&#xf04b;" class="font-awesome play" tap="{{ onSayName }}" />
      </StackLayout>

      <!-- 
        THIRD ROW - Pokemon Info and Stats

        Scroll view with a left and right column
       -->
      <ScrollView row="2" col="0" colSpan="2">
        <!-- content area -->
        <GridLayout rows="auto" columns="*,*">

          <!-- left column -->
          <StackLayout orientation="horizontal" row="0" col="0" class="cell">
            <Border borderRadius="20">
              <Image id="pokemon" src="{{ imageSource }}" stretch="aspectFit" />
            </Border>
          </StackLayout>

          </GridLayout>
        </ScrollView>
    </GridLayout>
</Page>

Adding more information

Adding more information is an exercise in UI layout. I'll add the right column.

Demo: Adding a right column wiht data bound fields

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
    <GridLayout rows="3*,2*,45*" columns="*,*">

      <!-- 
        left nav
        if you tap anything, the onTap event is called 
        -->
      <Image id="prevNav" row="0" col="0" rowSpan="2"
        src="~/images/left.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="prevNavText" row="0" col="0" tap="{{ onTap }}" 
        orientation="horizontal" horizontalAlignment="center" class="nav nav-prev">

        <!-- Custom font from font-awesome of a Left Arrow -->
        <Label text="&#xf053;" class="font-awesome nav-arrow" />

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="prevId" text="{{ prevId }}" class="nav-id" />
        <Label id="prevNavName" text="{{ prevName }}" class="nav-name" />
      </StackLayout>

      <!-- 
        right nav 
        if you tap anything, the onTap event is called 
        -->
      <Image id="nextNav" row="0" col="1" rowSpan="2"
        src="~/images/right.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="nextNavText" row="0" col="1" tap="{{ onTap }}"
        orientation="horizontal" horizontalAlignment="center" class="nav nav-next">

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="nextNavName" text="{{ nextName }}" class="nav-name" />
        <Label id="nextId" text="{{ nextId }}" class="nav-id" />

        <!-- Custom font from font-awesome of a Right Arrow -->
        <Label text="&#xf054;" class="font-awesome nav-arrow" />
      </StackLayout>

      <!--
        center-aligned Pokemon name
        tapping on the name, calls a text-to-speech
      -->
      <StackLayout orientation="horizontal" row="1" colSpan="2" horizontalAlignment="center"
        tap="{{ onSayName }}">

        <!-- data-bound name and id -->
        <Label id="name" text="{{ name }}" />
        <Label id="id" text="{{ id }}" />

        <Button text="&#xf04b;" class="font-awesome play" tap="{{ onSayName }}" />
      </StackLayout>

      <!-- 
        THIRD ROW - Pokemon Info and Stats

        Scroll view with a left and right column
       -->
      <ScrollView row="2" col="0" colSpan="2">
        <!-- content area -->
        <GridLayout rows="auto" columns="*,*">

          <!-- left column -->
          <StackLayout orientation="horizontal" row="0" col="0" class="cell">
            <Border borderRadius="20">
              <Image id="pokemon" src="{{ imageSource }}" stretch="aspectFit" />
            </Border>
          </StackLayout>


          <!-- right column -->
          <StackLayout row="0" col="1" class="cell">
            <!-- description -->
            <Label id="description" textWrap="true" text="{{ description }}" class="description"
              tap="{{ onReadDescription }}" />

            <!-- info -->
            <GridLayout rows="auto,auto,auto,auto" columns="*,*" class="info-container">
              <Label text="Height" row="0" col="0" class="info info-title" />
              <Label text="{{ height }}" row="1" col="0" class="info info-value" />

              <Label text="Category" row="0" col="1" class="info info-title" />
              <Label text="{{ category }}" row="1" col="1" class="info info-value" />

              <Label text="Weight" row="2" col="0" class="info info-title" />
              <Label text="{{ weight }}" row="3" col="0" class="info info-value" />
            </GridLayout>

            </StackLayout>

          </GridLayout>
        </ScrollView>
    </GridLayout>
</Page>

Animations

Now, let's have some fun by adding an animation to show the Pokemon's stats

Demo: Adding animation

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded">
    <GridLayout rows="3*,2*,45*" columns="*,*">

      <!-- 
        left nav
        if you tap anything, the onTap event is called 
        -->
      <Image id="prevNav" row="0" col="0" rowSpan="2"
        src="~/images/left.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="prevNavText" row="0" col="0" tap="{{ onTap }}" 
        orientation="horizontal" horizontalAlignment="center" class="nav nav-prev">

        <!-- Custom font from font-awesome of a Left Arrow -->
        <Label text="&#xf053;" class="font-awesome nav-arrow" />

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="prevId" text="{{ prevId }}" class="nav-id" />
        <Label id="prevNavName" text="{{ prevName }}" class="nav-name" />
      </StackLayout>

      <!-- 
        right nav 
        if you tap anything, the onTap event is called 
        -->
      <Image id="nextNav" row="0" col="1" rowSpan="2"
        src="~/images/right.png" stretch="fill" tap="{{ onTap }}" />

      <StackLayout id="nextNavText" row="0" col="1" tap="{{ onTap }}"
        orientation="horizontal" horizontalAlignment="center" class="nav nav-next">

        <!-- Data-bound fields between {{ and }}, uses page's bindingContext object -->
        <Label id="nextNavName" text="{{ nextName }}" class="nav-name" />
        <Label id="nextId" text="{{ nextId }}" class="nav-id" />

        <!-- Custom font from font-awesome of a Right Arrow -->
        <Label text="&#xf054;" class="font-awesome nav-arrow" />
      </StackLayout>

      <!--
        center-aligned Pokemon name
        tapping on the name, calls a text-to-speech
      -->
      <StackLayout orientation="horizontal" row="1" colSpan="2" horizontalAlignment="center"
        tap="{{ onSayName }}">

        <!-- data-bound name and id -->
        <Label id="name" text="{{ name }}" />
        <Label id="id" text="{{ id }}" />

        <Button text="&#xf04b;" class="font-awesome play" tap="{{ onSayName }}" />
      </StackLayout>

      <!-- 
        THIRD ROW - Pokemon Info and Stats

        Scroll view with a left and right column
       -->
      <ScrollView row="2" col="0" colSpan="2">
        <!-- content area -->
        <GridLayout rows="auto" columns="*,*">

          <!-- left column -->
          <StackLayout orientation="horizontal" row="0" col="0" class="cell">
            <Border borderRadius="20">
              <Image id="pokemon" src="{{ imageSource }}" stretch="aspectFit" />
            </Border>
          </StackLayout>


          <!-- right column -->
          <StackLayout row="0" col="1" class="cell">
            <!-- description -->
            <Label id="description" textWrap="true" text="{{ description }}" class="description"
              tap="{{ onReadDescription }}" />

            <!-- info -->
            <GridLayout rows="auto,auto,auto,auto" columns="*,*" class="info-container">
              <Label text="Height" row="0" col="0" class="info info-title" />
              <Label text="{{ height }}" row="1" col="0" class="info info-value" />

              <Label text="Category" row="0" col="1" class="info info-title" />
              <Label text="{{ category }}" row="1" col="1" class="info info-value" />

              <Label text="Weight" row="2" col="0" class="info info-title" />
              <Label text="{{ weight }}" row="3" col="0" class="info info-value" />
            </GridLayout>

           <!-- stats -->
            <GridLayout rows="auto,auto" columns="auto,*,*,*,*,*,*,auto" class="stats">
              <Label text="Stats" row="0" col="0" colSpan="8" class="stats-title" />

              <StackLayout row="1" col="0" class="meter-first">
              </StackLayout>

              <StackLayout id="hpMeter" row="1" col="1" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="HP" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout id="attackMeter" row="1" col="2" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="Attack" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout id="defenseMeter" row="1" col="3" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="Defense" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout id="specialAttackMeter" row="1" col="4" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="Special Attack" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout id="specialDefenseMeter" row="1" col="5" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="Special Defense" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout id="speedMeter" row="1" col="6" class="meter">
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <StackLayout class="meter-value"></StackLayout>
                <Label text="Speed" textWrap="true" class="meter-label" />
              </StackLayout>

              <StackLayout row="1" col="7" class="meter-last">
              </StackLayout>
            </GridLayout>
            </StackLayout>

          </GridLayout>
        </ScrollView>
    </GridLayout>
</Page>

And now the javascript logic to fire the animations:

var viewModel = require("./pokedex-view-model");
var colorModule = require("color");
var animationModule = require("ui/animation");
var observableModule = require("data/observable");
var page;

function onLoaded(args) {
    page = args.object;
    page.bindingContext = viewModel.pokedexViewModel;

    //
    // the pokedex view model has several properties
    // - image of the pokemen, i.e. 001.png
    // - name
    // - description
    // - id number
    // - height
    // - category
    // - weight
    // - stats (HP, Attack, Defense, Special Attack, Special Defense, and Speed)
    // - prev pokemon name & id
    // - next pokemon name & id   
    // - navigate to prev/next pokemon
    // - say the name
    //

    //
    // listen for when the data-bound data source has a property changed event, then
    // fire the stats animation
    viewModel.pokedexViewModel
        .addEventListener(observableModule.Observable.propertyChangeEvent, 
            function (args) {
                if (args.propertyName === "stats") {
                    animateStats(args.value);
                } 
            });

    // tell the initial pokemon loaded to animate it's stats
    animateStats(viewModel.pokedexViewModel.stats);

}
exports.onLoaded = onLoaded;

//
// animates the coloring of the stat boxes, based upon the pokemon's stat level
//
// concepts: 
// - fillMeter(): fills a meter up to the level specified 
// - drainMeter(): drains a meter to the level specified
//
// - each function returns a promise for chained commands
// - a start delay and end delay can be added in milliseconds
function animateStats(meterValues) {

    //
    // collection of UI ids for each of the stat meters
    var meterIds = 
        ["hpMeter", "attackMeter", "defenseMeter", 
            "specialAttackMeter", "specialDefenseMeter", "speedMeter"];

    //
    // STEPS
    // 1. drain to zero (resets)
    // 2. pick an initial level to fillto (random)
    // 3. fill to the initial level
    // 4. drain to zero
    // 5. fill to the data-bound stat level
    var controlMeter = function (meter, initialLevel, level, delay) {
        setTimeout(function() {
            drainMeter(meter, 10, 0, 500)
                .then(function() { return fillMeter(meter, 0, initialLevel, 0); })
                .then(function() { return drainMeter(meter, initialLevel, 0, 500); })
                .then(function() { return fillMeter(meter, 0, level, 0); });
        }, delay);
    };

    //
    // shuffles an array
    var shuffle = function (array) {
        var currentIndex = array.length, temporaryValue, randomIndex;

        // While there remain elements to shuffle...
        while (0 !== currentIndex) {

            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;

            // And swap it with the current element.
            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }
        return array;
    };

    //
    // STEPS: 
    // 1. establish a start delay of 250 milliseconds 
    // 2. randomize the order in which the stats are anuimated
    // 3. run the animation in the randomized order, passing in a random initial value
    var delay = 0;
    var delayOffset = 250;
    var order = shuffle([0,1,2,3,4,5]);
    for (var i = 0; i < order.length; i++) {
        var initialLevel = Math.floor((Math.random() * 10) + 1);
        controlMeter(meterIds[order[i]], initialLevel, meterValues[order[i]], delay);
        delay += delayOffset;
    }
}



function fillMeter(id, fromLevel, toLevel, beginDelay, endDelay) {
    var meter = getMeterById(id);
    return fillRecurse(meter, toLevel, fromLevel, 1, "#30A7D7", endDelay);
}

function drainMeter(id, fromLevel, toLevel, endDelay) {
    var meter = getMeterById(id);
    return fillRecurse(meter, toLevel-1, fromLevel-1, -1, "white", endDelay);    
}

function fillRecurse(meter, level, current, step, color, endDelay) {
    if (current === level) return new Promise(function(resolve,error) {
        setTimeout(function() {
            resolve();
        }, endDelay);
    });

    //
    // MAGIC HAPPENS HERE
    // call the animate() function of NativeScript, changing the color with a duration
    return meter[current].animate({
        backgroundColor: new colorModule.Color(color),
        duration: 40
    }).then(function() {
        return fillRecurse(meter, level, current+step, step, color, endDelay);
    });
}

function getMeterById(id) {
    var meter = new Array();

    var meterContainer = page.getViewById(id);
    var childrenCount = meterContainer.getChildrenCount();
    for (var i = childrenCount-2; i >= 0; i--) {
        meter[meter.length] = meterContainer.getChildAt(i);
    }

    return meter;
}