Search code examples
javascriptangularjsangularsystemjs

SystemJS error when importing modules into AngularJS - Anonymous System.register calls can only be made by modules loaded by SystemJS.import


I'm upgrading an AngularJS app to Angular, using the standard ngUpgrade module. I've converted a service to Angular, and I need to downgrade it so I can use it in an existing AngularJS module. I'm trying to import 'downgradeInjectable' and the Angular service like this:

import { downgradeInjectable } from '@angular/upgrade/static';
import { MyService } from './app/my-service';

(function () {

    var myCtrl = ['myService', function (myService) {
        this.myService = myService;
        ...
    }];

    ... stateConfig here    

    angular
        .module('myModule')
        .config(['$stateProvider', '$urlRouterProvider', stateConfig])
        .controller('myController', myCtrl)
        .factory('myService', downgradeInjectable(MyService))

})();

The imports are recognised as valid by the TypeScript compiler, and it all compiles fine, however I now get this error in the console:

Invalid System.register call. Anonymous System.register calls can only be made by modules loaded by SystemJS.import and not via script tags.

In the transpiled code, the import is handled like this by SystemJS:

System.register(["@angular/upgrade/static", "./app/my-service"], function (exports_1, context_1) {
    "use strict";
     ...

My other AngularJS code isn't using any imports, this is the first time I've introduced an 'import' in the existing AngularJS application. What do I need to do to import these modules successfully? I've reviewed similar errors however there's nothing that helps me or that matches this particular situation.


Update:

I'm loading the application by importing a main module using SystemJS in my main html file like this:

<script>
    System.import('/assets/js/app/main.js').catch(function (err) { console.error(err); });
</script>

The original TypeScript for the main.js file bootstraps the root Angular 4 module:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

... and the root module is like this:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';

import { AppComponent } from './app.component';
... other imports

@NgModule({
    imports: [BrowserModule, UpgradeModule ],
    declarations: [AppComponent],
    entryComponents: [AppComponent],
    providers: [ WindowRef, MyService]
})
export class AppModule {
    constructor(private upgrade: UpgradeModule) {
    }

    ngDoBootstrap() {
        var ngApp = <HTMLInputElement>document.getElementById('ngApp');
        this.upgrade.bootstrap(document.documentElement, [ngApp.value]);
    }
}

... which then bootstraps the AngularJS app, within the context of Angular.


My tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [ "es2015", "dom" ],
    "noImplicitAny": false,
    "suppressImplicitAnyIndexErrors": true,
    "noStrictGenericChecks": true
  },
  "include": [ "**/*.ts" ],
  "exclude": [
    "node_modules"
  ]
}

Further update:

Following the advice from @Aluan Haddad I'm now attempting to no import the root AngularJS declarations like this:

main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

**import root AngularJS declarations**
import '../app.js';

platformBrowserDynamic().bootstrapModule(AppModule);

The original app.ts for the AngularJS declarations in app.js looks like this:

declare var angular: any;

(function () {

    angular
        .module('shared', ['shared.primaryNavigation', ...otherDependencies]);

    angular
        .module('shared.ui.router', ['ui.router']);

    angular
        .module('ngApp1', ['dependency']);

     angular
        .module('ngApp2', ['dependency']);

There's nothing in here that's exporting anything though, so I wouldn't expect the attempted import to work, and honestly I now have no idea what's going on with the module loading as I only know about importing named classes and the AngularJS app doesn't use any exports! I now get these errors in the console:

[$injector:nomod] Module 'shared' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.

So how should I be structuring this to successfully load the app? My script inclusions look like this:

<!-- Angular JS -->
<!-- Root Module Declarations -->
<!-- now commenting this out -->
<!--<script src="/assets/js/app.js"></script>-->

<!-- Shared Config -->
<script src="/assets/js/shared/shared.config.js"></script>

<!-- Shared Directives -->
<script src="/assets/js/shared/directives/myDirective1.js"></script>
<script src="/assets/js/shared/directives/myDirective2.js"></script>

<!-- Shared Components -->
<script src="/assets/js/shared/modules/myModule1.js"></script>
<script src="/assets/js/shared/modules/myModule2.js"></script>


<!-- Shared Services -->
<script src="/assets/js/shared/services/myService1.js"></script>
<script src="/assets/js/shared/services/myService2.js"></script>

Solution

  • You are receiving the error because, a top level import or export makes a file into a module.

    That causes TypeScript to transpile it as such.

    An anonymous System.register module must be loaded by a loader that understands the format, typically SystemJS.

    Now that it is a module, import it like any other

    import './my-module';
    

    and remove script tags that load it.

    This can be improved

    import { downgradeInjectable } from '@angular/upgrade/static';
    import { MyService } from './app/my-service';
    
    class MyCtrl {
        static $inject = ['myService'];
        constructor(readonly myService) {...}
    }
    
    ... stateConfig here    
    
    angular
        .module('myModule')
        .config(['$stateProvider', '$urlRouterProvider', stateConfig])
        .controller('myController', MyCtrl)
        .factory('myService', downgradeInjectable(MyService));
    

    To explain the error in more detail, SystemJS also defines a named System.register module format. This is the output format used (by default) in the SystemJS Builder to emit bundles.