The Case of the Missing Frame and Page in NativeScript with Angular

This just happened to me and I was seriously confused. I'm used to getting a hold of the frame or injecting the into an Angular component…

The Case of the Missing Frame and Page in NativeScript with Angular poster

Take control of your career. Build JavaScript mobile apps.

ng atlanta

Catch Dave Coffin, Nathan Walker, and Alex Ziskind at ngAtlanta in February 2020 for an advanced NativeScript with Angular workshop called  Breathe life into mobile UX with solid architecture lessons. You can register now and take your NativeScript skills up a notch.  Register here.

This just happened to me and I was seriously confused. I'm used to getting a hold of the topmost frame or injecting the Page into an Angular component constructor, but this time, they were missing!



While working on an Angular demo for the new NativeScript OAuth2 plugin that I published last week, I discovered that I depended on the topmost frame being available in an app, just they it always was before. Before version 4 that is!

TLDR

You can just watch the video tip that describes why we use the topmost frame and the Page objects, shows the problem we run into when not using the <page-router-outlet>, and then shows how to use the new createFrameOnBootstrap property when bootstrapping an NativeScript Angular app. You can also find this documented in the Core Concepts section of the NativeScript docs, a section that I have read before, but completely forgot about.



Here is the video posted to my YouTube channel.



The Problem

By default, the NativeScript Angular Hello world template uses the <page-router-outlet> when you create a new app from the base template. Using this router outlet give you native navigation capabilities and automatically creates a Frame that wraps the content of the Page and components inside. In Angular you typically don't interact with the Frame or the Page, and you won't even worry or care about them. Your primary UI unit is the Component. So it's not really an issue that's noticed, unless you want to do something special, like use native navigation or programmatically build UI widgets. I go more into it in the video.



Sometimes you'll use the Angular router and you won't use native navigation of the device. So you'll only use the <router-outlet> to stub out where your components will be swapped, but you still might want to inject the Page into your component to build UI programmatically like this:



import { Component } from '@angular/core';
import { Page } from 'tns-core-modules/ui/page';

@Component({
  selector: 'ns-app',
  moduleId: module.id,
  templateUrl: './app.component.html'
})
export class AppComponent {
  constructor(private page: Page) {
    // create UI widgets and add them to page,
    // or do some other neat stuff with the Page instance
  }
}



Well here is where we will have a problem if you decided NOT to use <page-router-outlet>. this.page in your component will be null. Frame and Page go hand in hand here: no Frame => no Page. So if you try to use the topmost() frame in this situation, it will be undefined.



import { Component } from '@angular/core';
import { topmost } from 'tns-core-modules/ui/frame';

@Component({
  selector: 'ns-app',
  moduleId: module.id,
  templateUrl: './app.component.html'
})
export class AppComponent {
  constructor() {
    // try to use topmost to navigate, for example.
    // You most likely wouldn't do this, but you could
    // use the frame if you were building some native code,
    // as I do in the NativeScript-OAuth2 plugin
    topmost().navigate('...');
  }
}



Insert useless comment here: "Personally I think this causes more confusion than it is worth, but I'm sure it was done for a reason". - Alex



The Solution

If you are going to use <page-router-outlet>, then you are OK - the Frame and Page will be created for you by the framework and the pagework (hehe, see what I did there?).

BUT, if you are going to remove <page-router-outlet> and things start breaking because topmost() returns undefined or your Page instance is null, then you can set the createFrameOnBootstrap boolean property to true when your app boots up.



// main.ts

import { platformNativeScriptDynamic } from 'nativescript-angular/platform';
import { AppModule } from './app/app.module';

platformNativeScriptDynamic({ createFrameOnBootstrap: true }).bootstrapModule(
  AppModule
);



So next time you are sitting and scratching your head, wondering why your frame or page are missing, then I hope this post find you well.



For more video tutorials about NativeScript, look at our courses on NativeScripting.com. The new Hands-on UI course is a pretty popular starting point for those starting out learning NativeScript and how to work with UI.



Let me know if you enjoyed this short tutorial on Twitter: @digitalix or comment here. And send me your NativeScript related questions that I can answer in video form. If I select your question to make a video answer, I'll send you swag.



Also, the first 3 people to tweet me the location of something NativeScript-related in the poster image of this post will get some stickers.


Alex lives in Washington, DC. He's a speaker, trainer, and a Telerik Developer Expert. He's been invloved in NativeScript projects since 2015 and has created courses for Pluralsight and LinkedIn.

Did you enjoy this? Share it!

Take control of your career. Build JavaScript mobile apps.