Getting Started with NativeScript

Warning: This article was last updated on June 23, 2015, and as such, many of the examples are now out of date with newer NativeScript releases. You’re welcome to read through the article, but we recommend developers get started with NativeScript on either our JavaScript tutorial, or our TypeScript & Angular 2 tutorial.

NativeScript is a framework that lets you use JavaScript to build mobile applications utilizing platform specific, native API calls. It supports iOS, Android, and (as of February 2015) Windows Universal.

In this article, you will learn how to build a native mobile app using NativeScript. If you’d like some background on how NativeScript works first, you can read NativeScript – a Technical Overview before getting started.

Assumptions

This article is about the NativeScript library itself, and assumes you have a NativeScript development environment up and running. Specifically, you must

  • have a working IDE with NativeScript configured;
  • know how to create a new NativeScript project;
  • know how to run a NativeScript app in an emulator or deploy to a real device;
  • and (optionally) have the NativeScript companion app installed on a real device.

If you don’t have all of this ready, start by heading over to the NativeScript getting started page to get everything you need set up. If you’re using the NativeScript CLI, the NativeScript documentation has everything you need to get up and running for Windows or OS X or Linux.

Good? Alright, let’s get started.

Baby Steps

The NativeScript framework provides you with different building blocks to structure your project. This is why it is best to approach it in steps, one feature at a time. In this article you’ll learn about the following features:

  • 1) Views
  • 2) Code Behind
  • 3) Binding
  • 4) Observable
  • 5) CSS

If you want to see the final result, you can check the code at any time at https://github.com/sebawita/GettingStarted.

For clarity I added components folder (inside the app folder) where I am going to put all examples.

Here is how your folder structure should look.

Folder Structure Screenshot

Example 1: Hello world (views)

Now you are ready to create your first NativeScript app.

First

Add helloWorldView.xml to the components folder, with the following code:

<Page>
    <Label text="Hello World" />
</Page>

Next

Open app.js and change application.module to components/helloWorldView.

Your app.js code should look as follows:

var application = require("application");
application.mainModule = "components/helloWorldView";
application.cssFile = "./app.css";
application.start();

Finally

Build the app and deploy it to your device or emulator (for instructions see the getting started page, Step 4).

And voila! You have your very first Native app built with NativeScript.

Result

Hello World Screenshot

Important Info:

  • It is not necessary to use “View” in the name, however the convention makes the project’s file structure easier to follow.
  • The <Page> tag can only contain one item. Adding more items to the page will break it. In the next example we’ll examine how to add multiple items.
  • application.module is used to specify the starting page.

Example 2: Sign-up form (code behind)

In this example you are going to add a few UI components. Since the <Page> element can only contain one component you’re going to need a component that can hold and organise other components (a container). We’ll use StackLayout since it’s easy to use.

To explore other containers see the documentation area and Demystifying NativeScript Layouts

First

In app.js change the line that sets the mainModule to:

application.mainModule = "components/signUpView";

Next

Add signUpView.xml to the components folder with the following contents:

<Page loaded="loadSignUpView">
    <StackLayout>
        <Label text="Name:" />
        <TextField id="name" width="200"/>

        <Label text="Email:" />
        <TextField id="email" width="200"/>

        <Label text="Twitter Handle:" />
        <TextField id="twitterHandle" text="@" width="200"/>

        <Button id="signUpButton" text="Sign Up" width="100"/>

        <Label id="result" text=" " textWrap="true"/>
    </StackLayout> 
</Page>

The bit worth noting is loaded="loadSignUpView". loaded is an event that NativeScript triggers each time a page is loaded. The definition of loadSignUpView is located in the JavaScript file (see below).

Finally

Add signUpView.js to the components folder with the following contents:

var view = require("ui/core/view");

function onLoad(args) {
    var page = args.object;

    var nameTextField = view.getViewById(page, "name");
    var emailTextField = view.getViewById(page, "email");
    var twitterHandleTextField = view.getViewById(page, "twitterHandle");
    var signUpButton = view.getViewById(page, "signUpButton");
    var resultLabel = view.getViewById(page, "result");

    signUpButton.on("tap", function () {

        var result = "";

        if(nameTextField.text === "" || emailTextField.text === "" || twitterHandleTextField.text === "")
            result = "Error: All fields required";
        else
            result = "Success!!!\nName: " + nameTextField.text + "\nEmail:" + emailTextField.text + "\nTwitter Handle: " + twitterHandleTextField.text;

        resultLabel.text = result;
    });
}
exports.loadSignUpView = onLoad;

Things to Note:

  • Both the JavaScript and XML files must have the same name to be linked together.
  • The .js file contains the UI logic written in JavaScript.
  • The .xml file contains UI component descriptions.
  • [line 1] The require function loads modules in NativeScript. In this case it loads a view module linked with the current page, which gives you access to UI components on the page.
  • [line 24] The exports variable exposes functions and attributes to the elements on the page. In this case it exposes the loadSignUpView function (which corresponds to line 1 in signUpView.xml).
  • [line 6-10] view.getViewById retrieves a UI component from the page.
  • [line 12] The tap event adds code to be called when the button referenced by signUpButton is tapped.

Result

Sign Up Form Screenshot

Example 3: Journey Calculator (bindings)

Calling an element by its id and manually getting its text is a bad practice, as it ties the business logic of your app to the specific UI components. It also forces you to know the id of each UI component in your JavaScript code. Luckily NativeScript provides a more elegant approach: binding UI properties to attributes of a JavaScript object.

In this example you will create a journey cost calculator. The user will enter distance, miles per gallon and fuel cost, and the app will calculate the cost for the journey.

First

In app.js change the line that sets the mainModule to:

application.mainModule = "components/journeyCalculatorView";

Next

Add journeyCalculatorView.js to the components folder with the following contents:

var view = require("ui/core/view");

var journeyInfo = {
    distance: "24", 
    mpg: "35", 
    fuelCost: "120.5"
};

var page;

exports.loadSignUpView = function(args) {
    page = args.object;

    page.bindingContext = journeyInfo;
}

exports.calculate = function() {
    var distance = parseFloat(journeyInfo.distance);
    var milesPerGallon = parseFloat(journeyInfo.mpg);
    var milesPerLitre = milesPerGallon / 4.54609;

    var fuelCost = parseFloat(journeyInfo.fuelCost) / 100; // cost in £

    var costPerMile = fuelCost / milesPerLitre;
    var journeyCost = distance * costPerMile;

    var resultLabel = view.getViewById(page, "result");
    resultLabel.text = "Journey cost £" + journeyCost.toFixed(2)
                     + "\nCost per mile: £" + costPerMile.toFixed(2);
}

Things to Note:

  • [Lines 3-7] This is the definition of the data model. Its attributes are later bound to the properties of the UI components. In short: this is where your data goes.
  • [Line 14] This line sets the Page’s context to the previously created data object. After this assignment, UI components can bind to the journeyInfo object’s attributes using {{ attribute name }}
  • [Lines 17-30] Notice how data access is done using the journeyInfo object, rather than getting explicit references to UI components and setting their text property.

Finally

Add journeyCalculatorView.xml to the components folder with the following contents:

<Page loaded="loadSignUpView">
    <StackLayout>
        <StackLayout orientation="horizontal">
            <Label text="Distance" width="50"/>
            <TextField text="{{distance}}" width="100"/>
            <Label text="Miles" />
        </StackLayout>
        <StackLayout orientation="horizontal">
            <Label text="MPG"  width="50" />
            <TextField text="{{mpg}}" width="100"/>
            <Label text="MPG" />
        </StackLayout>
        <StackLayout orientation="horizontal">
            <Label text="Fuel Cost" width="50" />
            <TextField text="{{fuelCost}}" width="100"/>
            <Label text="Pence/Liter" />
        </StackLayout>

        <Button tap="calculate" id="calculateButton" text="Calculate" width="100"/>

        <Label id="result" textWrap="true"/>
    </StackLayout>
</Page>

Things to Note:

  • [Lines 5, 10, 15] Binding text values to attributes of the page’s context, or in this case the journeyInfo object, are created using {{ attributeName }}.

Result

Journey Calculator Screenshot

Important Info

  • Binding to a standard JavaScript object gives us direct access to the data input in the UI. However, as this is currently written the binding doesn’t work in reverse. That is, if you were to change attribute values in JavaScript, the UI wouldn’t get updated. To overcome that you need to use an observable object, which we’ll look at in the next example.
  • Bindings can be one-way (read only) or two-way (read/write).
  • By default bindings defined in XML are two-way.
  • You can also create a one-way binding expression —
    e.g. text={{ miles + "miles" }}.

Example 4: Number Game (observables)

The observable object is the missing piece in the binding puzzle. Its main purpose is to notify the UI about any relevant (bound) changes so that the UI can update itself.

In the previous example, if the journeyInfo was an observable object, you wouldn’t need to retrieve the result label to display the result on the page. Instead you would update the journeyInfo result and the UI would display the new value automatically.

In this example you will create a small game. In the game there are 10 numbers (1-10) in a random order. The player is presented with a number, and needs to guess whether the next number is higher or lower. The game needs to display the current card, the score, and the number of cards left. Finally, it must allow users to start a new game. Let’s see how this is built.

First

In app.js change the line that sets the mainModule to:

application.mainModule = "components/upDownView";

Next

Create upDownViewModel.js in the components folder with the following contents:

var observable = require("data/observable");
var upDownViewModel = new observable.Observable();

upDownViewModel.noOfCards = 10;
upDownViewModel.cards = [];

upDownViewModel.currentCardIndex = 0;

upDownViewModel.currentCard = 0;
upDownViewModel.nextCard = 0;

upDownViewModel.score = 0;
upDownViewModel.streak = 0;

upDownViewModel.cardsLeft = 0;


upDownViewModel.startNewGame = function() {
    this.set("score", 0);
    this.set("streak", 0);

    this.shuffleCards();

    this.getFirstCard();
}

upDownViewModel.goHigher = function() {
    if(!this.hasMoves())
        return;

    if(this.currentCard < this.nextCard)
        this.goodGuess();
    else
        this.badGuess();

    this.getNextCard();
}

upDownViewModel.goLower = function() {
    if(!this.hasMoves())
        return;

    if(this.currentCard > this.nextCard)
        this.goodGuess();
    else
        this.badGuess();

    this.getNextCard();
}

upDownViewModel.getFirstCard = function() {
    this.currentCardIndex = -1;
    this.getNextCard();
}

upDownViewModel.getNextCard = function() {
    this.currentCardIndex = this.currentCardIndex +1;
    index = this.currentCardIndex;

    this.set("currentCard", this.cards[index]);
    this.nextCard = this.cards[index+1];

    this.set("cardsLeft", this.noOfCards - index -1);
}

upDownViewModel.hasMoves = function() {
    return this.cardsLeft > 0;
}

upDownViewModel.goodGuess = function() {
    this.set("streak", this.streak + 1);
    this.set("score", this.score + this.streak);
}

upDownViewModel.badGuess = function() {
    this.set("streak", 0);
}

upDownViewModel.shuffleCards = function() {
    var cardDeck = [];
    for(var i=0; i<this.noOfCards; i++) {
        cardDeck.push(i+1);
    }

    var shuffledCards = [];
    while(cardDeck.length > 0) {
        var index = getRandom(cardDeck.length);

        var card = cardDeck.splice(index, 1)[0];

        shuffledCards.push(card);
    }

    this.set("cards", shuffledCards);
}

module.exports = upDownViewModel;

function getRandom (max) {
    return Math.floor(Math.random() * max);
}

Things to Note:

  • [Line 1] – Loads observable module.
  • [Line 2] – Creates an instance of an observable object.
  • [Lines 4-15] – Adds attributes to the observable object including those displayed in the UI:
    • [Line 9] – Current card
    • [Line 12] – Score
    • [Line 15] – Cards left
  • [Lines 18-49] – The definition of the startNewGame, goHigher and goLower functions.
  • [Line 97] – Exposes the observable object as a module, which can be required from another js file.
  • [Line 19] – set the score to 0 and notifies the UI that the score value is 0.

The above code is self-contained and is completely separated from the UI. Its sole purpose is to provide the game logic.

Next

Create upDownView.js in the components folder with the following contents:

var view = require("ui/core/view");
var model = require("./upDownViewModel");

exports.loadUpDownView = function(args) {
    var page = args.object;
    page.bindingContext = model;
}

Things to Note:

  • [Line 2] – Gets the observable object defined in the upDownViewModel.js file.
  • [Line 6] – Sets the observable object as the page’s context.

Finally

Create upDownView.xml with the following contents:

<Page loaded="loadUpDownView">
    <StackLayout>
        <Button tap="{{startNewGame}}" text="New Game"/>

        <Label text="{{'Cards Left: ' + cardsLeft}}" />
        <Label text="{{'Score (Streak):' + score + ' ('+ streak +')  '}}" />

        <StackLayout orientation="horizontal" horizontalAlignment="center">
            <Button tap="{{goLower}}" text="Lower" />
            <Label text="{{currentCard}}" />
            <Button tap="{{goHigher}}" text="Higher" />
        </StackLayout>
    </StackLayout>
</Page>

Things to Note:

  • {{ xyz }} is used to bind events and attributes of the UI to those exposed by the observable object (ViewModel) from upDownViewModel.js:
    • [Lines 3,9,11] – button tap event bindings
    • [Line 5,6,10] – labels text bindings

Result

Game Screenshot

Important Info:

  • Your application can be made of multiple pages. Each page can have their own observable object, observable objects can be shared across pages.
  • In the observable object, you can set the value of any attribute in two ways:
    • set("attributeName", value) sets the value of the attributeName attribute and notifies all bound properties in the UI. This should be used on attributes that are expected to be bound in the UI (like score, currentCard and cardsLeft).
    • Calling “=” (e.g. myObject.attributeName = value) sets the value of the attribute, but it doesn’t notify the UI. This should be used on attributes that are not meant to be displayed in the UI (like cards, nextCard and currentCardIndex).
  • The above example follows an MVVM architectural pattern, where the UI (View) and the application logic (ViewModel) are linked via set of bindings, resulting in a tidy separation of what you see versus what you do.
  • To read more about bindings go to the NativeScript documentation page about Data Binding.

Example 5: CSS

The game from the previous example works, but the UI isn’t all that attractive. Now is a good time to introduce NativeScript CSS styling and to make the game look better.

First

Add upDownViewWithStyle.css to the components folder, with the following code:

.info  {
    font-size: 30;
    color: #2E4F8A;
}

.infoLabel  {
    font-size: 22;
    color: #99ABD6;
}

.card {
    color: #D63326;
    font-size: 100;
    width: 100;
}

Button {
    vertical-align: center;
    background-color: #037AC4;
    color: #8CE8FC;
    font-size: 30;
}

Things to Note:

  • [Lines 1-15] – creates 3 css class names
  • [Line 17-22] – defines the default properties for all buttons

Next

In app.js change the line that sets the mainModule to:

application.mainModule = "components/upDownViewWithStyle";

Create upDownViewWithStyle.js in the components folder with the following contents:

var view = require("ui/core/view");
var viewModel = require("./upDownViewModel");

exports.loadUpDownView = function(args) {
    var page = args.object;
    page.bindingContext = viewModel;
}

Finally

Create upDownViewWithStyle.xml with the following code:

<Page loaded="loadUpDownView">
    <StackLayout>
        <Button tap="{{startNewGame}}" text="New Game" horizontalAlignment="center"  cssClass="actionButton"/>

        <GridLayout rows="auto">
            <Label text="  Cards Left:" cssClass="infoLabel" horizontalAlignment="left" />
            <Label text="Score (Streak):  " cssClass="infoLabel" horizontalAlignment="right" />
        </GridLayout>
        <GridLayout rows="auto">
            <Label text="{{'  ' + cardsLeft}}" cssClass="info" horizontalAlignment="left" />
            <Label text="{{score + ' ('+ streak +')  '}}" cssClass="info" horizontalAlignment="right" />
        </GridLayout>

        <StackLayout orientation="horizontal" horizontalAlignment="center">
            <Button tap="{{goLower}}" text="Lower" cssClass="actionButton" />
            <Label text="{{currentCard}}" cssClass="card" />
            <Button tap="{{goHigher}}" text="Higher" cssClass="actionButton" />
        </StackLayout>
    </StackLayout>
</Page>

Things to Note:

  • [Lines 6,7,10,11,16] – each label is assigned a cssClass that corresponds to a class name defined in upDownViewWithStyle.css.
  • [Lines 3,15,17] – The Button elements aren’t given a cssClass attribute because their styling is defined by the Button selector in upDownViewWithStyle.css.

Result

Game with CSS Screenshot

Important Info:

  • There are few ways to add styling to your app:
    • Global style: use app.css to add styling that will affect all views.
    • View style: use view-name.css to add styling to the specific view-name.js view (i.e. helloWorldView.js -> helloWorldView.css).
    • Component style: add in-line styling to a component.
  • The code in upDownViewWithStyle.css follows the CSS structures and naming conventions. However only a small subset of CSS properties are available for use at the moment: font-size, color, background-color, etc.
  • There was no need to change upDownViewModel for this example, as the styling of the UI is separate from the logic of the game.
  • The list of the supported styles is still growing, however some styles that exist in HTML may not possible to implement in native mobile apps.
  • Although this is not advised, You could copy and paste a CSS file from an existing web project. All supported styles will affect the UI, while the unsupported styles will get ignored.

Summary

Now you should have a working example of a NativeScript app with bindings and CSS styling. Hopefully these examples have prepared you to build your own NativeScript apps.

There are other things that you will need to know in order to build a successful app such as page navigation, adding 3rd-party native libraries, and adding platform-specific code. Don’t worry – we’ll be covering those topics (and more) in the near future.

For more information on NativeScript check out the docs.

Header image courtesy of Opassande

Comments

  • Gavin Engel

    Really useful thank you. So it seems the default nativescript MVC has been released and ready to use? Also, do you find it odd they call for XML files instead of json files?

    • Sebastian Witalec

      Thank you. I am glad you like the article.
      MVVM in NativeScript is mostly formed and ready to use. I am working on a similar type article about MVVM in NativeScript.
      The team reviewed various formats to describe the UI and XML was the strongest candidate. It also helps that it is more similar to existing UI declarations (HTML, XAML, Android XML, etc.)

      • Gavin Engel

        Thanks! So, I’d like to begin working with NativeScript in my free time. I don’t think I want to develop Cordova apps, just NativeScript because I find it really exciting. So obviously I want to use Telerik tools to assist me, but I don’t want to spend too much money. What Telerik products should I be subscribing to?

        • Sebastian Witalec

          All you need is a tool that can build your apps. You have 2 options:

          1. Telerik AppBuilder – you can use it to build both NativeScript and Cordova apps. One of the best features of AppBuilder is Compile as a Service, which uses a Cloud Service to build your apps. Meaning you don’t need to set up any SDKs to build apps and you don’t need a Mac to build iOS apps.
          2. Command Line Interface for NativeScript (https://github.com/NativeScript/nativescript-cli) – it is free, but you need to setup all SDKs yourself.

          • Péter Kulik

            Hey Sebastian,

            did you try your samples with CLI?

            I use CLI and the default, generated app template worked well. It doesn’t use xml, only platform specific js codes.

            I tried to reproduce your helloworld but the deployed app freezed on my tablet (android 4.2.2)

            Can you share a workable version which is deployable with CLI?

            Thanks,
            Peter

          • Hi Peter,

            The code should work as long as you name the xml file that same as your js files—e.g. helloWorldView.js and helloWorldView.xml. That being said, the CLI and AppBuilder currently use slightly different directory structures most notably in how they handle the “app” directory, so you can’t directly clone one from the other at the moment. This is something that we’re addressing for the upcoming NativeScript beta release: http://www.telerik.com/campaigns/nativescript.

            If you’d like some more help working through this please shoot me an email at tj.vantoll@telerik.com.

            TJ

  • Pingback: Dew Drop – January 15, 2015 (#1934) | Morning Dew()

  • Can’t wait to get my hands on this bad boy. Any idea when you guys start releasing it for everyone to play with ?
    Some of us need to learn it and blog about it 🙂

  • Pingback: Digest of interesting materials from the world of web development and IT for the last week No1 (12 - 18 January 2015) - WBD()

  • Péter Kulik

    Will you publish some complex samples about layouts?
    I would like to see an example how can we handle different screen sizes (phone/tablet).

    It would be a nice sample:

    tablet:
    – left side: entity list
    – right side: entity details

    phone:
    – page1: entity list -> click on an item -> navigate to page2
    – page2: entity details

    An other question:
    Is it possible to mix platform specific codes with platform independent codes (tns modules)?

    For example I want to add some platform specific control to a page (the page is definied in an xml file).

    And a last one:
    windows8.1/10 will be supported? (not only phone)

  • Pingback: How NativeScript Works -Telerik Developer Network()

  • Manuel A Cerda

    Hi,

    Great insight.

    I wanted to know if Native Script converts scripts to “minified” version (app.min.js or app.min.css) ?

    Thanks

    • CokoBWare

      From what I’ve seen, NativeScript doesn’t minify code (I could be wrong, but haven’t seen evidence of this). The JS files are on the client when the NativeScript app starts, so from a practical perspective, minifying your JS for NativeScript I think would be a waste of time. Minification only really benefits load times when transporting over a network, like the web, and doesn’t really optimize performance as much at the VM level. However, using a JS linter would probably help you with making your code perform at least somewhat better at the outset.

  • Pingback: What is NativeScript anyways? | Wheres My Keyboard?()

  • CokoBWare

    I just watched a video of you presenting NS at Bulgaria Web Summit 2015… I used this article in my classroom as a learning point for my students to solve a lab they did. This article is key for helping learners use the different approaches to binding and using data in forms. Certainly the feedback on what this article offers was very positive! 🙂

    • Bulgaria Web Summit

      There will be another one at Bulgaria Web Summit 2016 🙂