Quantcast
Channel: Devdactic – Ionic Tutorials
Viewing all articles
Browse latest Browse all 183

NativeScript Angular 2 Introduction: Why NativeScript Matters

$
0
0

While there is a big buzz around Angular 2 and the Ionic Framework 2, a new force awakens in the shadow of the cross-platform development world. While this force has not yet reached the maturity level of other frameworks, the potential and the chances for success makes this force a worthy candidate to become one of the strongest frameworks on the cross-platform horizon.

The new force we are talking about is NativeScript, a framework to “Build truly native apps with JavaScript” as their page says.

Not another framework, cmon!

That was my first impression when my good friend Nic Raboy started to blog about NativeScript.

But some weeks ago on the German Developer Week conference I had the chance to sit down with the great Sebastian Witalec from the Telerik team, the creators of NativeScript. He gave me very good insight into what NativeScript is and what it isn’t, so today let’s explore what NativeScript can do for you!

What is NativeScript?

At the heart, NativeScript allows you to develop mobile apps from a single code base, written in JavaScript. With the release 2 you are now also able to use Angular 2 + TypeScript for your NativeScript apps, which is also one of the best ways to develop future web apps.

The great thing about it?

While Ionic 2 also uses Angular 2, the code of an Ionic 2 app is not completely the same like for an Angular 2 web app. With NativeScript, you code is actually very much the same, which gives you the chance to use shared code between your mobile and your web app. Pretty awesome!

We haven’t covered one of the most important parts about NativeScript yet: Native performance.

How?

NativeScript does not run in a Webview like Ionic or general cordova apps, but compiles to native UI components. The JavaScript for the logic is still packaged inside the app and not compiled to native code, but that’s not really a problem.

So in order to get native UI components on iOS and Android, NativeScript uses a special syntax for creating the views. This is not HTML, as it’s not running in a Webview (yeah Sebastian, I understand the reasons now!). The syntax for the view is like XML, but with Angular 2 support you can also use Angular 2 elements. It’s a bit confusing at this point and the documentation is not really good by now, but you will see it in action soon.

There is a lot more to say about the build system and other stuff related to NativeScript or the Telerik platform, but for now the knowledge is enough to dive into some hands-on stuff.

I am pretty new to NativeScript but wanted to give you the best possible impression, so like always we will develop a little app and see how it goes. Some people will like this way of doing things, others won’t and that’s fine.

No framework should target everyone, because that would in the end mean it won’t target anyone!

Let’s dive into some NativeScript code and build a little Pokéindex using the Pokémon API!

nativescript-pokemon

Starting a new NativeScript App

We start with an empty NativeScript app and pass the --ng flag to indicate we want to use Angular 2.

As the CLI is no that mighty at the moment, we need to create the folders and files we would like to have by hand, so feel free to copy the touch commands.

I’m using a Mac so I can add the iOS and Android platform, if you are not on a Mac you can only use Android!

// Install NativeScript if needed
npm install -g nativescript
//Create Project
tns create devdactic-pokemon --ng
cd devdactic-pokemon
tns platform add ios
tns platform add android
mkdir app/pages
mkdir app/pages/list
mkdir app/pages/pokemon
touch app/pages/list/list.component.ts
touch app/pages/list/list.component.html
touch app/pages/pokemon/pokemon.component.ts
touch app/pages/pokemon/pokemon.component.html
touch app/pages/pokemon/pokemon.component.css

If you want to run your app on iOS, you need to apply a little change to the app/shared_resources/info.plist to have access to outside APIs. This is also one of the strengths of NativeScript, you can actually change the behaviour or settings for one platform quite easily!

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

If you want to run this demo you need this in some form, NSAllowsArbitraryLoads in this example just allows all outside connections which might not be the best practice for productive apps. It was introduced with iOS9 and needs to be added because Apple wants to make sure you are using secure connections. Read more about it here.

By now your app should already work, so give it a try and run the app on whichever platform you like:

tns run android
tns run ios

// or inside the emulator
tns run android --emulator
tns run ios --emulator

As said before, the app does not run in a Webview so we can’t just debug the app inside Chrome like with Ionic. Anyway, NativeScript offers livereload to improve the development flow. This is a bit slow at the moment but speed improvements are planned for the next releases!

We also need to apply a little patch to our app in order to use HTTP requests. This counts only for the current version 2.2 of NativeScript and will hopefully be fixed soon.

So open the app/main.ts and insert:

// this import should be first in order to load some required settings (like globals and reflect-metadata)
import {nativeScriptBootstrap} from "nativescript-angular/application";
import {AppComponent, APP_ROUTER_PROVIDERS} from "./app.component";

// HACK - patch dom adapter
// For more info see: https://github.com/NativeScript/nativescript-angular/issues/305
import {Parse5DomAdapter} from '@angular/platform-server/src/parse5_adapter';
(<any>Parse5DomAdapter).prototype.getCookie = function (name) { return null; };

nativeScriptBootstrap(AppComponent, [APP_ROUTER_PROVIDERS]);

Setting up the Angular Router

Our app is currently empty, so let’s start with some logic for routing to our different views. As said before, NativeScript uses the actual Angular router.

Therefore, go ahead and open the app/app.component.ts and insert:

import {Component} from "@angular/core";
import {RouterConfig} from "@angular/router";
import {NS_ROUTER_DIRECTIVES, nsProvideRouter} from "nativescript-angular/router";

import {ListPage} from "./pages/list/list.component";
import {PokemonPage} from "./pages/pokemon/pokemon.component";

@Component({
    moduleId: module.id,
    selector: "my-app",
    directives: [NS_ROUTER_DIRECTIVES],
    template: `
      
        
      
    `
})
export class AppComponent {
}

export const APP_ROUTES: RouterConfig = [
  { path: "", component: ListPage },
  { path: "pokemon/:url", component: PokemonPage  }
];

export const APP_ROUTER_PROVIDERS = nsProvideRouter(
  APP_ROUTES,
  { enableTracing: false }
);

This looks a lot like Angular 2! We simply define 2 paths inside our app:

  • /list: The path to our ListPage, the default page we show after starting the app
  • /pokemon/:url: The details page for a specific Pokémon which is the PokemonPage

So this is the backbone of our app, let’s head on to the more interesting stuff!

Loading 150 Pokèmons inside a Listview

Because everyone knows that only the first 150 are the real Pokémon, we will simply load that amount from the PokéAPI.

Our ListPage loads the information and saves the data to an array, while also maintaining 2 loading states. These states are used for displaying a loading indicator and also fading in the list with the actual results.

Open the app/pages/list/list.component.ts and insert:

import { Component } from "@angular/core";
import { Http, HTTP_PROVIDERS } from '@angular/http';
import 'rxjs/add/operator/map';
import {Router} from "@angular/router";

@Component({
  selector: "list",
  providers: [HTTP_PROVIDERS],
  templateUrl: "pages/list/list.component.html"
})
export class ListPage {
  pokemon: Array = [];
  isLoading = false;
  listLoaded = false;

  constructor(private router: Router, private http: Http) {}

  ngOnInit() {
    this.isLoading = true;
    this.http.get("http://pokeapi.co/api/v2/pokemon?limit=150")
      .map(res => res.json())
      .subscribe((data) => {
        this.pokemon = data["results"];
      },
      (err) => {
        console.error(err);
        alert("Failed to load the data:" + JSON.stringify(err));
      },
      () => {
        this.isLoading = false;
        this.listLoaded = true;
      })
  }

  public showDetails(args: any) {
      let selectedPokemon = this.pokemon[args.index];
      this.router.navigate(["/pokemon", encodeURIComponent(selectedPokemon["url"]) ]);
  }
}

As you can see we also created the showDetails function which will be called once we want to drill down on a specific Pokémon of our list. We just need to get the selected object and then call the router and pass the URL for that Pokémon (the detail page will handle the rest of loading the new data).

So far everything looks like an Angular 2 app, but let’s see how the view is created.

Open the app/pages/list/list.component.html and fill in:

<ActionBar title="My Pokedex"></ActionBar>
<GridLayout>
  <Image src="http://pokeapi.co/media/sprites/pokemon/150.png" [class.hide]="!isLoading"></Image>

  <ListView [items]="pokemon" [class.visible]="listLoaded" class="small-spacing" (itemTap)="showDetails($event)">
    <template let-item="item" let-x="index">
      <DockLayout stretchLastChild="true">
          <Image 
            dock="left" 
            [src]="'http://pokeapi.co/media/sprites/pokemon/' + (x+1) + '.png'"
            width="45"></Image>
          <Label [text]="x+1 + '.  ' + item.name" class="medium-spacing"></Label>
      </DockLayout>
    </template>
  </ListView>
  <ActivityIndicator [busy]="isLoading" [visibility]="isLoading ? 'visible' : 'collapse'" row="1" horizontalAlignment="center"
    verticalAlignment="center"></ActivityIndicator>
</GridLayout>

This is no standard HTML in a *.html file! Dafack is this?

You just met NativeScript!

At first sight, this looks a bit strange. Always keep in mind that this view component hast to be rendered as native components, not a webview.

So what’s happening here is that we use a simple GridLayout Layout container from the NativeScript layout options. This allows us to easily add different view components and they get positioned like in a table with rows and views.

Additionally we style our rows with a DockLayout to show a little image of the Pokémon in front of our row. Most of the rest is simply Angular 2 syntax!

Everything in NativeScript lives in these layout containers, and once you get used to it this actually helps you a lot to build views faster. Also, great guys are currently working on rendering of NativeScript components inside the web. This could really be a huge help as you most of the time need an app plus a website.

Think about the view component what you want; it’s a bit irritating the first time coming from ionic and standard HTML, but it’s a pill you have to swallow if you want to go the NativeScript way.

What are your first impressions? Let me know below!

To get a better appearance effect and some styling for the list we add some styling and animation inside app/app.css (there are better places for styling, but this works as well):

.small-spacing {
  margin: 5;
}

.medium-spacing {
  margin: 10;
}

ListView {
  opacity: 0;
}

// Animations
.visible {
  animation-name: show;
  animation-duration: 1s;
}

@keyframes show {
  from { opacity: 0; }
  to { opacity: 1; }
}

.hide {
    animation-name: animate-hide;
    animation-duration: 2s;
    animation-fill-mode: forwards;
}

@keyframes animate-hide {
  from {  opacity: 1}
  to {  opacity: 0 }
}

These are actually standard CSS animations, which will be mapped to native changes of the UI like described in this post.

Basically we are defining some general properties in the upper half of the file and an animation to fade in or out our list view in the lower half using @keyframes to define the animation. By using the name of the animation inside the definition for animation-name the animation is bind to that property. If you want to find out more about NativeScript animations, check out their documentation for Animations with CSS.

Crafting the Details View for a Pokèmon

Let’s finish up our little app by adding the details view for a Pokémon. This component will get the URL to a specific Pokémon so it needs to load all the data for it at first.

Once we got the data, we grab some data out of the response, which is what we want to display (I know the real Pokédex was a lot cooler, but hey that’s up to you to continue this post!).

We also have some loading states again, just to have a better looking UI. So open the app/pages/pokemon/pokemon.component.ts and insert:

import { Component } from "@angular/core";
import { Http, HTTP_PROVIDERS } from '@angular/http';
import 'rxjs/add/operator/map';
import {Router, ActivatedRoute} from "@angular/router";

@Component({
  selector: "pokemon-page",
  providers: [HTTP_PROVIDERS],
  templateUrl: "pages/pokemon/pokemon.component.html"
})
export class PokemonPage {
  name: string = "";
  pokeimg: string = "";
  weight: number = 0;
  height: number = 0;

  isLoading: boolean = false;
  dataLoaded:boolean = false;

  constructor(private router: Router, private route: ActivatedRoute, private http: Http) {  }

  ngOnInit() {
    this.isLoading = true;

    this.route.params
      .map(params => decodeURIComponent(params['url']))
      .subscribe(url => {
        this.http.get(url)
        .map(res => res.json())
        .subscribe((data) => {
          this.name = data["name"];
          this.pokeimg = data["sprites"]["front_default"];
          this.weight = data["weight"];
          this.height= data["height"];
        },
        (err) => {
          console.error(err);
        },
        () => {
          this.isLoading = false;
          this.dataLoaded = true;
        })
    })
  }

  navigateBack() {
    this.router.navigate(["/"]);
  }
}

Again very standard Angular 2, not really much more we need to say about it. The map logic might look a bit strange but that’s how you get the most power in as few lines as possible.

The view inside the app/pages/pokemon/pokemon.component.html should contain this:

<ActionBar [title]="name">
  <NavigationButton text="Back" android.systemIcon="ic_menu_back" (tap)="navigateBack()"></NavigationButton>
</ActionBar>
<StackLayout class="item-group">
  <Label [text]="'Height:' + height" textWrap="true"></Label>
  <Label [text]="'Weight:' + weight" textWrap="true"></Label>
  <Image [src]="pokeimg"></Image>

  <ActivityIndicator
    [busy]="isLoading"
    [visibility]="isLoading ? 'visible' : 'collapse'"
    horizontalAlignment="center"
    verticalAlignment="center">
  </ActivityIndicator>
</StackLayout>

This time we use a StackLayout as container, which is simply arranging every object below the previous one. We want to have our Bar at the top to navigate back to the list, two labels with the information and finally the image.

I found it a bit complicated which syntax to actually use, so sometimes we access Angular properties but sometimes they are normal properties of the object. Also, the documentation is not very helpful at this moment, which was also noted by Raymond Camden when he worked with NativeScript. But I think the team is aware of this and will constantly improve this!

Finally, we can actually apply some styling to our not-really HTML view with some CSS inside app/pages/pokemon/pokemon.component.css so let’s do it:

.item-group {
    padding: 10;
}
 
.item-label {
    font-weight: bold;
}

That’s it for our first NativeScript app!

Now run your app on iOS or Android and enjoy your childhood memories of 150 Pokémon and their images.

nativescript-pokemon

Why NativeScript matters

As said before, my initial thoughts were “Why should I learn another framework?“, and still get those thoughts when working with new stuff like React Native.

But if you think a bit different everything becomes easier:

All of the frameworks for cross-platform development have their strengths and weaknesses!

For me there is no single best framework, it always depends on the business case and requirements you have. While Ionic is great in many areas, it will always be a Webview and you will always lack some performance vs. a native app.

There will always be people who don’t like Angular at all, which are better suited with something they might already know like Xamarin for .NET or React-Native for ReactJS developers, or even Unity3D if you want to target even more devices.

So we shouldn’t fight the cross-platform war like we fought Apple vs. Android, they both exist and you have to target both.


We shouldn’t fight the cross-platform war.
Click To Tweet


Back to NativeScript

So these were some general thoughts, now back to the question what makes NativeScript strong and who should use it?

First of all, every framework that settles on Angular 2 is a potential candidate for something we want to use. Developing Angular 2 with TypeScript does not feel like old web development but like writing actual good code, plus the community is huge.

And, if you want to create a mobile app and website chances are very high you can share code between your NativeScript and web app! This is something that will become more and more important. And as NativeScript does not force you to have some special Angular stuff, you could really share that pieces sometimes 1:1 which is currently not possible with Ionic 2.

The next big plus is native performance coming from the native compiled UI components. Of course this makes NativeScript attractive to people who rely on good performance and fear the almighty Webview in hybrid apps.

One thing we haven’t really touched before is the use of native APIs, which comes pretty easy with NativeScript. This means, you can actually call the let’s say iOS UITableViewController directly from code!

Of course this means you need to have some native knowledge, but if you do have it this can sometimes offer you great possibilities as you don’t have to wait for some company to write a wrapper but just directly use it by yourself. For a better explanation, you can watch the NativeScript explanation video here.

Finally I would recommend to try NativeScript to everyone, but especially to those who:

  • like Angular 2 and TypeScript
  • want better performance for Cross Platform apps
  • already have some native experience

These people might benefit the most from it, and that’s again the reason why I said there is space for many frameworks. What these people might like about NativeScript is not helpful at all for others, who are then suited better with a different framework.

Conclusion

My first adventure with NativeScript was interesting, I learned a lot about how things work and I can see the potential benefits for developing cross-platform apps with it. I will give it some more time and maybe more tutorials in the near future to see if it’s something I want to work with more often.

What are your first experiences with NativeScript? Would love to hear them!

A big thank you goes out to Sebastian Witalec again for porting my code from NativeScript 2.1 to 2.2. You can definitely count on the NativeScript team if you encounter any problems!

Happy Coding,
Simon

The post NativeScript Angular 2 Introduction: Why NativeScript Matters appeared first on Devdactic.


Viewing all articles
Browse latest Browse all 183

Trending Articles