Angular 1 and 2 hybrid app – "require is not defined"

Issue

I’m trying to upgrade a giant client-side app from Angular 1.x to Angular 2, and I’ve hit a really annoying roadblock. I’ve recreated the issue with a dummy, light-weight project (files pasted below), but let me just explain the problem first.

Basically, when my tsconfig.json specifies module as commonjs, I get the following error in my chrome dev console:

app.module.ts:1Uncaught ReferenceError: require is not defined

When I switch the module to system, I get the following error:

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

And when I switch module to umd, everything works fine. But given that angular themselves suggest using commonjs, I’m concerned that umd is not the right answer. However, if I’m wrong about that and umd is perfectly fine, I’d love to hear a good explanation as to why.

Here’s my code to reproduce my issue:

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
 }
 ,
"exclude": [
    "node_modules"
 ]
}

typings.json:

{
  "globalDependencies": {
  "angular": "registry:dt/angular#1.5.0+20160922195358",
  "core-js": "registry:dt/core-js#0.0.0+20160725163759",
  "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
  "jquery": "registry:dt/jquery#1.10.0+20160929162922",
  "node": "registry:dt/node#6.0.0+20160909174046"
  }
}

systemjs.config.js :

(function (global) {
   System.config({
    paths: {
        // paths serve as alias
        'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
        // our app is within the app folder
        app: 'app',
        // angular bundles
        '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
        '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
        '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
        '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
        '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
         '@angular/ ': 'npm:@angular/http/bundles/http.umd.js',
        '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
        '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
        // other libraries
        'rxjs':                      'npm:rxjs',
        'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
        app: {
            main: './main.js',
            defaultExtension: 'js'
        },
        rxjs: {
            defaultExtension: 'js'
        },
        'angular-in-memory-web-api': {
            main: './index.js',
            defaultExtension: 'js'
        }
    }
 });
})(this);

package.json :

{
 "name": "mattsApp",
 "version": "0.0.1",
 "dependencies": {
 "angular": "^1.5.8",
 "@angular/common": "~2.0.2",
 "@angular/compiler": "~2.0.2",
 "@angular/core": "~2.0.2",
 "@angular/forms": "~2.0.2",
 "@angular/http": "~2.0.2",
 "@angular/platform-browser": "~2.0.2",
 "@angular/platform-browser-dynamic": "~2.0.2",
 "@angular/router": "~3.0.2",
 "@angular/upgrade": "~2.0.2",
 "angular-in-memory-web-api": "~0.1.5",
 "bootstrap": "^3.3.7",
 "core-js": "^2.4.1",
 "reflect-metadata": "^0.1.8",
 "rxjs": "5.0.0-beta.12",
 "systemjs": "0.19.39",
 "zone.js": "^0.6.25"
},
"devDependencies": {
  "concurrently": "^3.0.0",
  "lite-server": "^2.2.2",
  "typescript": "^2.0.3",
  "typings":"^1.4.0"
 },
 "scripts": {
  "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
  "lite": "lite-server",
  "postinstall": "typings install",
  "tsc": "tsc",
  "tsc:w": "tsc -w",
  "typings": "typings"
 }
}

app.js :

angular.module('app', []);

app.module.ts :

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { upgradeAdapter } from './upgrade_adapter';

@NgModule({
  imports:      [ BrowserModule ]
})
export class AppModule { }

upgradeAdapter.bootstrap(document.body, ['app'], {strictDi: true});

appCtrl.ts :

angular.module('app')
    .controller('appCtrl', ['$scope', function($scope) {
      $scope.hello = "howdy worldy";
    }]);

upgrade_adapter.ts :

import { UpgradeAdapter } from '@angular/upgrade';
import {AppModule} from "./app.module";
export const upgradeAdapter = new UpgradeAdapter(AppModule);

What am I missing?

Solution

First, thanks Andres. I need to read up a bit about the forwardRef to understand what that does. But it turns out that the answer in my particular case was something different.

I didn’t post my index.html here (simple oversight), but the problem was that I wasn’t loading my modules with the System.import(‘app’). Embarrassing error, I have to admit. So the answer is to add that line.

I should note that this led to a different error which I solved, but I’ll point it out here in case others have a similar issue. Since this is a hybrid angular 1 and 2 app, I have typescript files that are sometimes used by angular 1 controllers / directives / etc, and also by angular 2 components. I had changed those typescript files to use export so I could import them into my angular 2 components. This led me to also change my /// in the angular 1 files to use import. Unfortunately, this gave me an “undefinedModule” error. The solution (in my case) is not to use export on typescript files unless they are ONLY used with the angular 2 components. Meaning, in some angular 2 components, I’m actually using the /// and not the import.

Just thought other people might find that useful.

Answered By – MattyZ

Answer Checked By – Cary Denson (AngularFixing Admin)

Leave a Reply

Your email address will not be published.