Navigating A NativeScript App With The Angular 2 Router

Unless you want a very boring single page application, you’re going to want some form of page navigation with multiple pages available.  Previously I wrote a tutorial for navigating between routes in a vanilla JavaScript NativeScript application, but with Angular 2 in full force, it probably makes sense to demonstrate navigation with the very different Angular 2 Router component.

Anyone who has been following Angular 2 since beta knows that the navigation components have changed drastically in pretty much every release.  Anyone who has been following NativeScript and Angular 2 knows that Telerik likes to use any and all Angular 2 in its vanilla state.  This means that navigation in NativeScript Angular 2 applications has changed quite a bit over the past year.  However, with Angular 2 now in general availability (GA), the Angular 2 Router is no longer beta and should no longer be changing.

We’re going to take a look at simple navigation between two Angular 2 components in a NativeScript Android and iOS mobile application using the now stable Angular 2 Router.

To make this guide easy to follow, we’re going to create a fresh NativeScript Android and iOS project.  From the Command Prompt (Windows) or Terminal (Mac and Linux), execute the following:

Note that the --ng tag indicates an Angular 2 project, not a vanilla NativeScript project.  This means we’ll be using TypeScript and Angular 2.  Also note that if you’re not using a Mac with Xcode installed, you won’t be able to build for iOS.

This particular project will make use of two different navigation routes, both of which are not included in the base NativeScript template.  Create the following in the app directory of your project:

If your command line doesn’t have mkdir and touch or you don’t feel comfortable using them, go ahead and create those files and directories manually.

Let’s focus on the second page of our application first, or in other words, the page that we plan to navigate to.

Open the project’s app/components/page2/page2.ts file and include the following code:

There isn’t anything special in the above TypeScript code.  Essentially we are just saying that the TypeScript code is bound to the corresponding HTML file.  Having created the Page2Component class is important though.

With the TypeScript out of the way for the second page, open the project’s app/components/page2/page2.html file so we can add some UI markup:

In the above markup, all we’re doing is creating a navigation bar with a back button and an empty layout.  However, the navigation bar title is more than enough to tell us that we’ve navigated away from the first and default page.

With the second page out of the way, we can now focus on the first page which will be the default page when the application opens.

Open the project’s app/components/page1/page1.ts file and include the following TypeScript code:

Without getting too far ahead of ourselves, this TypeScript file looks very similar to that of the second page.  However, we’ve included an onTap method and imported the Angular 2 Router component.  After having injected the Router in the constructor, we can use it to navigate to any of our available routes.

We’ve not yet defined our routes, but it is safe to assume that page2 represents our second page.  We’ll get to that in a minute.

Open the project’s app/components/page1/page1.html file and include the following HTML markup:

Notice in the above HTML we have a navigation button that triggers the onTap method when clicked?  This is how the navigation is triggered.

We’re not done yet.  Remember how I mentioned that we haven’t defined our available routes?  We need to define those routes now.

Create an app/app.routing.ts file in your project.  Within this file, the following code should exist:

Notice that we’ve imported all available pages at the top?  We can establish available routes within the appRoutes array where the path represents the value used in the TypeScript navigation and the component being the corresponding component to the path.

To save us some time in the next step, we construct an array of every available component as well.

The next step would be to include this route information in the all-powerful @NgModule block found in the project’s app/main.ts file.  The file would look something like this:

We’ve imported the NativeScriptRouterModule and the constant variables that we had just defined in the app/app.routing.ts file.

In the @NgModule block we declare all components found in the appComponents array in the declarations property.  We also import the NativeScriptRouterModule and available routes in the imports property.

We’re almost done!

The fine step would be to determine where these routes become visible on the screen.  Open the project’s app/app.component.html file and exchange all the HTML markup with the following:

At this point if you tried to run your application, navigation between the two pages should work.

Passing Parameters Between Routes

Now what happens if you want to pass some data between routes?  A solid example of this would be passing in an id from a list and then querying that id in the second page.

Let’s make a few modifications to make this possible.

Open the project’s app/app.routing.ts file and change the following line:

Notice that we’ve added /:name to the path?  This is because we plan to pass a variable representing a name.

To pass this data from the first page, open the project’s app/components/page1/page1.ts file and change the navigation command so it looks like the following:

In the above example we are passing a string representing my name with the navigation request.  On the second page we need to anticipate a value and retrieve it.

Open the project’s app/components/page2/page2.ts file and change the code to match the following:

To receive data from a parent page, the ActivatedRoute component must be imported.  Within the constructor method we’ve subscribed to the navigation parameters and assigned them to the fullName variable.  We can then display that variable in the UI.

Open the project’s app/components/page2/page2.html and include the following HTML markup:

The screen should say “Hello Nic Raboy” when you navigate to it.

Conclusion

We just saw how to use the latest stable release of the Angular 2 Router for handling navigation between pages in a NativeScript Android and iOS mobile application.  Not only did we handle navigation between pages, but we also accommodated the scenario where we need to pass a parameter of data to a page.

This tutorial was a followup to the vanilla NativeScript with JavaScript tutorial that I wrote not too long ago.

A video version of this article can be seen below.

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.

  • Davor Peić

    Hey, as we chatted on twitter, this tutorial would be great if you could expand passing a parameter with “Passing data object between routes” for cases where we already have data downloaded on the page1 and want to pass it to page2. Either way thanks for great tutorial!

  • Herman

    Hi Nic, I got a question. When I’m trying to run my app I’ve got this error:

    app/app.routing.ts(1,32): error TS2307: Cannot find module ‘./components/page1/page1’.

    app/app.routing.ts(2,32): error TS2307: Cannot find module ‘./components/page2/page2’.

    • Herman

      I got it… I needed to rebuild the project with “../components/page1/page1” and “../components/page1/page1”
      Thanks anyway.

      • Herman

        Nope, same thing… 🙁

        • Verify your placement of the app.routing.ts file as well as the two files that you’re trying to import. I’ve made the mistake before where I created those files in the wrong directory (parent or child) and didn’t realize it.

          The tutorial is still valid, likewise with the YouTube video I made for it. My best guess is your file placement.

          • Herman

            Yeah, you’re right I’ve created the folder in the wrong place. That one is solved, but now when I run the app I got this errors:
            W/System.err( 2651): at com.tns.Runtime.callJSMethodNative(Native Method)
            W/System.err( 2651): at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:865)
            W/System.err( 2651): at com.tns.Runtime.callJSMethodImpl(Runtime.java:730)
            W/System.err( 2651): at com.tns.Runtime.callJSMethod(Runtime.java:716)
            W/System.err( 2651): at com.tns.Runtime.callJSMethod(Runtime.java:697)
            W/System.err( 2651): at com.tns.Runtime.callJSMethod(Runtime.java:687)

          • Herman
          • Just tested it again on my end and it works fine for me. Based on your error regarding the page-router-outlet, I imagine that you’re not importing or injecting the right things in your main.ts file.

            I recommend going through the tutorial again.

            I’m using the latest version of NativeScript as of now which is 2.3.0

            Best,

          • Brandon S. Brandon

            Nic,
            When I run: “D:myproject>tns run android –emulator”

            I get this error:

            app/app.module.ts(3,10): error TS2305: Module ‘”D:/myproject/app/app.routing”‘ has no exported member ‘AppRoutingModule’.

            TypeScript compiler failed with exit code 1

            The word “AppRoutingModule” appears a lot in the Angular guide on Routing on their website (https://angular.io/docs/ts/latest/guide/router.html) 43 times and zero times in this tutorial, so I assume that perhaps Angular has made some changes since 2 came out, but if you have any pointers –I’d appreciate it.

  • Alberto Perez Vazquez

    Hi,

    I´m sorry for the comment on the wrong post, if you want I can delete it.

    Thanks for this tutorial, I want to make a transition between detailed pages on page-router-outlet on nativescript angular 2 app:

    { path: “noticia/:current”, component: SingleNoticiaComponent}

    I made the next action with a swipe event and I pass the current note, the information is changing according to the current value but the component is not reloading is only changing the binding information. I have read that this is a feature for page-router-outlet, but this feature avoid me to make a transition between each note.

    Can you help me?

    I add a gif of my problem here: https://drive.google.com/file/d/0B2oa3frtO834MWp2ZUV0TEc4Z28/view

    As you can see the nice transition happens only on the first note.

    Thanks !

  • Ibrahim Eim

    Hi Nick I always appreciate your tutorials.
    I have one component (MenuComponent) which am using to display all my menus. It takes an id to know which menu to display {path: ‘menu/menuId’, ‘component’: ‘MenuComponent’}.
    I works well when am navigating from pages that use different components but the navigation can’t work if am on one menu and I want to replace it with another menu.

    So is there a way to navigate pages that use the same component.

    I.e from (menu/main_menu to menu/sub_menu)

    • Maybe I don’t understand your scenario, but why do you have a menu at all as a route?

      • Ibrahim Eim

        am loading menus dynamically from the database. so the menuId I pass returns a stringified json object from the db. It’s this object that menuComponent uses to create a menu. My implementation is like this because when I want to add items to the menu, I do it from the remote server so when the app user launches the app, it will make an http request to the remote server and compare database versions. if it’s version is old then the app will update it’s database. I use the same concept even to load my forms.
        So a tap to some menu items will route to a form while a tap to other menu items will route to a submenu which has to reuse the menuComponent.

        { path: “menu/:menuId”, component: MenuComponent },

        export class MenuComponent{
        menuDefination:any;
        menuId:any;
        formError:string;
        menuImagesArray:Array = [];
        scopePinDialog:any;
        formDefination:any;

        I use this function when a menu item is tapped

        itemTapped(menuItem) {
        if (menuItem.flashMessage) {
        //Just show message and thats it
        notice.showMessage(this, menuItem.flashMessageTitle, menuItem.flashMessageText, null)

        //This is not working well (to re-route to menu and resuse menu component.
        else if (menuItem.nextUiItemType == “MENU”) {
        //Load menu
        this.router.navigate([“menu”, menuItem.nextUiItem]);
        }
        //This navigates to a form and uses FormComponent. it works well
        else if (menuItem.nextUiItemType == “FORM”) {
        //Load form
        this.router.navigate([“form”, menuItem.nextUiItem]);
        }
        else if (menuItem.nextUiItemType == “ACTIVITY”) {
        console.log(‘activity’)
        // load activity com.jmsoft.micropaymobile.PaymentRequestsActivity
        if(menuItem.nextUiItem == “com.jmsoft.micropaymobile.PaymentRequestsActivity”) {
        this.loader = notice.showLoader(“Wait..”);
        this.router.navigate([‘paymentrequests’]);
        }
        //AndroidUIHelper.loadActivity(this, mainMenuItem.getAndroidUIMenuItem().getNextUiItem());
        } else {
        console.log(‘none’)
        }
        }
        —-other functions

        } //end of mainComponent

        • Instead of trying to navigate to the same component, why not just change the data to the component you’re already in. Example, the menu is a list populated by array. Click an item and replace the array with a new array?

          I probably don’t understand what you’re after still. Please don’t paste large chunks of code because I’m not going to look at it.

          Best,

          • Ibrahim Eim

            Sorry about the large chunk. I had already done that
            reload(id){ this.menuId=id; this.init(); }// this reloads the menu with new content
            The problem is when a user presses the back button can’t navigate to the old menu.

          • I’m no expert on UX, but should you really have X levels of menu nesting in your back stack? You may just want to implement a side drawer if you don’t want to navigate to the root menu.

  • texasman03

    Nick,

    Always great stuff sir. Quick question:

    How would you handle a tabbed application where each tab has nested routes, and you want to animate those transitions?

    For example in Ionic, each tab has its own nav stack, so if I’m on the ‘chats’ tab, I can click a chat, go to that individual chat detail page, animate from left to right and so on. I can’t seem to find any Nativescript Angular tutorials or examples with this type of functionality.

    Thoughts?

    • If you’re going from a chat tab to a chat detail page, it doesn’t sound like you’d be using nested routes. Seems like the details page should be a parent route.

      It makes sense that each tab is a nested route of a parent tab controller page, but probably not any deeper.

      Does that make sense?

  • Brandon S. Brandon

    Nic,
    When I run: “D:myproject>tns run android –emulator”

    I get this error:

    app/app.module.ts(3,10): error TS2305: Module ‘”D:/myproject/app/app.routing”‘ has no exported member ‘AppRoutingModule’.

    TypeScript compiler failed with exit code 1

    The word “AppRoutingModule” appears a lot in the Angular guide on Routing on their website (https://angular.io/docs/ts/… 43 times and zero times in this tutorial, so I assume that perhaps Angular has made some changes since 2 came out, but if you have any pointers –I’d appreciate it.

    • There are minor differences, neither are correct or incorrect. The point here is that you want to define your routes, somewhere, doesn’t matter where, and import them into the NgModule block. This can either be directly or through a child NgModule block.

      Both ways will work 🙂

  • BlauerPulli

    the two commands: “mkdir -p components/page1” and “mkdir -p components/page2” at the begin doesn’t
    create the components folder in the app folder. in my case it was in the project folder. have a look that your components- is in the app-folder.
    add in the app.routing file : export class AppRoutingModule { }

    with that it works for me. comment: I work with an macbook.

  • Vishwa Deepak

    Hi Nic,

    Nice Tutorial as always!!!.
    I am currently in a situation where I need to call a method in the Parent class, when child class navigates back.
    Can you pls suggest a way to achieve the same.

    • So it depends, and I’m not totally sure what you’re after:

      Track backwards navigation with the Location service
      Use a modal
      Use a shared service

      If you look at a few of my other tutorials, you’ll notice that I do things via the ngOnInit while subscribing to the Location service. That is an option if you don’t need to pass data from child to parent. If you need to pass data from child to parent, you might consider a modal:

      https://www.thepolyglotdeveloper.com/2017/01/using-modal-dialogs-nativescript-angular-mobile-application/

      The last solution is to cache your data in some shared service and use a combination of Location and the service.

      Best,

      • Vishwa Deepak

        Sorry for the unclear description …
        I would rephrase,
        1. I am navigating to Class B from Class A using navigateTo(),
        2. On doing some action (say click on listview), I need to dismiss class B view I am doing using this.routerExtensions.backToPreviousPage();.
        3. Now I need to call method a method in class A depending upon the item clicked in class B

        Sorry for my English 🙂