Back to Blog Home
← all posts

We really love Angular...10x as much this time

July 16, 2020 — by Nathan Walker

The NativeScript team is dedicated to Angular and truly love building apps with it. We are excited to bring Angular 10 support and already looking forward to Angular 11.

In this update we wanted to go the extra mile and also apply some major cleanups to @nativescript/core to bring proper TypeScript 3.9 support to align with Angular 10. In fact, we're introducing some rc versions of the new upcoming NativeScript 7 in some of this support so those looking to upgrade can get ahead with several updates here.

We'll also show a nice new feature that's never been supported with NativeScript Angular previously. Finally true Async APP_INITIALIZER support! 🎉

TL;DR Version!

If you want to try this quickly, this section is for you. If you want more details, continue further down.

Install the {N} cli (at least 7.0.0 and newer):

npm i -g nativescript

This will install the next upcoming cli version which supports scoped packages (the future of all {N} npm packages). This will still work with older projects so you can just start using this. It also introduces a new cli alias we call ns (can be interpreted as NativeScript Compiler or just simply NativeScript). tns still works and will always work. We will use ns here to introduce it as part of the 2020 roadmap to slowly begin moving away from tns things.

ns create sampleapp --ng

You now have a NativeScript for Angular 10 app to play with.

Detailed update instructions

Let's breakdown what's needed to try this out right now in your own projects. We plan to keep this in rc for another week to vet any last remaining issues. You can help make a better final release by reporting any issues you encounter here - just prefix the issue with [Angular 10] so we see it quickly.

  1. npm i -g nativescript

This will install the latest cli version.

  1. Modify your package.json to contain the following in part:
"scripts": {
  "ngcc": "ngcc --properties es2015 module main --first-only",
  "postinstall": "npm run ngcc"
},
"dependencies": {
  "@angular/animations": "~10.0.0",
  "@angular/common": "~10.0.0",
  "@angular/compiler": "~10.0.0",
  "@angular/core": "~10.0.0",
  "@angular/forms": "~10.0.0",
  "@angular/platform-browser": "~10.0.0",
  "@angular/platform-browser-dynamic": "~10.0.0",
  "@angular/router": "~10.0.0",
  "@nativescript/angular": "~10.0.0",
  "@nativescript/core": "~7.0.0",
  "@nativescript/theme": "~2.3.0",
  "reflect-metadata": "~0.1.12",
  "rxjs": "^6.6.0",
  "zone.js": "~0.10.3"
},
"devDependencies": {
  "@angular/compiler-cli": "~10.0.0",
  "@nativescript/types": "~7.0.0",
  "@nativescript/webpack": "~3.0.0",
  "@ngtools/webpack": "~10.0.0",
  "typescript": "~3.9.0"
}

Make sure you completely remove nativescript-angular and tns-core-modules from dependencies and/or devDependencies.

The ngcc and postinstall is still a good idea to keep and will explain that more in a moment.

If you're used to using tns-platform-declarations, you can now safely use @nativescript/types which is the same thing but just named with a bit more clarity. This allows you to update your references.d.ts as follows:

/// <reference path="./node_modules/@nativescript/types/ios.d.ts" />
/// <reference path="./node_modules/@nativescript/types/android.d.ts" />

If you're used to using nativescript-dev-webpack, you can now safely use @nativescript/webpack which is the same thing but now contains all the latest updates from webpack and transient dependencies which also includes up-to-date webpack.config.js files. To replace your own project's webpack.config.js, you can simply delete it, then clean your project using a similar npm script shown above to clean and when you rerun the project the @nativescript/webpack will simply drop a brand new updated one in your project. If you had custom webpack changes you can use your git changeset to keep your custom bits.

If you have trouble with your webpack config you can copy/paste this one here: https://github.com/NativeScript/NativeScript/blob/204b5a427b11a411ffd70468155e149bda168755/packages/webpack/templates/webpack.angular.js

  1. Update your tsconfig.json with similar settings (dependending on your project it doesn't have to mirror this exactly but you can use as a good gauge). In general the module and target are updated to newer targets.
{
  "compilerOptions": {
    "module": "ESNext",
    "target": "es2015",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmitHelpers": true,
    "noEmitOnError": true,
    "skipLibCheck": true,
    "lib": [
      "es2018", "es2017", "dom", "es6"
    ],
    "baseUrl": ".",
    "paths": {
        "~/*": [
            "app/*"
        ]
    }
  },
  "include": [
    "references.d.ts",
    "src/tests/**/*.ts"
  ],
  "files": [
    "./src/main.ts"
  ],
  "exclude": [
    "node_modules",
    "platforms",
    "e2e"
  ]
}
  1. Update all imports to simply @nativescript/angular if they were nativescript-angular/{anything} previously. This includes the Angular 9 import { platformNativeScriptDynamic } from '@nativescript/angular/platform'; in your main.ts which can be flattened to be 100% consistent across the board. So just import { platformNativeScriptDynamic } from '@nativescript/angular';.

  2. A note about that ngcc.config.js and the postinstall, etc.

A lot of plugins out there have not been built with ng-packagr and/or been built with Angular 9 or 10...yet. This is not a bad thing and is perfectly okay right now. Therefore this file can help bridge the gap to allow those plugins to work right now with your Angular 10 projects. The rule of thumb generally is if you encounter a build complaint about a plugin you may need to add an entry to this file. You can follow a similar pattern with adding entries for different dependencies based on the sample ngcc.config.js file which is provided as a convenience with nsc create sampleapp --ng sample app.

  1. Now give it a run: ns run ios --emulator or ns run android --emulator

And report any issues here, thank you! ❤️

Troubleshooting

Problem 1

file: node_modules/@angular/core/fesm2015/core.js:3380:0: JS ERROR ReferenceError: Can't find variable: setTimeout
(CoreFoundation) *** Terminating app due to uncaught exception 'NativeScript encountered a fatal error: ReferenceError: Can't find variable: setTimeout

Solution:

Make sure import { platformNativeScriptDynamic } from "@nativescript/angular"; is the first import in main.ts. For example:

  • BEFORE:
import { enableProdMode } from "@angular/core";
import { platformNativeScriptDynamic } from "@nativescript/angular";
  • AFTER:
import { platformNativeScriptDynamic } from "@nativescript/angular";
import { enableProdMode } from "@angular/core";

Problem 2

CONSOLE ERROR file:///app/bundle.js:3489:19: No value accessor for form control with name: 'someControl',ZoneAwareError(file: node_modules/@nativescript/zone-js/zone-nativescript.js:1298:0)
at _throwError(file: node_modules/@angular/forms/fesm2015/forms.js:2440:0)

Or something similar.

Solution:

This can occur if you have a component that extends an abstract class which is missing a decoration. To resolve for example:

  • BEFORE:
export abstract class SomeParentClass { ...
  • AFTER:
@Directive()
export abstract class SomeParentClass { ...

Proper Async APP_INITIALIZER support

Sometimes you need to fetch data before the app boots up or do async operations before the app is ready for user interaction. Or maybe you just want a neat launch sequence to animate before your app springs to life. With Angular 10 and NativeScript you can now finally use an Async APP_INITIALIZER just as you can with Angular web apps.

Here's an example:

export function asyncBoot(): Function {
  return (): Promise<any> => new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, 5000);
  })
}

@NgModule({
    imports: [NativeScriptModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [
      {
        provide: APP_INITIALIZER,
        useFactory: asyncBoot,
        multi: true
      },
    ]
})
export class AppModule {}

You could also inject HttpClient into that factory function to perform remote requests.

We'll be discussing this in greater detail in a followup blog post to explore a new NativeScript Angular bootstrap option providing the ability to do engaging custom launch animations.