简体   繁体   中英

Angular 1 and 2 hybrid app - “require is not defined”

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?

you need instantiate the UpgradeAdapter with a non null parametter. In this way you need pass a forwardRef instance, something like this:

const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));

finally you need import the forwardRef

import {NgModule, forwardRef} from '@angular/core';

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM